├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── codecov.yml ├── dependabot.yml ├── renovate.json └── workflows │ ├── build.yml │ ├── moderne-ingest.yml │ ├── qodana-license-audit.yml │ ├── release.yml │ ├── run-ui-tests.yml │ └── update-gradle-wrapper.yml ├── .gitignore ├── .idea ├── $CACHE_FILE$ ├── .gitignore ├── .name ├── JavaParser Code Inspector.iml ├── JavaParser-AST-Inspector.iml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── git_toolbox_prj.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations │ ├── JavaCodeBrowser__clean_build___warning_mode_all_.xml │ └── JavaCodeBrowser__runIde_.xml └── vcs.xml ├── .run ├── Run IDE for UI Tests.run.xml ├── Run IDE with Plugin.run.xml ├── Run Plugin Tests.run.xml ├── Run Plugin Verification.run.xml └── Run Qodana.run.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── detekt-config.yml ├── dev └── README-template.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── qodana.yml ├── settings.gradle.kts └── src ├── main ├── java │ └── com │ │ └── github │ │ └── rogerhowell │ │ └── javaparser_ast_inspector │ │ └── plugin │ │ ├── logging │ │ └── NotificationLogger.java │ │ ├── printers │ │ ├── ASCIITreePrinter.java │ │ ├── CustomDotPrinter.java │ │ ├── CustomJsonPrinter.java │ │ ├── CypherPrinter.java │ │ ├── GraphMLPrinter.java │ │ └── NodePrinter.java │ │ ├── services │ │ ├── HighlightingService.java │ │ ├── JavaParserService.java │ │ ├── PrinterService.java │ │ └── impl │ │ │ ├── HighlightingServiceImpl.java │ │ │ ├── JavaParserServiceImpl.java │ │ │ └── PrinterServiceImpl.java │ │ ├── ui │ │ ├── notifications │ │ │ └── NotificationsNotifier.java │ │ ├── swing_components │ │ │ ├── NodeDetailsTextPane.java │ │ │ ├── config_panel │ │ │ │ ├── CharacterEncodingComboBox.java │ │ │ │ ├── ConfigPanel.java │ │ │ │ ├── CustomComboBox.java │ │ │ │ ├── ExportAsComboBox.java │ │ │ │ └── LanguageLevelComboBox.java │ │ │ ├── forms │ │ │ │ ├── AstInspectorToolWindow.form │ │ │ │ ├── AstInspectorToolWindow.java │ │ │ │ └── Form.java │ │ │ └── output_results_tabs │ │ │ │ ├── ParseResultsTabPane.java │ │ │ │ └── ParseResultsTabPanesContainer.java │ │ └── tool_window │ │ │ └── AstBrowserToolWindowFactory.java │ │ └── util │ │ ├── Constants.java │ │ ├── EditorUtil.java │ │ ├── FontUtil.java │ │ ├── LanguageLevelUtil.java │ │ ├── PsiUtil.java │ │ └── StringUtil.java └── resources │ ├── JavaCodeBrowser │ ├── graph icon -- 13x13.png │ ├── graph icon -- error -- 13x13.png │ └── graph icon.xcf │ ├── META-INF │ └── plugin.xml │ └── logos │ ├── jp-logo.png │ └── jp-logo_13x13.png └── test └── java └── com └── github └── rogerhowell └── javaparser_ast_inspector └── plugin └── util ├── LanguageLevelUtilTest.java └── StringUtilTest.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | 12 | 13 | **Describe the bug:** 14 | 15 | 16 | **Steps to reproduce:** 17 | 18 | 19 | **Expected behavior:** 20 | 21 | 22 | **Additional context:** 23 | 24 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: true 3 | 4 | 5 | coverage: 6 | precision: 3 7 | round: down 8 | 9 | 10 | # Comments on PRs 11 | comment: 12 | layout: "reach,diff,flags,files,footer" 13 | behavior: default 14 | require_changes: false 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | # Maintain dependencies for Gradle dependencies 7 | - package-ecosystem: "gradle" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | # Maintain dependencies for GitHub Actions 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "packageRules": [ 6 | { 7 | "updateTypes": ["minor", "patch", "pin", "digest"], 8 | "automerge": true 9 | } 10 | ], 11 | "automergeType": "pr", 12 | "prHourlyLimit": 0, 13 | "semanticCommits": "enabled", 14 | "dependencyDashboard": true, 15 | "configWarningReuseIssue": false, 16 | "labels": ["dependencies"] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow created for testing and preparing the plugin release in following steps: 2 | # - validate Gradle Wrapper, 3 | # - run 'test' and 'verifyPlugin' tasks, 4 | # - ~~run Qodana inspections,~~ (now within in separate workflow) 5 | # - run 'buildPlugin' task and prepare artifact for the further tests, 6 | # - run 'runPluginVerifier' task, 7 | # - create a draft release. 8 | # 9 | # Workflow is triggered on push and pull_request events. 10 | # 11 | # Docs: 12 | # - GitHub Actions reference: https://help.github.com/en/actions 13 | # - IntelliJ Plugin Verifier GitHub Action: https://github.com/ChrisCarini/intellij-platform-plugin-verifier-action 14 | # 15 | ## JBIJPPTPL 16 | 17 | name: Build 18 | on: 19 | # Trigger the workflow on pushes to only the 'master' branch (this avoids duplicate checks being run e.g. for dependabot pull requests) 20 | push: 21 | branches: [master] 22 | # Trigger the workflow on any pull request 23 | pull_request: 24 | 25 | jobs: 26 | 27 | # Run Gradle Wrapper Validation Action to verify the wrapper's checksum 28 | # Run verifyPlugin, IntelliJ Plugin Verifier, and test Gradle tasks 29 | # Build plugin and provide the artifact for the next workflow jobs 30 | build: 31 | name: Build 32 | runs-on: ubuntu-latest 33 | outputs: 34 | version: ${{ steps.properties.outputs.version }} 35 | changelog: ${{ steps.properties.outputs.changelog }} 36 | steps: 37 | 38 | # Check out current repository 39 | - name: Fetch Sources 40 | uses: actions/checkout@v3.0.2 41 | 42 | # Validate wrapper 43 | - name: Gradle Wrapper Validation 44 | uses: gradle/wrapper-validation-action@v1.0.4 45 | 46 | # Setup Java 11 environment for the next steps 47 | - name: Setup Java 48 | uses: actions/setup-java@v3 49 | with: 50 | distribution: zulu 51 | java-version: 11 52 | cache: gradle 53 | 54 | # Set environment variables 55 | - name: Export Properties 56 | id: properties 57 | shell: bash 58 | run: | 59 | PROPERTIES="$(./gradlew properties --console=plain -q)" 60 | VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')" 61 | NAME="$(echo "$PROPERTIES" | grep "^pluginName:" | cut -f2- -d ' ')" 62 | CHANGELOG="$(./gradlew getChangelog --unreleased --no-header --console=plain -q)" 63 | CHANGELOG="${CHANGELOG//'%'/'%25'}" 64 | CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" 65 | CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" 66 | 67 | echo "::set-output name=version::$VERSION" 68 | echo "::set-output name=name::$NAME" 69 | echo "::set-output name=changelog::$CHANGELOG" 70 | echo "::set-output name=pluginVerifierHomeDir::~/.pluginVerifier" 71 | 72 | ./gradlew listProductsReleases # prepare list of IDEs for Plugin Verifier 73 | 74 | # Run tests 75 | - name: Run Tests 76 | run: ./gradlew test 77 | 78 | # Collect Tests Result of failed tests 79 | - name: Collect Tests Result 80 | if: ${{ failure() }} 81 | uses: actions/upload-artifact@v3 82 | with: 83 | name: tests-result 84 | path: ${{ github.workspace }}/build/reports/tests 85 | 86 | # # Cache Plugin Verifier IDEs 87 | # - name: Setup Plugin Verifier IDEs Cache 88 | # uses: actions/cache@v3.0.2 89 | # with: 90 | # path: ${{ steps.properties.outputs.pluginVerifierHomeDir }}/ides 91 | # key: plugin-verifier-${{ hashFiles('build/listProductsReleases.txt') }} 92 | 93 | # Run Verify Plugin task and IntelliJ Plugin Verifier tool 94 | - name: Run Plugin Verification tasks 95 | run: ./gradlew runPluginVerifier -Pplugin.verifier.home.dir=${{ steps.properties.outputs.pluginVerifierHomeDir }} 96 | 97 | # Collect Plugin Verifier Result 98 | - name: Collect Plugin Verifier Result 99 | if: ${{ always() }} 100 | uses: actions/upload-artifact@v3 101 | with: 102 | name: pluginVerifier-result 103 | path: ${{ github.workspace }}/build/reports/pluginVerifier 104 | 105 | # ## Run Qodana inspections 106 | # ## (originally within build.yml (per plugin template project), but recommended to be in a separate workflow so can be parallelised) 107 | # - name: Qodana - Code Inspection 108 | # uses: JetBrains/qodana-action@v4.2.3 109 | 110 | # Prepare plugin archive content for creating artifact 111 | - name: Prepare Plugin Artifact 112 | id: artifact 113 | shell: bash 114 | run: | 115 | cd ${{ github.workspace }}/build/distributions 116 | FILENAME=`ls *.zip` 117 | unzip "$FILENAME" -d content 118 | 119 | echo "::set-output name=filename::${FILENAME:0:-4}" 120 | 121 | # Store already-built plugin as an artifact for downloading 122 | - name: Upload artifact 123 | uses: actions/upload-artifact@v3.1.0 124 | with: 125 | name: ${{ steps.artifact.outputs.filename }} 126 | path: ./build/distributions/content/*/* 127 | 128 | # Prepare a draft release for GitHub Releases page for the manual verification 129 | # If accepted and published, release workflow would be triggered 130 | releaseDraft: 131 | name: Release Draft 132 | if: github.event_name != 'pull_request' 133 | needs: build 134 | runs-on: ubuntu-latest 135 | steps: 136 | 137 | # Check out current repository 138 | - name: Fetch Sources 139 | uses: actions/checkout@v3.0.2 140 | 141 | # Remove old release drafts by using the curl request for the available releases with draft flag 142 | - name: Remove Old Release Drafts 143 | env: 144 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 145 | run: | 146 | gh api repos/{owner}/{repo}/releases \ 147 | --jq '.[] | select(.draft == true) | .id' \ 148 | | xargs -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{} 149 | 150 | # Create new release draft - which is not publicly visible and requires manual acceptance 151 | - name: Create Release Draft 152 | env: 153 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 154 | run: | 155 | gh release create v${{ needs.build.outputs.version }} \ 156 | --draft \ 157 | --title "v${{ needs.build.outputs.version }}" \ 158 | --notes "$(cat << 'EOM' 159 | ${{ needs.build.outputs.changelog }} 160 | EOM 161 | )" 162 | -------------------------------------------------------------------------------- /.github/workflows/moderne-ingest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Moderne ingest 3 | 4 | on: 5 | workflow_dispatch: 6 | push: 7 | branches: [ master ] 8 | schedule: 9 | - cron: "0 19 * * *" 10 | 11 | jobs: 12 | ingest: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | - name: ingest 19 | uses: docker://moderne/ingest:java11-latest 20 | env: 21 | MODERNE_API_ACCESS_TOKEN: ${{ secrets.MODERNE_API_ACCESS_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/qodana-license-audit.yml: -------------------------------------------------------------------------------- 1 | name: Qodana - License Audit 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | qodana: 7 | runs-on: ubuntu-latest 8 | steps: 9 | # clone your project 10 | - uses: actions/checkout@v3 11 | # run qodana-license-audit 12 | - name: Qodana - License Audit 13 | uses: jetbrains/qodana-license-audit-action@main 14 | with: 15 | options: PYTHON_VERSION=3.7.10 16 | # upload the results to GitHub artifacts 17 | - uses: actions/upload-artifact@v3 18 | with: 19 | path: ${{ github.workspace }}/qodana -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow created for handling the release process based on the draft release prepared 2 | # with the Build workflow. Running the publishPlugin task requires the PUBLISH_TOKEN secret provided. 3 | 4 | name: Release 5 | on: 6 | release: 7 | types: [prereleased, released] 8 | 9 | jobs: 10 | 11 | # Prepare and publish the plugin to the Marketplace repository 12 | release: 13 | name: Publish Plugin 14 | runs-on: ubuntu-latest 15 | steps: 16 | 17 | # Check out current repository 18 | - name: Fetch Sources 19 | uses: actions/checkout@v3.0.2 20 | with: 21 | ref: ${{ github.event.release.tag_name }} 22 | 23 | # Setup Java 11 environment for the next steps 24 | - name: Setup Java 25 | uses: actions/setup-java@v3 26 | with: 27 | distribution: zulu 28 | java-version: 11 29 | cache: gradle 30 | 31 | # Set environment variables 32 | - name: Export Properties 33 | id: properties 34 | shell: bash 35 | run: | 36 | CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d' 37 | ${{ github.event.release.body }} 38 | EOM 39 | )" 40 | 41 | CHANGELOG="${CHANGELOG//'%'/'%25'}" 42 | CHANGELOG="${CHANGELOG//$'\n'/'%0A'}" 43 | CHANGELOG="${CHANGELOG//$'\r'/'%0D'}" 44 | 45 | echo "::set-output name=changelog::$CHANGELOG" 46 | 47 | # Update Unreleased section with the current release note 48 | - name: Patch Changelog 49 | if: ${{ steps.properties.outputs.changelog != '' }} 50 | env: 51 | CHANGELOG: ${{ steps.properties.outputs.changelog }} 52 | run: | 53 | ./gradlew patchChangelog --release-note="$CHANGELOG" 54 | 55 | # Publish the plugin to the Marketplace 56 | - name: Publish Plugin 57 | env: 58 | # Environment variable set, must match the env variable used within `build.gradle.kts` (`publishPlugin` section) 59 | INTELLIJ_PLUGIN_PUBLISH_TOKEN: ${{ secrets.INTELLIJ_PLUGIN_PUBLISH_TOKEN }} 60 | run: ./gradlew publishPlugin 61 | 62 | # Upload artifact as a release asset 63 | - name: Upload Release Asset 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/* 67 | 68 | # Create pull request 69 | - name: Create Pull Request 70 | if: ${{ steps.properties.outputs.changelog != '' }} 71 | env: 72 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 73 | run: | 74 | VERSION="${{ github.event.release.tag_name }}" 75 | BRANCH="changelog-update-$VERSION" 76 | 77 | git config user.email "action@github.com" 78 | git config user.name "GitHub Action" 79 | 80 | git checkout -b $BRANCH 81 | git commit -am "Changelog update - $VERSION" 82 | git push --set-upstream origin $BRANCH 83 | 84 | gh pr create \ 85 | --title "Changelog update - \`$VERSION\`" \ 86 | --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \ 87 | --base master \ 88 | --head $BRANCH 89 | -------------------------------------------------------------------------------- /.github/workflows/run-ui-tests.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps: 2 | # - prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with UI 3 | # - wait for IDE to start 4 | # - run UI tests with separate Gradle task 5 | # 6 | # Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform 7 | # 8 | # Workflow is triggered manually. 9 | 10 | name: Run UI Tests 11 | on: 12 | workflow_dispatch 13 | 14 | jobs: 15 | 16 | testUI: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - os: ubuntu-latest 23 | runIde: | 24 | export DISPLAY=:99.0 25 | Xvfb -ac :99 -screen 0 1920x1080x16 & 26 | gradle runIdeForUiTests & 27 | - os: windows-latest 28 | runIde: start gradlew.bat runIdeForUiTests 29 | - os: macos-latest 30 | runIde: ./gradlew runIdeForUiTests & 31 | 32 | steps: 33 | 34 | # Check out current repository 35 | - name: Fetch Sources 36 | uses: actions/checkout@v3.0.2 37 | 38 | # Setup Java 11 environment for the next steps 39 | - name: Setup Java 40 | uses: actions/setup-java@v3 41 | with: 42 | distribution: zulu 43 | java-version: 11 44 | cache: gradle 45 | 46 | # Run IDEA prepared for UI testing 47 | - name: Run IDE 48 | run: ${{ matrix.runIde }} 49 | 50 | # Wait for IDEA to be started 51 | - name: Health Check 52 | uses: jtalk/url-health-check-action@v2 53 | with: 54 | url: http://127.0.0.1:8082 55 | max-attempts: 15 56 | retry-delay: 30s 57 | 58 | # Run tests 59 | - name: Tests 60 | run: ./gradlew test 61 | -------------------------------------------------------------------------------- /.github/workflows/update-gradle-wrapper.yml: -------------------------------------------------------------------------------- 1 | name: Update Gradle Wrapper 2 | 3 | on: 4 | ## Midnight UTC every day 5 | schedule: 6 | - cron: "0 0 * * *" 7 | ## Option for manual trigger 8 | workflow_dispatch: 9 | 10 | jobs: 11 | update-gradle-wrapper: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Update Gradle Wrapper 18 | uses: gradle-update/update-gradle-wrapper-action@v1 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Windows template 3 | # Windows thumbnail cache files 4 | Thumbs.db 5 | Thumbs.db:encryptable 6 | ehthumbs.db 7 | ehthumbs_vista.db 8 | 9 | # Dump file 10 | *.stackdump 11 | 12 | # Folder config file 13 | [Dd]esktop.ini 14 | 15 | # Recycle Bin used on file shares 16 | $RECYCLE.BIN/ 17 | 18 | # Windows Installer files 19 | *.cab 20 | *.msi 21 | *.msix 22 | *.msm 23 | *.msp 24 | 25 | # Windows shortcuts 26 | *.lnk 27 | 28 | ### Linux template 29 | *~ 30 | 31 | # temporary files which can be created if a process still has a handle open of a deleted file 32 | .fuse_hidden* 33 | 34 | # KDE directory preferences 35 | .directory 36 | 37 | # Linux trash folder which might appear on any partition or disk 38 | .Trash-* 39 | 40 | # .nfs files are created when an open file is removed but is still being accessed 41 | .nfs* 42 | 43 | ### Gradle template 44 | .gradle 45 | /build/ 46 | 47 | # Ignore Gradle GUI config 48 | gradle-app.setting 49 | 50 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 51 | !gradle-wrapper.jar 52 | 53 | # Cache of project 54 | .gradletasknamecache 55 | 56 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 57 | # gradle/wrapper/gradle-wrapper.properties 58 | 59 | ### Java template 60 | # Compiled class file 61 | *.class 62 | 63 | # Log file 64 | *.log 65 | 66 | # BlueJ files 67 | *.ctxt 68 | 69 | # Mobile Tools for Java (J2ME) 70 | .mtj.tmp/ 71 | 72 | # Package Files # 73 | *.jar 74 | *.war 75 | *.nar 76 | *.ear 77 | *.zip 78 | *.tar.gz 79 | *.rar 80 | 81 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 82 | hs_err_pid* 83 | 84 | ### NetBeans template 85 | **/nbproject/private/ 86 | **/nbproject/Makefile-*.mk 87 | **/nbproject/Package-*.bash 88 | build/ 89 | nbbuild/ 90 | dist/ 91 | nbdist/ 92 | .nb-gradle/ 93 | 94 | ### NotepadPP template 95 | # Notepad++ backups # 96 | *.bak 97 | 98 | ### JetBrains template 99 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 100 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 101 | 102 | # User-specific stuff 103 | .idea/**/workspace.xml 104 | .idea/**/tasks.xml 105 | .idea/**/usage.statistics.xml 106 | .idea/**/dictionaries 107 | .idea/**/shelf 108 | 109 | # Generated files 110 | .idea/**/contentModel.xml 111 | 112 | # Sensitive or high-churn files 113 | .idea/**/dataSources/ 114 | .idea/**/dataSources.ids 115 | .idea/**/dataSources.local.xml 116 | .idea/**/sqlDataSources.xml 117 | .idea/**/dynamic.xml 118 | .idea/**/uiDesigner.xml 119 | .idea/**/dbnavigator.xml 120 | 121 | # Gradle 122 | .idea/**/gradle.xml 123 | .idea/**/libraries 124 | 125 | # Gradle and Maven with auto-import 126 | # When using Gradle or Maven with auto-import, you should exclude module files, 127 | # since they will be recreated, and may cause churn. Uncomment if using 128 | # auto-import. 129 | # .idea/artifacts 130 | # .idea/compiler.xml 131 | # .idea/modules.xml 132 | # .idea/*.iml 133 | # .idea/modules 134 | # *.iml 135 | # *.ipr 136 | 137 | # CMake 138 | cmake-build-*/ 139 | 140 | # Mongo Explorer plugin 141 | .idea/**/mongoSettings.xml 142 | 143 | # File-based project format 144 | *.iws 145 | 146 | # IntelliJ 147 | out/ 148 | 149 | # mpeltonen/sbt-idea plugin 150 | .idea_modules/ 151 | 152 | # JIRA plugin 153 | atlassian-ide-plugin.xml 154 | 155 | # Cursive Clojure plugin 156 | .idea/replstate.xml 157 | 158 | # Crashlytics plugin (for Android Studio and IntelliJ) 159 | com_crashlytics_export_strings.xml 160 | crashlytics.properties 161 | crashlytics-build.properties 162 | fabric.properties 163 | 164 | # Editor-based Rest Client 165 | .idea/httpRequests 166 | 167 | # Android studio 3.1+ serialized cache file 168 | .idea/caches/build_file_checksums.ser 169 | 170 | ### Maven template 171 | target/ 172 | pom.xml.tag 173 | pom.xml.releaseBackup 174 | pom.xml.versionsBackup 175 | pom.xml.next 176 | release.properties 177 | dependency-reduced-pom.xml 178 | buildNumber.properties 179 | .mvn/timing.properties 180 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 181 | .mvn/wrapper/maven-wrapper.jar 182 | 183 | ### macOS template 184 | # General 185 | .DS_Store 186 | .AppleDouble 187 | .LSOverride 188 | 189 | # Icon must end with two \r 190 | Icon 191 | 192 | # Thumbnails 193 | ._* 194 | 195 | # Files that might appear in the root of a volume 196 | .DocumentRevisions-V100 197 | .fseventsd 198 | .Spotlight-V100 199 | .TemporaryItems 200 | .Trashes 201 | .VolumeIcon.icns 202 | .com.apple.timemachine.donotpresent 203 | 204 | # Directories potentially created on remote AFP share 205 | .AppleDB 206 | .AppleDesktop 207 | Network Trash Folder 208 | Temporary Items 209 | .apdisk 210 | 211 | .qodana 212 | 213 | -------------------------------------------------------------------------------- /.idea/$CACHE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | JavaParser AST Inspector -------------------------------------------------------------------------------- /.idea/JavaParser Code Inspector.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/JavaParser-AST-Inspector.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 347 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations/JavaCodeBrowser__clean_build___warning_mode_all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 19 | true 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/runConfigurations/JavaCodeBrowser__runIde_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.run/Run IDE for UI Tests.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 15 | 17 | true 18 | true 19 | false 20 | 21 | 22 | -------------------------------------------------------------------------------- /.run/Run IDE with Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/Run Plugin Tests.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.run/Run Plugin Verification.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/Run Qodana.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 16 | 19 | 21 | true 22 | true 23 | false 24 | 25 | 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | ### Added 5 | 6 | ### Changed 7 | 8 | ### Deprecated 9 | 10 | ### Removed 11 | 12 | ### Fixed 13 | 14 | ### Security 15 | 16 | ## [0.5.4] 17 | ### Added 18 | 19 | ### Changed 20 | 21 | ### Deprecated 22 | 23 | ### Removed 24 | 25 | ### Fixed 26 | 27 | ### Security 28 | 29 | ## [0.5.4] 30 | ### Added 31 | - Added compatibility for 2022.1 (build 221.*) 32 | 33 | ### Changed 34 | - Multiple dependency updates 35 | 36 | ### Fixed 37 | - Upstream fix: [GitHub Actions - fixed duplicated `.zip` extension in artifact file's name of the build flow](https://github.com/JetBrains/intellij-platform-plugin-template/pull/224) 38 | 39 | ## [0.5.3] 40 | ### Added 41 | - Automatically set the language level to match the language level set within the IntelliJ IDEA project 42 | 43 | ### Changed 44 | - Bumped `pluginUntilBuild` to allow for 2021.3 (redo) 45 | 46 | ### Fixed 47 | - Fixed release / changelog update action 48 | 49 | ## [0.5.2] 50 | ### Changed 51 | - ~~Bumped `pluginUntilBuild` to allow for 2021.3~~ 52 | - Upgraded to using JavaParser v3.24.0 53 | - Update the project with recent template project changes (workflow tweaks, added qodana, keeping detekt (for now)) 54 | - Multiple other dependency updates 55 | 56 | ## [0.5.1] 57 | ### Changed 58 | - Builds now require JDK11 minimum, per IntelliJ Plugin requirement 59 | - Upgraded to using JavaParser v3.23.0 60 | - Multiple other dependency updates 61 | 62 | ## [0.5.0] 63 | ### Added 64 | - Detection of the IntelliJ's project language level, and default to that within the AST Inspector 65 | 66 | ### Changed 67 | - Upgraded to using JavaParser v3.22.1 68 | - Upgraded multiple dependencies 69 | - Made the plugin `dumbAware`, enabling it to be used while the project is being indexed 70 | - The exporters now use the system's newline, rather than `\n` 71 | 72 | ### Fixed 73 | - Exporters now respect the `include new line` configuration option 74 | 75 | ## [0.4.5] 76 | ### Changed 77 | - Upgraded to using JavaParser v3.20.2 78 | - Upgraded many other dependencies too 79 | - Upgraded compatibility with recent intellij builds 80 | 81 | ## [0.4.4] 82 | ### Changed 83 | - Upgraded to using JavaParser v3.16.2 84 | - Upgraded multiple dependencies 85 | - Upgraded compatibility with recent intellij builds 86 | - Switched to kotlin DSL for builds 87 | 88 | ## [0.4.3] 89 | ### Changed 90 | - Upgraded to using JavaParser v3.15.21 91 | 92 | ## [0.4.2] 93 | ### Added 94 | - Initial submitted release of the plugin to the Jetbrains plugin repo, using JavaParser v3.15.18 95 | - Parsing via the plugin UI is feature complete, with analysis/generation/symbol resolution to come in future versions. 96 | - Key highlights: 97 | - Display of the AST produced, which can be explored (clicking on an item in the AST will highlight the relevant section of source code) 98 | - Display of the parsed tokens 99 | - Exporting of alternative representations of the AST (including as YAML, DOT, XML, Cypher, and others) 100 | - Being able to view a log of parse attempts (including any errors, and the configuration used in the parse) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Roger Howell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # JavaParser AST Inspector 5 | 6 | 7 | ![Build](https://github.com/MysterAitch/JavaParser-AST-Inspector/workflows/Build/badge.svg) 8 | [![Version](https://img.shields.io/jetbrains/plugin/v/14245.svg)](https://plugins.jetbrains.com/plugin/14245) 9 | [![Downloads](https://img.shields.io/jetbrains/plugin/d/14245.svg)](https://plugins.jetbrains.com/plugin/14245) 10 | [![GitHub license](https://img.shields.io/github/license/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/blob/master/LICENSE) 11 | [![GitHub forks](https://img.shields.io/github/forks/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/network) 12 | [![GitHub stars](https://img.shields.io/github/stars/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/stargazers) 13 | 14 | 15 | 16 | 17 | Browse and navigate the Abstract Syntax Tree (AST) based on the Java code in your editor. 18 | 19 | Uses JavaParser v3.24.2 which currently handles Java 1-15, with Java 16 coming soon! 20 | 21 | 22 | 23 | ## Installation 24 | 25 | - Using IDE built-in plugin system: 26 | 27 | Settings/Preferences > 28 | Plugins > 29 | Marketplace > 30 | Search for "JavaParser-AST-Inspector" > 31 | Install Plugin 32 | 33 | - Manually: 34 | 35 | Download the [latest release](https://github.com/MysterAitch/JavaParser-AST-Inspector/releases/latest) and install it manually using 36 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... 37 | 38 | 39 | 40 | ## Hackathon Notes 41 | - Plan: Using JavaParser to parse a given project, and display a navigable syntax tree to navigate around the project. 42 | - Reality: Whatever comes out of the #covhack2020 hackathon :) 43 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.gitlab.arturbosch.detekt.Detekt 2 | import org.jetbrains.changelog.markdownToHTML 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 4 | 5 | fun properties(key: String) = project.findProperty(key).toString() 6 | 7 | plugins { 8 | id("idea") 9 | // Java support 10 | id("java") 11 | // Kotlin support 12 | id("org.jetbrains.kotlin.jvm") version "1.6.21" 13 | // Gradle IntelliJ Plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin 14 | id("org.jetbrains.intellij") version "1.5.3" 15 | // Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin 16 | id("org.jetbrains.changelog") version "1.3.1" 17 | // Gradle Qodana Plugin 18 | id("org.jetbrains.qodana") version "0.1.13" 19 | // detekt linter - read more: https://detekt.github.io/detekt/gradle.html 20 | id("io.gitlab.arturbosch.detekt") version "1.20.0" 21 | // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle 22 | id("org.jlleitschuh.gradle.ktlint") version "10.3.0" 23 | 24 | id("jacoco") 25 | } 26 | 27 | idea { 28 | // Default to also downloading JavaDoc and sources, when fetching dependencies. 29 | module { 30 | isDownloadJavadoc = true 31 | isDownloadSources = true 32 | } 33 | } 34 | 35 | group = properties("pluginGroup") 36 | version = properties("pluginVersion") 37 | 38 | var jpVersion = "3.24.2" 39 | 40 | // Configure project's dependencies 41 | repositories { 42 | mavenCentral() 43 | } 44 | dependencies { 45 | detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.20.0") 46 | 47 | implementation("com.github.javaparser:javaparser-symbol-solver-core:$jpVersion") 48 | implementation("org.apache.commons:commons-text:1.9") 49 | implementation("guru.nidi:graphviz-java-all-j2v8:0.18.1") 50 | implementation("org.apache.logging.log4j:log4j-core:2.17.2") 51 | implementation("org.apache.logging.log4j:log4j-slf4j-impl:2.17.2") 52 | 53 | testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") 54 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") 55 | } 56 | 57 | // Configure plugin `Gradle IntelliJ Plugin`. 58 | // Read more: https://github.com/JetBrains/gradle-intellij-plugin 59 | intellij { 60 | pluginName.set(properties("pluginName")) 61 | version.set(properties("platformVersion")) 62 | type.set(properties("platformType")) 63 | downloadSources.set(properties("platformDownloadSources").toBoolean()) 64 | updateSinceUntilBuild.set(true) 65 | 66 | // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file. 67 | plugins.set( 68 | properties("platformPlugins") 69 | .split(',') 70 | .map(String::trim) 71 | .filter(String::isNotEmpty) 72 | ) 73 | } 74 | 75 | // Configure plugin `Gradle Changelog Plugin`. 76 | // Read more: https://github.com/JetBrains/gradle-changelog-plugin 77 | changelog { 78 | version.set(properties("pluginVersion")) 79 | groups.set(listOf("Added", "Changed", "Deprecated", "Removed", "Fixed", "Security")) 80 | } 81 | 82 | // Configure Gradle Qodana Plugin - read more: https://github.com/JetBrains/gradle-qodana-plugin 83 | qodana { 84 | cachePath.set(projectDir.resolve(".qodana").canonicalPath) 85 | reportPath.set(projectDir.resolve("build/reports/inspections").canonicalPath) 86 | saveReport.set(true) 87 | showReport.set(System.getenv("QODANA_SHOW_REPORT")?.toBoolean() ?: false) 88 | } 89 | 90 | // Configure detekt plugin. 91 | // Read more: https://detekt.github.io/detekt/kotlindsl.html 92 | detekt { 93 | config = files("./detekt-config.yml") 94 | buildUponDefaultConfig = true 95 | } 96 | 97 | tasks.withType().configureEach { 98 | reports { 99 | xml.required.set(true) 100 | xml.required.set(true) 101 | txt.required.set(true) 102 | } 103 | } 104 | 105 | val copyReadmeTask = tasks.register("copyReadme") { 106 | val pathToTemplateReadme = "dev/README-template.md" 107 | val templateReadme = layout.projectDirectory.file(pathToTemplateReadme) 108 | val outputReadme = layout.projectDirectory 109 | from(templateReadme) 110 | into(outputReadme) 111 | 112 | rename("README-template.md", "README.md") 113 | 114 | expand("javaparser_version" to jpVersion) 115 | } 116 | 117 | tasks { 118 | // Set the JVM compatibility versions 119 | properties("javaVersion").let { 120 | withType { 121 | sourceCompatibility = it 122 | targetCompatibility = it 123 | } 124 | withType { 125 | kotlinOptions.jvmTarget = it 126 | } 127 | withType { 128 | jvmTarget = it 129 | } 130 | } 131 | 132 | wrapper { 133 | gradleVersion = properties("gradleVersion") 134 | } 135 | 136 | patchPluginXml { 137 | dependsOn(copyReadmeTask) 138 | 139 | version.set(properties("pluginVersion")) 140 | sinceBuild.set(properties("pluginSinceBuild")) 141 | untilBuild.set(properties("pluginUntilBuild")) 142 | 143 | // Extract the section from README.md and provide for the plugin's manifest 144 | pluginDescription.set( 145 | projectDir.resolve("README.md") 146 | .readText() 147 | .lines() 148 | .run { 149 | val start = "" 150 | val end = "" 151 | 152 | if (!containsAll(listOf(start, end))) { 153 | throw GradleException("Plugin description section not found in README.md:\n$start ... $end") 154 | } 155 | subList(indexOf(start) + 1, indexOf(end)) 156 | } 157 | .joinToString("\n") 158 | .run { markdownToHTML(this) } 159 | ) 160 | 161 | // Get the latest available change notes from the changelog file 162 | changeNotes.set( 163 | provider { 164 | changelog.run { 165 | getOrNull(properties("pluginVersion")) ?: getLatest() 166 | }.toHTML() 167 | } 168 | ) 169 | } 170 | 171 | runPluginVerifier { 172 | ideVersions.set(properties("pluginVerifierIdeVersions").split(',').map(String::trim).filter(String::isNotEmpty)) 173 | } 174 | 175 | // Configure UI tests plugin 176 | // Read more: https://github.com/JetBrains/intellij-ui-test-robot 177 | runIdeForUiTests { 178 | systemProperty("robot-server.port", "8082") 179 | systemProperty("ide.mac.message.dialogs.as.sheets", "false") 180 | systemProperty("jb.privacy.policy.text", "") 181 | systemProperty("jb.consents.confirmation.enabled", "false") 182 | } 183 | 184 | signPlugin { 185 | certificateChain.set(System.getenv("CERTIFICATE_CHAIN")) 186 | privateKey.set(System.getenv("PRIVATE_KEY")) 187 | password.set(System.getenv("PRIVATE_KEY_PASSWORD")) 188 | } 189 | 190 | publishPlugin { 191 | dependsOn("patchChangelog") 192 | token.set(System.getenv("INTELLIJ_PLUGIN_PUBLISH_TOKEN")) 193 | // pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3 194 | // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more: 195 | // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel 196 | channels.set(listOf(properties("pluginVersion").split('-').getOrElse(1) { "default" }.split('.').first())) 197 | } 198 | } 199 | 200 | tasks.jacocoTestReport { 201 | reports { 202 | xml.required.set(true) 203 | csv.required.set(true) 204 | html.required.set(true) 205 | } 206 | } 207 | 208 | // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html 209 | // https://docs.gradle.org/current/userguide/java_testing.html#using_junit5 210 | tasks.test { 211 | // enable JUnit Platform (a.k.a. JUnit 5) support 212 | useJUnitPlatform() 213 | 214 | finalizedBy("jacocoTestReport") 215 | doLast { 216 | logger.info("View code coverage at:") 217 | logger.info("file://$buildDir/reports/jacoco/test/html/index.html") 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /detekt-config.yml: -------------------------------------------------------------------------------- 1 | # Default detekt configuration: 2 | # https://github.com/detekt/detekt/blob/master/detekt-core/src/main/resources/default-detekt-config.yml 3 | 4 | formatting: 5 | Indentation: 6 | continuationIndentSize: 8 7 | ParameterListWrapping: 8 | indentSize: 8 9 | -------------------------------------------------------------------------------- /dev/README-template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # JavaParser AST Inspector 5 | 6 | 7 | ![Build](https://github.com/MysterAitch/JavaParser-AST-Inspector/workflows/Build/badge.svg) 8 | [![Version](https://img.shields.io/jetbrains/plugin/v/14245.svg)](https://plugins.jetbrains.com/plugin/14245) 9 | [![Downloads](https://img.shields.io/jetbrains/plugin/d/14245.svg)](https://plugins.jetbrains.com/plugin/14245) 10 | [![GitHub license](https://img.shields.io/github/license/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/blob/master/LICENSE) 11 | [![GitHub forks](https://img.shields.io/github/forks/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/network) 12 | [![GitHub stars](https://img.shields.io/github/stars/MysterAitch/JavaParser-AST-Inspector)](https://github.com/MysterAitch/JavaParser-AST-Inspector/stargazers) 13 | 14 | 15 | 16 | 17 | Browse and navigate the Abstract Syntax Tree (AST) based on the Java code in your editor. 18 | 19 | Uses JavaParser v${javaparser_version} which currently handles Java 1-15, with Java 16 coming soon! 20 | 21 | 22 | 23 | ## Installation 24 | 25 | - Using IDE built-in plugin system: 26 | 27 | Settings/Preferences > 28 | Plugins > 29 | Marketplace > 30 | Search for "JavaParser-AST-Inspector" > 31 | Install Plugin 32 | 33 | - Manually: 34 | 35 | Download the [latest release](https://github.com/MysterAitch/JavaParser-AST-Inspector/releases/latest) and install it manually using 36 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... 37 | 38 | 39 | 40 | ## Hackathon Notes 41 | - Plan: Using JavaParser to parse a given project, and display a navigable syntax tree to navigate around the project. 42 | - Reality: Whatever comes out of the #covhack2020 hackathon :) 43 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1024m 2 | 3 | # IntelliJ Platform Artifacts Repositories 4 | # -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html 5 | 6 | pluginGroup = com.github.rogerhowell 7 | pluginName = JavaParser-AST-Inspector 8 | pluginVersion = 0.5.5-SNAPSHOT 9 | 10 | ## See build numbers here: 11 | ## https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html#intellij-platform-based-products-of-recent-ide-versions 12 | ## Since 2019.2 (192), until most recent (currently 213 / 2021.3) 13 | pluginSinceBuild = 192 14 | pluginUntilBuild = 221.* 15 | 16 | # Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl 17 | # See https://jb.gg/intellij-platform-builds-list for available build versions 18 | ## Note: If exception about unable to resolve download url, it may be that the download url is no longer listed. 19 | ## In this case, a later patch release may need to be specified (details in the url to a JSON file above). 20 | #### > Task :runPluginVerifier 21 | #### [gradle-intellij-plugin :JavaParser AST Inspector:runPluginVerifier] Cannot resolve direct download URL for: https://data.services.jetbrains.com/products/download?code=IC&platform=linux&type=release&version=2019.3 22 | #### java.io.FileNotFoundException: https://data.services.jetbrains.com/products/download?code=IC&platform=linux&type=release&version=2019.3 23 | #pluginVerifierIdeVersions = 2019.3.5, 2020.1.4, 2020.2.4, 2020.3.2, 2021.1, 2021.3, 2021.3.1, 2021.3.2 24 | ## Leaving this blank will automatically determine the versions to check against 25 | pluginVerifierIdeVersions = 26 | 27 | #// See https://github.com/JetBrains/gradle-intellij-plugin/#intellij-platform-properties 28 | #// Note that the default is 'LATEST-EAP-SNAPSHOT', but can be set to specific versions (e.g. '2020.1') 29 | platformType = IC 30 | platformVersion = LATEST-EAP-SNAPSHOT 31 | #platformVersion = 2020.2 32 | platformDownloadSources = true 33 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html 34 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 35 | platformPlugins = com.intellij.java 36 | 37 | # Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3 38 | javaVersion = 11 39 | 40 | # Gradle Releases -> https://github.com/gradle/gradle/releases 41 | gradleVersion = 7.3.3 42 | 43 | # Opt-out flag for bundling Kotlin standard library. 44 | # See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details. 45 | # suppress inspection "UnusedProperty" 46 | kotlin.stdlib.default.dependency = false 47 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /qodana.yml: -------------------------------------------------------------------------------- 1 | # Qodana configuration: 2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html 3 | 4 | version: 1.0 5 | profile: 6 | name: qodana.recommended 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "JavaParser AST Inspector" 2 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/logging/NotificationLogger.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.logging; 2 | 3 | import com.intellij.notification.NotificationDisplayType; 4 | import com.intellij.notification.NotificationGroup; 5 | import com.intellij.notification.NotificationType; 6 | import com.intellij.openapi.diagnostic.Logger; 7 | import com.intellij.openapi.project.Project; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.Optional; 12 | 13 | public class NotificationLogger { 14 | 15 | private static final boolean IS_LOGGING_ENABLED_DEBUG = false; 16 | private static final boolean IS_LOGGING_ENABLED_ERROR = true; 17 | private static final boolean IS_LOGGING_ENABLED_INFO = true; 18 | private static final boolean IS_LOGGING_ENABLED_TRACE = false; 19 | private static final boolean IS_LOGGING_ENABLED_WARN = true; 20 | 21 | @NotNull 22 | private final Logger logger; 23 | 24 | @NotNull 25 | private final NotificationGroup notificationGroup_debug; 26 | @NotNull 27 | private final NotificationGroup notificationGroup_error; 28 | @NotNull 29 | private final NotificationGroup notificationGroup_info; 30 | @NotNull 31 | private final NotificationGroup notificationGroup_trace; 32 | @NotNull 33 | private final NotificationGroup notificationGroup_warn; 34 | 35 | 36 | // public NotificationLogger(@NotNull Class clazz, @NotNull String displayId) { 37 | public NotificationLogger(@NotNull Class clazz) { 38 | this.logger = Logger.getInstance(clazz.getName()); 39 | this.notificationGroup_trace = new NotificationGroup( 40 | clazz.getName() + " (trace)", 41 | NotificationDisplayType.NONE, 42 | IS_LOGGING_ENABLED_TRACE 43 | ); 44 | this.notificationGroup_debug = new NotificationGroup( 45 | clazz.getName() + " (debug)", 46 | NotificationDisplayType.NONE, 47 | IS_LOGGING_ENABLED_DEBUG 48 | ); 49 | this.notificationGroup_info = new NotificationGroup( 50 | clazz.getName() + " (information)", 51 | NotificationDisplayType.NONE, 52 | IS_LOGGING_ENABLED_INFO 53 | ); 54 | this.notificationGroup_warn = new NotificationGroup( 55 | clazz.getName() + " (warnings)", 56 | NotificationDisplayType.BALLOON, 57 | IS_LOGGING_ENABLED_WARN 58 | ); 59 | this.notificationGroup_error = new NotificationGroup( 60 | clazz.getName() + " (errors)", 61 | NotificationDisplayType.STICKY_BALLOON, 62 | IS_LOGGING_ENABLED_ERROR 63 | ); 64 | } 65 | 66 | 67 | public void debug(@NotNull String messageContent) { 68 | this.debug(null, messageContent); 69 | } 70 | 71 | 72 | public void debug(@Nullable Project project, @NotNull String messageContent) { 73 | this.logger.debug(messageContent); 74 | this.notificationGroup_debug 75 | .createNotification("DEBUG", this.getSubtitle(project), messageContent, NotificationType.INFORMATION) 76 | .notify(project); 77 | } 78 | 79 | 80 | public void debug(@NotNull String messageContent, Throwable e) { 81 | this.debug(null, messageContent, e); 82 | } 83 | 84 | 85 | public void debug(@Nullable Project project, @NotNull String messageContent, Throwable e) { 86 | this.logger.debug(messageContent, e); 87 | this.notificationGroup_debug 88 | .createNotification("DEBUG", this.getSubtitle(project), messageContent, NotificationType.INFORMATION) 89 | .notify(project); 90 | } 91 | 92 | 93 | public void error(@NotNull String messageContent) { 94 | this.error(null, messageContent); 95 | } 96 | 97 | 98 | public void error(@Nullable Project project, @NotNull String messageContent) { 99 | this.logger.error(messageContent); 100 | this.notificationGroup_error 101 | .createNotification("ERROR", this.getSubtitle(project), messageContent, NotificationType.ERROR) 102 | .notify(project); 103 | } 104 | 105 | 106 | public void error(@NotNull String messageContent, Throwable e) { 107 | this.error(null, messageContent, e); 108 | } 109 | 110 | 111 | public void error(@Nullable Project project, @NotNull String messageContent, Throwable e) { 112 | this.logger.error(messageContent, e); 113 | this.notificationGroup_error 114 | .createNotification("ERROR", this.getSubtitle(project), messageContent, NotificationType.ERROR) 115 | .notify(project); 116 | } 117 | // public void trace(@NotNull String messageContent, Throwable e) { 118 | // this.trace(null, messageContent, e); 119 | // } 120 | // public void trace(@Nullable Project project, @NotNull String messageContent, Throwable e) { 121 | // final String projectName = (project == null) ? "" : project.getName(); 122 | // this.logger.trace(messageContent, e); 123 | // notificationGroup.createNotification("Title", "Project: " + projectName(project).orElse(""), messageContent).notify(project); 124 | // } 125 | 126 | 127 | @NotNull 128 | private String getSubtitle(@Nullable final Project project) { 129 | return "Project: " + this.projectName(project).orElse(""); 130 | } 131 | 132 | 133 | public void info(@NotNull String messageContent) { 134 | this.info(null, messageContent); 135 | } 136 | 137 | 138 | public void info(@Nullable Project project, @NotNull String messageContent) { 139 | this.logger.info(messageContent); 140 | this.notificationGroup_info 141 | .createNotification("INFORMATION", this.getSubtitle(project), messageContent, NotificationType.INFORMATION) 142 | .notify(project); 143 | } 144 | 145 | 146 | public void info(@NotNull String messageContent, Throwable e) { 147 | this.info(null, messageContent, e); 148 | } 149 | 150 | 151 | public void info(@Nullable Project project, @NotNull String messageContent, Throwable e) { 152 | this.logger.info(messageContent, e); 153 | this.notificationGroup_info 154 | .createNotification("INFORMATION", this.getSubtitle(project), messageContent, NotificationType.INFORMATION) 155 | .notify(project); 156 | } 157 | 158 | 159 | private Optional projectName(@Nullable Project project) { 160 | if (project == null) { 161 | return Optional.empty(); 162 | } 163 | return Optional.of(project.getName()); 164 | } 165 | 166 | 167 | public void trace(@NotNull String messageContent) { 168 | this.trace(null, messageContent); 169 | } 170 | 171 | 172 | public void trace(@Nullable Project project) { 173 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2]; 174 | 175 | String location = stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber(); 176 | String methodName = stackTraceElement.getMethodName(); 177 | String message = location + " (#" + methodName + ")"; 178 | 179 | this.trace(project, message); 180 | } 181 | 182 | 183 | public void trace(@Nullable Project project, @NotNull String messageContent) { 184 | this.logger.trace(messageContent); 185 | this.notificationGroup_trace 186 | .createNotification("TRACE", this.getSubtitle(project), messageContent, NotificationType.INFORMATION) 187 | .notify(project); 188 | } 189 | 190 | 191 | public void traceEnter() { 192 | this.traceEnter(null); 193 | } 194 | 195 | 196 | public void traceEnter(@Nullable Project project) { 197 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2]; 198 | 199 | String location = stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber(); 200 | String methodName = stackTraceElement.getMethodName(); 201 | 202 | this.traceEnter(project, methodName, location); 203 | } 204 | 205 | 206 | public void traceEnter(@Nullable Project project, @NotNull String methodName, @NotNull String location) { 207 | // String messageContent = location + " (#" + methodName + ")"; 208 | String locationString = "#" + methodName + " @ " + location + ""; 209 | 210 | this.logger.trace(locationString); 211 | this.notificationGroup_trace 212 | .createNotification("TRACE ENTER", locationString, this.getSubtitle(project), NotificationType.INFORMATION) 213 | .notify(project); 214 | } 215 | 216 | 217 | public void warn(@NotNull String messageContent) { 218 | this.warn(null, messageContent); 219 | } 220 | 221 | 222 | public void warn(@Nullable Project project, @NotNull String messageContent) { 223 | this.logger.warn(messageContent); 224 | this.notificationGroup_warn 225 | .createNotification("WARNING", this.getSubtitle(project), messageContent, NotificationType.WARNING) 226 | .notify(project); 227 | } 228 | 229 | 230 | public void warn(@NotNull String messageContent, Throwable e) { 231 | this.warn(null, messageContent, e); 232 | } 233 | 234 | 235 | public void warn(@Nullable Project project, @NotNull String messageContent, Throwable e) { 236 | this.logger.warn(messageContent, e); 237 | this.notificationGroup_warn 238 | .createNotification("WARNING", this.getSubtitle(project), messageContent, NotificationType.WARNING) 239 | .notify(project); 240 | } 241 | 242 | } 243 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/ASCIITreePrinter.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 2 | 3 | import com.github.javaparser.Position; 4 | import com.github.javaparser.ast.Node; 5 | import com.github.javaparser.printer.configuration.DefaultConfigurationOption; 6 | import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration; 7 | import com.github.javaparser.printer.configuration.PrinterConfiguration; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.StringReader; 12 | import java.util.ArrayList; 13 | import java.util.Collections; 14 | import java.util.Iterator; 15 | import java.util.List; 16 | import java.util.function.Function; 17 | 18 | /** 19 | * ASCII printable text tree (of the Nodes within an AST) i.e. 20 | *
 21 |  * ASCIITreePrinter.print(StaticJavaParser.parseExpression( "new Object(){\n    int i;\n}") );
 22 |  * 
23 | * prints: 24 | *
 25 |  * "new Object() {...}" ObjectCreationExpr : (1,1)-(3,1)
 26 |  * ├─"Object" ClassOrInterfaceType : (1,5)-(1,10)
 27 |  * │ └─"Object" SimpleName : (1,5)-(1,10)
 28 |  * └─"int i;" FieldDeclaration : (2,5)-(2,10)
 29 |  *   └─"i" VariableDeclarator : (2,9)-(2,9)
 30 |  *     ├─"int" PrimitiveType : (2,5)-(2,7)
 31 |  *     └─"i" SimpleName : (2,9)-(2,9)
 32 |  * 
33 | * 34 | * ...for 35 | *
 36 |  * new Object(){
 37 |  *     int i;
 38 |  * }
 39 |  * 
40 | * 41 | * NOTE: this is handy for printing a summary of the {@link Node}s in the AST and how they are connected in the tree 42 | * for debugging what the structure of the AST is (directly in the console). 43 | * 44 | * The POINT of this tool is to have a "quick and dirty" way of "sanity checking" that the structure of the AST is. 45 | * 46 | * modified from: 47 | * https://stackoverflow.com/questions/4965335/how-to-print-binary-tree-diagram 48 | * 49 | * @author Eric DeFazio 50 | */ 51 | public class ASCIITreePrinter implements NodePrinter { 52 | 53 | 54 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000; 55 | 56 | private static final String NEWLINE = String.format("%n"); 57 | 58 | /** 59 | * The ASCIITreePrinter doesn't do comments by design 60 | */ 61 | private static final PrinterConfiguration PRINT_NO_COMMENTS = new DefaultPrinterConfiguration() 62 | .removeOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.PRINT_COMMENTS)) 63 | .removeOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.PRINT_JAVADOC)); 64 | 65 | /** 66 | * Print format each {@link Node} in the tree (prints to a single line) for example: 67 | *
 68 |      * CompilationUnit (1,1)-(15,3) : "@Deprecated...}"
 69 |      * \____________/  \__________/ : \_______________/
 70 |      *   node class     node range  :   node summary
 71 |      * 
72 | * 73 | * @see ASCIITreePrinter#printNodeSummary(Node) 74 | * @see ASCIITreePrinter#printRange(Node) 75 | * @see ASCIITreePrinter#printRange(Node) 76 | */ 77 | 78 | public static final Function CLASS_RANGE_SUMMARY_FORMAT = n -> n.getClass().getSimpleName() + " " + printRangeCoordinates(n) + " : \"" + printNodeSummary(n) + "\""; 79 | 80 | /** 81 | * Print format each {@link Node} in the tree (prints to a single line) for example: 82 | *
 83 |      *  "@Deprecated...}" CompilationUnit : (1,1)-(15,3)
 84 |      *  \_______________/ \____________/   \__________/
 85 |      *    node summary      node class      node range
 86 |      *  
87 | * 88 | * @see #printNodeSummary(Node) 89 | * @see #printRange(Node) 90 | */ 91 | public static final Function SUMMARY_CLASS_RANGE_FORMAT = n -> "\"" + printNodeSummary(n) + "\" " + n.getClass().getSimpleName() + " : " + printRangeCoordinates(n); 92 | 93 | /** 94 | * Print format each {@link Node} in the tree (prints to a single line) for example: 95 | *
 96 |      *  "@Deprecated...}" [CompilationUnit]
 97 |      *  \_______________/ \_______________/
 98 |      *    node summary      node class
 99 |      *  
100 | * 101 | * @see #printNodeSummary(Node) 102 | */ 103 | public static final Function SUMMARY_CLASS_FORMAT = n -> "\"" + printNodeSummary(n) + "\" [" + n.getClass().getSimpleName() + "]"; 104 | 105 | /** 106 | * DEFAULT format for printing each node (on a single line in the tree) 107 | */ 108 | public Function nodeFormat = SUMMARY_CLASS_RANGE_FORMAT; 109 | 110 | 111 | /** 112 | * Create an ASCIITreePrinter with the default format 113 | */ 114 | public ASCIITreePrinter() { 115 | this(SUMMARY_CLASS_RANGE_FORMAT); 116 | } 117 | 118 | 119 | /** 120 | * An ASCIITreePrinter with a specified nodeFormat for formatting each node 121 | * 122 | * @param nodeFormat 123 | */ 124 | public ASCIITreePrinter(Function nodeFormat) { 125 | this.nodeFormat = nodeFormat; 126 | } 127 | 128 | 129 | /** 130 | * Breaks the single String into an array of String Lines 131 | * 132 | * @param inputString a single String 133 | * @return a collection of strings, which is inputString split by newlines 134 | */ 135 | public static List lines(String inputString) { 136 | if (inputString == null) { 137 | return Collections.emptyList(); 138 | } 139 | 140 | List strLine = new ArrayList<>(); 141 | try (BufferedReader br = new BufferedReader(new StringReader(inputString))) { 142 | String line = br.readLine(); 143 | while (line != null) { 144 | strLine.add(line); 145 | line = br.readLine(); 146 | } 147 | } catch (IOException e) { 148 | //this shouldnt happen 149 | throw new RuntimeException("Error formatting Lines", e); 150 | } 151 | 152 | return strLine; 153 | } 154 | 155 | 156 | /** 157 | * Print a Tree to System.out defining the contents with the AST node 158 | * 159 | * @param rootNode the root 160 | * @param nodeFormat 161 | */ 162 | public static void print(Node rootNode, Function nodeFormat) { 163 | System.out.println(TNode.of(new TNode(rootNode)).output(nodeFormat)); 164 | } 165 | 166 | 167 | /** 168 | * Print a Tree to System.out defining the contents with the AST node 169 | * 170 | * @param rootNode any AST node to describe the contents of in tree form 171 | */ 172 | public static void print(Node rootNode) { 173 | System.out.println(TNode.of(new TNode(rootNode)).output(SUMMARY_CLASS_RANGE_FORMAT)); 174 | } 175 | 176 | 177 | /** 178 | * Prints an abbreviated view of a AST node (as to keep the content all on one line) 179 | * in the event the text is truncated, appends "..." and the last non-empty character 180 | * on the last non-empty line of the node. i.e. for this ObjectCreationExpr AST which spans multiple lines: 181 | *
182 |      *     new Object(){
183 |      *         int i=0;
184 |      *     }
185 |      * 
186 | * prints: 187 | *
"new Object() {...}"
188 | * 189 | * @param n 190 | * @return 191 | */ 192 | public static String printNodeSummary(Node n) { 193 | String s = n.toString(PRINT_NO_COMMENTS).trim(); 194 | if (s.isEmpty()) { 195 | return ""; //this happens, sometimes we have UnknownType (for Lambda) with NO text 196 | } 197 | List lines = lines(s); 198 | if (lines.get(lines.size() - 1).isEmpty()) { 199 | lines.remove(lines.size() - 1); 200 | } 201 | if (lines.size() == 1) { 202 | return lines.get(0); //its all on one line 203 | } 204 | String lastLine = lines.get(Math.max(lines.size() - 1, 0)); 205 | //returns the first line, then "..." then the last character on the last line; usually ( '}', ';' or ')' ) 206 | return lines.get(0) + "..." + lastLine.charAt(lastLine.length() - 1); 207 | } 208 | 209 | 210 | public static String printPosition(Position p) { 211 | return "(" + p.line + "," + p.column + ")"; 212 | } 213 | 214 | 215 | /** 216 | * Tries to print the Range of the Node n, if the Range is not present 217 | * (which happens in UnknownType of Lambda for instance) prints (-) 218 | */ 219 | public static String printRange(Node n) { 220 | if (n.getRange().isPresent()) { 221 | return n.getRange().get().toString(); 222 | } 223 | //this sometimes happens (i.e. a Unknown type AST node has no text and no range) 224 | return "(-)"; 225 | } 226 | 227 | 228 | /** 229 | * Prints range coordinates with (line,column)-(line,column) i.e. 230 | * (1,1)-(5,1) = line 1, column 1, to line 5 column 1 231 | * 232 | * @param n the node to print 233 | * @return String representing the line,column range coordinates 234 | */ 235 | public static String printRangeCoordinates(Node n) { 236 | if (n.getRange().isPresent()) { 237 | return printPosition(n.getRange().get().begin) + "-" + printPosition(n.getRange().get().end); 238 | } 239 | //this sometimes happens (i.e. a Unknown type AST node has no text and no range) 240 | return "(-)"; 241 | } 242 | 243 | 244 | /** 245 | * Build the output as a String and return it 246 | * 247 | * @param rootNode the top AST {@link Node} to print the contents of 248 | * @return a String representing an ASCII tree 249 | */ 250 | @Override 251 | public String output(Node rootNode) { 252 | return TNode.of(new TNode(rootNode)).output(this.nodeFormat); 253 | } 254 | 255 | 256 | /** 257 | * @param rootNode 258 | * @param nodeFormat how to print out each node 259 | * @return a String representing an ASCII tree 260 | */ 261 | public String output(Node rootNode, Function nodeFormat) { 262 | return TNode.of(new TNode(rootNode)).output(nodeFormat); 263 | } 264 | 265 | 266 | /** 267 | * Change the Node Format for printing the contents of each node to a line 268 | * 269 | * @param nodeFormat 270 | * @return 271 | */ 272 | public ASCIITreePrinter setNodeFormat(Function nodeFormat) { 273 | this.nodeFormat = nodeFormat; 274 | return this; 275 | } 276 | 277 | 278 | @Override 279 | public String toString() { 280 | return "ASCIITreePrinter{" + 281 | "nodeFormat=" + this.nodeFormat + 282 | '}'; 283 | } 284 | 285 | 286 | /** 287 | * Underlying Nodes that will print out 288 | */ 289 | private static class TNode { 290 | final Node node; 291 | public List children = new ArrayList<>(); 292 | 293 | 294 | /** 295 | * Build a ROOT TNode that can contain children 296 | * 297 | * @param rootNode 298 | */ 299 | public TNode(Node rootNode) { 300 | this.node = rootNode; 301 | } 302 | 303 | 304 | /** 305 | * builds and returns a TNode and resolves 306 | * 307 | * @param tn 308 | * @return 309 | */ 310 | public static TNode of(TNode tn) { 311 | tn.node.stream(Node.TreeTraversal.DIRECT_CHILDREN).forEach(c -> { 312 | TNode child = new TNode(c); 313 | tn.children.add(child); 314 | of(child); 315 | }); 316 | return tn; 317 | } 318 | 319 | 320 | /** 321 | * Builds the Ascii tree into the buffer 322 | * 323 | * @param nodeStringFunction 324 | * @param buffer 325 | * @param prefix 326 | * @param childrenPrefix 327 | */ 328 | private void build(Function nodeStringFunction, StringBuilder buffer, String prefix, String childrenPrefix) { 329 | buffer.append(prefix); 330 | buffer.append(nodeStringFunction.apply(this.node)); 331 | buffer.append(NEWLINE); 332 | for (Iterator it = this.children.iterator(); it.hasNext(); ) { 333 | TNode next = it.next(); 334 | /* this is the more "open" format 335 | if (it.hasNext()) { 336 | next.build(nodeStringFunction, buffer, childrenPrefix + "├── ", childrenPrefix + "│ "); 337 | } else { 338 | next.build(nodeStringFunction, buffer, childrenPrefix + "└── ", childrenPrefix + " "); 339 | } 340 | */ 341 | // this is the "dense"/compact format 342 | if (it.hasNext()) { 343 | next.build(nodeStringFunction, buffer, childrenPrefix + "├─", childrenPrefix + "│ "); 344 | } else { 345 | next.build(nodeStringFunction, buffer, childrenPrefix + "└─", childrenPrefix + " "); 346 | } 347 | } 348 | } 349 | 350 | 351 | public String output(Function nodeToStringFunction) { 352 | StringBuilder buffer = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 353 | this.build(nodeToStringFunction, buffer, "", ""); 354 | return buffer.toString(); 355 | } 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CustomDotPrinter.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2007-2010 Júlio Vilmar Gesser. 4 | * Copyright (C) 2011, 2013-2016 The JavaParser Team. 5 | * 6 | * This file is part of JavaParser. 7 | * 8 | * JavaParser can be used either under the terms of 9 | * a) the GNU Lesser General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * b) the terms of the Apache License 13 | * 14 | * You should have received a copy of both licenses in LICENCE.LGPL and 15 | * LICENCE.APACHE. Please refer to those files for details. 16 | * 17 | * JavaParser is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU Lesser General Public License for more details. 21 | */ 22 | 23 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 24 | 25 | 26 | import com.github.javaparser.Range; 27 | import com.github.javaparser.ast.Node; 28 | import com.github.javaparser.ast.NodeList; 29 | import com.github.javaparser.ast.expr.Expression; 30 | import com.github.javaparser.metamodel.NodeMetaModel; 31 | import com.github.javaparser.metamodel.PropertyMetaModel; 32 | import com.github.javaparser.resolution.UnsolvedSymbolException; 33 | import com.github.javaparser.resolution.types.ResolvedType; 34 | import org.apache.commons.text.StringEscapeUtils; 35 | 36 | import java.util.List; 37 | 38 | import static com.github.javaparser.utils.Utils.assertNotNull; 39 | import static java.util.stream.Collectors.toList; 40 | 41 | /** 42 | * Outputs a Graphviz diagram of the AST. 43 | */ 44 | public class CustomDotPrinter implements NodePrinter { 45 | 46 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000; 47 | 48 | private static final boolean DEFAULT_RESOLVE_TYPES = false; 49 | 50 | private final boolean outputNodeType; 51 | private int nodeCount; 52 | 53 | 54 | public CustomDotPrinter(final boolean outputNodeType) { 55 | this.outputNodeType = outputNodeType; 56 | this.nodeCount = 0; 57 | } 58 | 59 | 60 | private static String escape(String value) { 61 | return value.replace("\"", "\\\""); 62 | } 63 | 64 | 65 | private String nextNodeName() { 66 | return "n" + (this.nodeCount++); 67 | } 68 | 69 | 70 | /** 71 | * @param node The node to be printed - typically a CompilationUnit. 72 | * @param resolveTypes Should node types be resolved? 73 | * @return The DOT-formatted equivalent of node. 74 | */ 75 | public String output(final Node node, final boolean resolveTypes) { 76 | this.nodeCount = 0; 77 | final StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 78 | output.append("digraph {"); 79 | this.output(node, null, "root", output, resolveTypes); 80 | output.append(System.lineSeparator()).append("}"); 81 | return output.toString(); 82 | } 83 | 84 | 85 | public void output(final Node node, final String parentNodeName, final String name, final StringBuilder builder) { 86 | this.output(node, parentNodeName, name, builder, DEFAULT_RESOLVE_TYPES); 87 | } 88 | 89 | 90 | public void output(final Node node, final String parentNodeName, final String name, final StringBuilder builder, final boolean resolveTypes) { 91 | assertNotNull(node); 92 | final NodeMetaModel metaModel = node.getMetaModel(); 93 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 94 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList()); 95 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList()); 96 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList()); 97 | 98 | final String typeName = metaModel.getTypeName(); 99 | String range = ""; 100 | 101 | // Custom: If range is present, add it. 102 | if (node.getRange().isPresent()) { 103 | range += ""; 104 | range += this.rangeAsString(node.getRange().get()); 105 | range += ""; 106 | } 107 | 108 | 109 | final String lineColor; 110 | final String lineLabel; 111 | if ("comment".equals(name)) { 112 | // lineColor = "gray"; 113 | lineColor = "LightGray"; 114 | // lineLabel = "comment"; 115 | lineLabel = ""; 116 | } else if ("name".equals(name)) { 117 | // lineColor="darkgreen"; 118 | // lineColor="blue"; 119 | // lineColor="SlateBlue"; 120 | lineColor = "SteelBlue"; 121 | // lineLabel = "name"; 122 | lineLabel = ""; 123 | } else if ("StringLiteralExpr".equals(typeName)) { 124 | // } else if (typeName.endsWith("LiteralExpr")) { 125 | // lineColor="SlateBlue"; 126 | lineColor = "SeaGreen"; 127 | lineLabel = "Literal Expression"; 128 | // lineLabel = "LiteralExpr"; 129 | } else { 130 | lineColor = "black"; 131 | lineLabel = ""; 132 | } 133 | 134 | final String ndName = this.nextNodeName(); 135 | StringBuilder nodeDot = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 136 | nodeDot.append(System.lineSeparator()); 137 | nodeDot.append(ndName); 138 | nodeDot.append(" ["); 139 | nodeDot.append("shape=none"); 140 | nodeDot.append(","); 141 | nodeDot.append("label=<"); 142 | 143 | nodeDot.append(""); 144 | 145 | nodeDot.append(""); 146 | nodeDot.append(""); 147 | nodeDot.append(""); 148 | nodeDot.append(""); 149 | nodeDot.append(escape(name)); 150 | if (this.outputNodeType) { 151 | nodeDot.append(" (").append(typeName).append(")"); 152 | } 153 | nodeDot.append(""); 154 | nodeDot.append("
"); 155 | nodeDot.append(""); 156 | nodeDot.append(range); 157 | nodeDot.append(""); 158 | 159 | 160 | if (resolveTypes && node instanceof Expression) { 161 | final Expression bar = (Expression) node; 162 | 163 | String returnTypeString = null; 164 | 165 | try { 166 | // if (!bar.toString().equals("System") && !bar.toString().equals("String")) { 167 | ResolvedType returnType = bar.calculateResolvedType(); 168 | returnTypeString = StringEscapeUtils.escapeHtml4(returnType.describe()); 169 | // } 170 | } catch (final UnsolvedSymbolException e) { 171 | // returnTypeString = "Unable to resolve type of " + bar + " (UnsolvedSymbolException)"; 172 | System.err.println("Unable to resolve type of " + bar + " (UnsolvedSymbolException)"); 173 | e.printStackTrace(); 174 | } catch (final Exception e) { 175 | // returnTypeString = "Unable to resolve type of " + bar + " (Exception - " + e.getClass().getName() + ")"; 176 | System.err.println("Unable to resolve type of " + bar + " (Exception - " + e.getClass().getName() + ")"); 177 | e.printStackTrace(); 178 | } 179 | 180 | if (returnTypeString != null) { 181 | nodeDot.append("
"); 182 | nodeDot.append(""); 183 | nodeDot.append("Resolved Type: "); 184 | nodeDot.append(returnTypeString); 185 | nodeDot.append(""); 186 | } 187 | } 188 | 189 | nodeDot.append(""); 190 | nodeDot.append(""); 191 | 192 | for (final PropertyMetaModel a : attributes) { 193 | nodeDot.append(""); 194 | nodeDot.append("").append(a.getName()).append(""); 195 | nodeDot.append(""); 196 | 197 | String value = a.getValue(node).toString(); 198 | String[] lines = value.trim().split("\\r?\\n"); 199 | 200 | String cellAlignment = lines.length > 1 ? "left" : "center"; 201 | nodeDot.append(""); 202 | for (final String line : lines) { 203 | nodeDot.append(""); 204 | } 205 | nodeDot.append("
").append(StringEscapeUtils.escapeHtml4(line)).append("
"); 206 | 207 | nodeDot.append(""); 208 | nodeDot.append(""); 209 | } 210 | 211 | nodeDot.append(""); 212 | 213 | nodeDot.append("
"); 214 | nodeDot.append(">];"); 215 | 216 | builder.append(nodeDot.toString()); 217 | 218 | 219 | if (parentNodeName != null) { 220 | builder.append(System.lineSeparator()) 221 | .append(parentNodeName).append(" -> ").append(ndName) 222 | .append(" [").append("color=").append(lineColor).append(", fontcolor=").append(lineColor).append(", label=\"").append(lineLabel).append("\"").append("]") 223 | .append(";"); 224 | } 225 | 226 | for (final PropertyMetaModel sn : subNodes) { 227 | final Node nd = (Node) sn.getValue(node); 228 | if (nd != null) { 229 | this.output(nd, ndName, sn.getName(), builder, resolveTypes); 230 | } 231 | } 232 | 233 | String color; 234 | String label; 235 | 236 | for (final PropertyMetaModel sl : subLists) { 237 | final NodeList nl = (NodeList) sl.getValue(node); 238 | if (nl != null && nl.isNonEmpty()) { 239 | // color = "FireBrick"; 240 | // color = "red"; 241 | color = "OrangeRed"; 242 | label = "property list"; 243 | 244 | final String ndLstName = this.nextNodeName(); 245 | builder.append(System.lineSeparator()).append(ndLstName).append(" [shape=ellipse,color=").append(color).append(",label=\"").append(escape(sl.getName())).append("\"];"); 246 | builder.append(System.lineSeparator()).append(ndName).append(" -> ") 247 | .append(ndLstName) 248 | .append(" [").append("color=").append(color).append(", fontcolor=").append(color).append(", label=\"").append(label).append("\"").append("]"); 249 | // .append(" [color = ").append(color).append("];"); 250 | final String slName = sl.getName().substring(0, sl.getName().length() - 1); 251 | for (final Node nd : nl) { 252 | this.output(nd, ndLstName, slName, builder, resolveTypes); 253 | } 254 | } 255 | } 256 | } 257 | 258 | 259 | @Override 260 | public String output(final Node node) { 261 | return this.output(node, DEFAULT_RESOLVE_TYPES); 262 | } 263 | 264 | 265 | private String rangeAsString(final Range range) { 266 | final int startLine = range.begin.line; 267 | final int startColumn = range.begin.column; 268 | final int endLine = range.end.line; 269 | final int endColumn = range.end.column; 270 | 271 | return "[" + startLine + ":" + startColumn + "-" + endLine + ":" + endColumn + "]"; 272 | } 273 | 274 | 275 | @Override 276 | public String toString() { 277 | return "CustomDotPrinter{" + 278 | "outputNodeType=" + this.outputNodeType + 279 | ", nodeCount =" + this.nodeCount + 280 | '}'; 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CustomJsonPrinter.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 2 | 3 | import com.github.javaparser.ast.Node; 4 | import com.github.javaparser.ast.NodeList; 5 | import com.github.javaparser.ast.expr.ObjectCreationExpr; 6 | import com.github.javaparser.metamodel.NodeMetaModel; 7 | import com.github.javaparser.metamodel.PropertyMetaModel; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | import static com.github.javaparser.utils.Utils.assertNotNull; 14 | import static java.util.stream.Collectors.toList; 15 | 16 | /** 17 | * Outputs a JSON file containing the AST meant for inspecting it. 18 | */ 19 | public class CustomJsonPrinter implements NodePrinter { 20 | private final boolean outputNodeType; 21 | 22 | 23 | public CustomJsonPrinter(final boolean outputNodeType) { 24 | this.outputNodeType = outputNodeType; 25 | } 26 | 27 | 28 | private static String q(final String value) { 29 | return "\"" + value.replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r") + "\""; 30 | } 31 | 32 | 33 | @Override 34 | public String output(final Node node) { 35 | return this.output(node, null, 0); 36 | } 37 | 38 | 39 | public String output(final Node node, final String name, final int level) { 40 | assertNotNull(node); 41 | final NodeMetaModel metaModel = node.getMetaModel(); 42 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 43 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList()); 44 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList()); 45 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList()); 46 | 47 | final List content = new ArrayList<>(); 48 | 49 | if (this.outputNodeType) { 50 | content.add(CustomJsonPrinter.q("_type") + ":" + CustomJsonPrinter.q(metaModel.getTypeName())); 51 | } 52 | 53 | for (final PropertyMetaModel attributeMetaModel : attributes) { 54 | content.add(CustomJsonPrinter.q(attributeMetaModel.getName()) + ":" + CustomJsonPrinter.q(attributeMetaModel.getValue(node).toString())); 55 | } 56 | 57 | 58 | // Custom: If range is present, add it. 59 | if (node.getRange().isPresent()) { 60 | content.add(CustomJsonPrinter.q("_start_line") + ":" + node.getRange().get().begin.line); 61 | content.add(CustomJsonPrinter.q("_start_column") + ":" + node.getRange().get().begin.column); 62 | content.add(CustomJsonPrinter.q("_end_line") + ":" + node.getRange().get().end.line); 63 | content.add(CustomJsonPrinter.q("_end_column") + ":" + node.getRange().get().end.column); 64 | } 65 | 66 | // Object creation 67 | if (node.getClass().getSimpleName().equals("ObjectCreationExpr")) { 68 | final ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr) node; 69 | final String foo = objectCreationExpr.getType().getName().asString(); 70 | content.add(CustomJsonPrinter.q("_typeNameString") + ":" + CustomJsonPrinter.q(foo)); 71 | } 72 | 73 | 74 | for (final PropertyMetaModel subNodeMetaModel : subNodes) { 75 | final Node value = (Node) subNodeMetaModel.getValue(node); 76 | if (value != null) { 77 | content.add(this.output(value, subNodeMetaModel.getName(), level + 1)); 78 | } 79 | } 80 | 81 | for (final PropertyMetaModel subListMetaModel : subLists) { 82 | final NodeList subList = (NodeList) subListMetaModel.getValue(node); 83 | if (subList != null && !subList.isEmpty()) { 84 | final List listContent = new ArrayList<>(); 85 | for (final Node subListNode : subList) { 86 | listContent.add(this.output(subListNode, null, level + 1)); 87 | } 88 | content.add(listContent.stream().collect(Collectors.joining(",", CustomJsonPrinter.q(subListMetaModel.getName()) + ":[", "]"))); 89 | } 90 | } 91 | 92 | if (name == null) { 93 | return content.stream().collect(Collectors.joining(",", "{", "}")); 94 | } 95 | return content.stream().collect(Collectors.joining(",", CustomJsonPrinter.q(name) + ":{", "}")); 96 | } 97 | 98 | 99 | @Override 100 | public String toString() { 101 | return "CustomJsonPrinter{" + 102 | "outputNodeType=" + this.outputNodeType + 103 | '}'; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CypherPrinter.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 2 | 3 | import com.github.javaparser.ast.Node; 4 | import com.github.javaparser.ast.NodeList; 5 | import com.github.javaparser.metamodel.NodeMetaModel; 6 | import com.github.javaparser.metamodel.PropertyMetaModel; 7 | import com.github.javaparser.utils.LineSeparator; 8 | 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import static com.github.javaparser.utils.Utils.assertNotNull; 14 | import static java.util.stream.Collectors.toList; 15 | 16 | public class CypherPrinter implements NodePrinter { 17 | 18 | 19 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000; 20 | 21 | private static final String EOL = LineSeparator.SYSTEM.asRawString(); 22 | 23 | private final Set currentIds; 24 | private final boolean outputNodeType; 25 | private int nodeCount; 26 | 27 | 28 | public CypherPrinter(boolean outputNodeType) { 29 | this.outputNodeType = outputNodeType; 30 | this.currentIds = new HashSet<>(); 31 | } 32 | 33 | 34 | private static String escapeQuotes(String value) { 35 | return value.replace("'", "\\'"); 36 | } 37 | 38 | 39 | private String nextNodeName() { 40 | return "n" + (this.nodeCount++); 41 | } 42 | 43 | 44 | public void output(Node node, String parentNodeName, String name, StringBuilder builder) { 45 | assertNotNull(node); 46 | 47 | 48 | NodeMetaModel metaModel = node.getMetaModel(); 49 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 50 | 51 | List attributes = allPropertyMetaModels 52 | .stream() 53 | .filter(PropertyMetaModel::isAttribute) 54 | .filter(PropertyMetaModel::isSingular) 55 | .collect(toList()); 56 | List subNodes = allPropertyMetaModels 57 | .stream() 58 | .filter(PropertyMetaModel::isNode) 59 | .filter(PropertyMetaModel::isSingular) 60 | .collect(toList()); 61 | List subLists = allPropertyMetaModels 62 | .stream() 63 | .filter(PropertyMetaModel::isNodeList) 64 | .collect(toList()); 65 | 66 | String ndName = this.nextNodeName(); 67 | this.currentIds.add(ndName); 68 | 69 | builder.append(EOL) 70 | .append("WITH ").append(String.join(", ", this.currentIds)).append(EOL); 71 | 72 | 73 | builder.append("MERGE(").append(ndName).append(":Node:").append(metaModel.getTypeName()).append(" {"); 74 | 75 | if (this.outputNodeType) { 76 | builder.append(EOL) 77 | .append(" type: '").append(metaModel.getTypeName()).append("'"); 78 | } 79 | 80 | // builder.append( 81 | // EOL + " nodeName: '" + ndName + "'," + 82 | // EOL + " parentName: '" + parentNodeName + "'" + 83 | // ""); 84 | 85 | builder.append(",").append(EOL) 86 | .append(" ").append("name").append(": '").append(escapeQuotes(name)).append("'"); 87 | 88 | for (PropertyMetaModel a : attributes) { 89 | String x = "," + EOL + " " + escapeQuotes(a.getName()) + ": '" + escapeQuotes(a.getValue(node).toString()) + "'"; 90 | builder.append(x); 91 | } 92 | 93 | builder.append(EOL) 94 | .append("})"); 95 | 96 | // Do relationships 97 | if (parentNodeName != null) { 98 | builder.append(EOL) 99 | .append("MERGE (").append(parentNodeName).append(")<-[:PARENT]-(").append(ndName).append(")"); 100 | } 101 | builder.append(EOL); 102 | builder.append(EOL); 103 | 104 | 105 | for (PropertyMetaModel sn : subNodes) { 106 | Node nd = (Node) sn.getValue(node); 107 | if (nd != null) { 108 | this.output(nd, ndName, sn.getName(), builder); 109 | } 110 | } 111 | 112 | for (PropertyMetaModel sl : subLists) { 113 | NodeList nl = (NodeList) sl.getValue(node); 114 | if (nl != null && nl.isNonEmpty()) { 115 | String slName = sl.getName().substring(0, sl.getName().length() - 1); 116 | for (Node nd : nl) { 117 | this.output(nd, ndName, slName, builder); 118 | } 119 | } 120 | } 121 | 122 | this.currentIds.remove(name); 123 | } 124 | 125 | 126 | @Override 127 | public String output(Node node) { 128 | this.nodeCount = 0; 129 | StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 130 | this.output(node, null, "root", output); 131 | return output.toString(); 132 | } 133 | 134 | 135 | public void output2(Node node, String parentNodeName, String name, StringBuilder builder) { 136 | assertNotNull(node); 137 | 138 | NodeMetaModel metaModel = node.getMetaModel(); 139 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 140 | 141 | List attributes = allPropertyMetaModels 142 | .stream() 143 | .filter(PropertyMetaModel::isAttribute) 144 | .filter(PropertyMetaModel::isSingular) 145 | .collect(toList()); 146 | List subNodes = allPropertyMetaModels 147 | .stream() 148 | .filter(PropertyMetaModel::isNode) 149 | .filter(PropertyMetaModel::isSingular) 150 | .collect(toList()); 151 | List subLists = allPropertyMetaModels 152 | .stream() 153 | .filter(PropertyMetaModel::isNodeList) 154 | .collect(toList()); 155 | 156 | String ndName = this.nextNodeName(); 157 | if (this.outputNodeType) { 158 | builder.append(EOL) 159 | .append(ndName).append(" [label=\"").append(escapeQuotes(name)).append(" (").append(metaModel.getTypeName()).append(")\"];"); 160 | } else { 161 | builder.append(EOL) 162 | .append(ndName).append(" [label=\"").append(escapeQuotes(name)).append("\"];"); 163 | } 164 | 165 | if (parentNodeName != null) { 166 | builder.append(EOL) 167 | .append(parentNodeName).append(" -> ").append(ndName).append(";"); 168 | } 169 | 170 | for (PropertyMetaModel a : attributes) { 171 | String attrName = this.nextNodeName(); 172 | builder.append(EOL) 173 | .append(attrName).append(" [label=\"").append(escapeQuotes(a.getName())).append("='").append(escapeQuotes(a.getValue(node).toString())).append("'\"];"); 174 | builder.append(EOL) 175 | .append(ndName).append(" -> ").append(attrName).append(";"); 176 | 177 | } 178 | 179 | for (PropertyMetaModel sn : subNodes) { 180 | Node nd = (Node) sn.getValue(node); 181 | if (nd != null) { 182 | this.output(nd, ndName, sn.getName(), builder); 183 | } 184 | } 185 | 186 | for (PropertyMetaModel sl : subLists) { 187 | NodeList nl = (NodeList) sl.getValue(node); 188 | if (nl != null && nl.isNonEmpty()) { 189 | String ndLstName = this.nextNodeName(); 190 | builder.append(EOL) 191 | .append(ndLstName).append(" [label=\"").append(escapeQuotes(sl.getName())).append("\"];"); 192 | builder.append(EOL) 193 | .append(ndName).append(" -> ").append(ndLstName).append(";"); 194 | String slName = sl.getName().substring(0, sl.getName().length() - 1); 195 | for (Node nd : nl) { 196 | this.output(nd, ndLstName, slName, builder); 197 | } 198 | } 199 | } 200 | } 201 | 202 | 203 | @Override 204 | public String toString() { 205 | return "CypherPrinter{" + 206 | "currentIds=" + this.currentIds + 207 | ", outputNodeType=" + this.outputNodeType + 208 | ", nodeCount=" + this.nodeCount + 209 | '}'; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/GraphMLPrinter.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 2 | 3 | import com.github.javaparser.ast.Node; 4 | import com.github.javaparser.ast.NodeList; 5 | import com.github.javaparser.metamodel.NodeMetaModel; 6 | import com.github.javaparser.metamodel.PropertyMetaModel; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Set; 11 | import java.util.TreeSet; 12 | 13 | import static com.github.javaparser.utils.Utils.assertNotNull; 14 | import static java.util.stream.Collectors.toList; 15 | 16 | /** 17 | * Outputs an GraphML file containing the AST for import into a graph database. 18 | */ 19 | public class GraphMLPrinter implements NodePrinter { 20 | 21 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000; 22 | 23 | private static final String NEWLINE = String.format("%n"); 24 | 25 | private static final String DATA_INDENT = " "; 26 | private static final String EDGE_INDENT = " "; 27 | private static final String GRAPH_INDENT = " "; 28 | private static final String KEY_INDENT = " "; 29 | private static final String NODE_INDENT = " "; 30 | 31 | private final Set edgeKeys; 32 | private final List edges; 33 | private final Set nodeKeys; 34 | private final List nodes; 35 | private final boolean outputNodeType; 36 | 37 | private int edgeCount; 38 | private int nodeCount; 39 | 40 | 41 | public GraphMLPrinter(boolean outputNodeType) { 42 | this.edgeCount = 0; 43 | this.nodeCount = 0; 44 | this.outputNodeType = outputNodeType; 45 | this.nodeKeys = new TreeSet<>(); 46 | this.edgeKeys = new TreeSet<>(); 47 | this.nodes = new ArrayList<>(); 48 | this.edges = new ArrayList<>(); 49 | } 50 | 51 | 52 | private String attribute(String name, String value) { 53 | return " " + name + "=\"" + value + "\""; 54 | } 55 | 56 | 57 | private String dataEntry(String key, String value) { 58 | String escapedValue = value 59 | .replaceAll("<", "<") 60 | .replaceAll(">", ">"); 61 | 62 | return "" + 65 | escapedValue + 66 | ""; 67 | } 68 | 69 | 70 | private String keyEntry(String name, String elemType, String type) { 71 | return ""; 77 | } 78 | 79 | 80 | private String nextEdgeName() { 81 | return "e" + (this.edgeCount++); 82 | } 83 | 84 | 85 | private String nextNodeName() { 86 | return "n" + (this.nodeCount++); 87 | } 88 | 89 | 90 | private void output(Node node, String name, int level, String parentNdName) { 91 | assertNotNull(node); 92 | NodeMetaModel metaModel = node.getMetaModel(); 93 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 94 | List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList()); 95 | List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList()); 96 | List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList()); 97 | 98 | String ndName = this.nextNodeName(); 99 | StringBuilder nodeBuilder = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 100 | String typeName = metaModel.getTypeName(); 101 | 102 | this.nodeKeys.add("id"); 103 | this.nodeKeys.add("labels"); 104 | nodeBuilder 105 | .append(NODE_INDENT).append(""); 109 | 110 | if (this.outputNodeType) { 111 | this.nodeKeys.add("type"); 112 | nodeBuilder.append(NEWLINE).append(DATA_INDENT).append(this.dataEntry("type", typeName)); 113 | } 114 | 115 | for (PropertyMetaModel attributeMetaModel : attributes) { 116 | String attributeName = attributeMetaModel.getName(); 117 | String value = attributeMetaModel.getValue(node).toString(); 118 | this.nodeKeys.add(attributeName); 119 | nodeBuilder.append(NEWLINE).append(DATA_INDENT).append(this.dataEntry(attributeName, value)); 120 | } 121 | 122 | nodeBuilder.append(NEWLINE).append(NODE_INDENT).append(""); 123 | this.nodes.add(nodeBuilder.toString()); 124 | 125 | if (parentNdName != null) { 126 | String edgeName = this.nextEdgeName(); 127 | String edgeLabel = "PARENT"; 128 | 129 | this.edgeKeys.add("id"); 130 | this.edgeKeys.add("source"); 131 | this.edgeKeys.add("target"); 132 | this.edgeKeys.add("label"); 133 | 134 | String edge = ""; 135 | edge += EDGE_INDENT; 136 | edge += ""; 142 | edge += NEWLINE + DATA_INDENT + this.dataEntry(edgeLabel, edgeLabel); 143 | edge += NEWLINE + EDGE_INDENT + ""; 144 | 145 | this.edges.add(edge); 146 | } 147 | 148 | for (PropertyMetaModel subNodeMetaModel : subNodes) { 149 | Node value = (Node) subNodeMetaModel.getValue(node); 150 | if (value != null) { 151 | this.output(value, subNodeMetaModel.getName(), level + 1, ndName); 152 | } 153 | } 154 | // 155 | for (PropertyMetaModel subListMetaModel : subLists) { 156 | NodeList subList = (NodeList) subListMetaModel.getValue(node); 157 | if (subList != null && !subList.isEmpty()) { 158 | String listName = subListMetaModel.getName(); 159 | String singular = listName.substring(0, listName.length() - 1); 160 | for (Node subListNode : subList) { 161 | this.output(subListNode, singular, level + 1, ndName); 162 | } 163 | } 164 | } 165 | } 166 | 167 | 168 | @Override 169 | public String output(Node node) { 170 | StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY); 171 | output.append("").append(NEWLINE) 172 | .append("").append(NEWLINE); 175 | 176 | this.output(node, "root", 0, null); 177 | 178 | this.nodeKeys.forEach(s -> { 179 | output.append(NEWLINE).append(KEY_INDENT).append(this.keyEntry(s, "node", "string")); 180 | }); 181 | this.edgeKeys.forEach(s -> { 182 | output.append(NEWLINE).append(KEY_INDENT).append(this.keyEntry(s, "edge", "string")); 183 | }); 184 | 185 | output.append(NEWLINE).append(GRAPH_INDENT).append(""); 186 | this.nodes.forEach(s -> { 187 | output.append(NEWLINE).append(s); 188 | }); 189 | 190 | this.edges.forEach(s -> { 191 | output.append(NEWLINE).append(s); 192 | }); 193 | 194 | output.append(NEWLINE).append(GRAPH_INDENT).append(""); 195 | output.append(NEWLINE).append(""); 196 | 197 | return output.toString(); 198 | } 199 | 200 | 201 | @Override 202 | public String toString() { 203 | return "GraphMLPrinter{" + 204 | "nodes=" + this.nodes + 205 | ", outputNodeType=" + this.outputNodeType + 206 | ", edgeCount=" + this.edgeCount + 207 | ", nodeCount=" + this.nodeCount + 208 | '}'; 209 | } 210 | } 211 | 212 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/NodePrinter.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers; 2 | 3 | import com.github.javaparser.ast.Node; 4 | 5 | public interface NodePrinter { 6 | 7 | 8 | /** 9 | * @param node The node to be printed - typically a CompilationUnit. 10 | * @return The formatted equivalent of node. 11 | */ 12 | String output(Node node); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/HighlightingService.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services; 2 | 3 | import com.github.javaparser.ast.Node; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.psi.PsiFile; 7 | 8 | import java.util.Optional; 9 | 10 | public interface HighlightingService { 11 | static HighlightingService getInstance() { 12 | return ServiceManager.getService(HighlightingService.class); 13 | } 14 | 15 | Optional getSelectedNode(); 16 | 17 | void setSelectedNode(Node node); 18 | 19 | void updateHighlight(PsiFile psiFile, Editor editor); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/JavaParserService.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services; 2 | 3 | import com.github.javaparser.JavaParser; 4 | import com.github.javaparser.ParseResult; 5 | import com.github.javaparser.ParserConfiguration; 6 | import com.github.javaparser.Provider; 7 | import com.github.javaparser.ast.CompilationUnit; 8 | import com.github.javaparser.utils.SourceRoot; 9 | import com.intellij.openapi.components.ServiceManager; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.vfs.VirtualFile; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.List; 15 | 16 | public interface JavaParserService { 17 | 18 | static JavaParserService getInstance(@NotNull Project project) { 19 | return ServiceManager.getService(project, JavaParserService.class); 20 | } 21 | 22 | 23 | ParserConfiguration getConfiguration(); 24 | 25 | ParserConfiguration getDefaultConfiguration(); 26 | 27 | JavaParser getJavaParserInstance(); 28 | 29 | ParseResult parseCu(Provider provider); 30 | 31 | List vFilesToSourceRoots(VirtualFile[] vFiles); 32 | 33 | String vFilesToSourceRoots(VirtualFile[] vFiles, String delimiter); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/PrinterService.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services; 2 | 3 | import com.github.javaparser.ast.CompilationUnit; 4 | import com.github.javaparser.ast.Node; 5 | import com.intellij.openapi.components.ServiceManager; 6 | import com.intellij.openapi.project.Project; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public interface PrinterService { 10 | 11 | static PrinterService getInstance(@NotNull Project project) { 12 | return ServiceManager.getService(project, PrinterService.class); 13 | } 14 | 15 | 16 | String asAsciiTreeText(Node node); 17 | 18 | String asAsciiTreeText(Node node, boolean outputNodeType); 19 | 20 | String asCypher(Node node); 21 | 22 | String asCypher(Node node, boolean outputNodeType); 23 | 24 | 25 | String asDot(Node node); 26 | 27 | String asDot(Node node, boolean outputNodeType); 28 | 29 | 30 | String asDotCustom(Node node); 31 | 32 | String asDotCustom(Node node, boolean outputNodeType); 33 | 34 | 35 | String asGraphMl(Node node); 36 | 37 | String asGraphMl(Node node, boolean outputNodeType); 38 | 39 | 40 | String asJavaPrettyPrint(Node node); 41 | 42 | 43 | String asJsonCustom(Node node); 44 | 45 | String asJsonCustom(Node node, boolean outputNodeType); 46 | 47 | 48 | String asXml(Node node, boolean outputNodeType); 49 | 50 | String asXml(Node node); 51 | 52 | 53 | String asYaml(Node node, boolean outputNodeType); 54 | 55 | String asYaml(Node node); 56 | 57 | 58 | default String outputAs(String outputFormat, CompilationUnit compilationUnit) { 59 | return this.outputAs(outputFormat, compilationUnit, false); 60 | } 61 | 62 | String outputAs(String outputFormat, CompilationUnit compilationUnit, boolean includeNodeType); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/HighlightingServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl; 2 | 3 | import com.github.javaparser.Range; 4 | import com.github.javaparser.ast.Node; 5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 6 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.HighlightingService; 7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.EditorUtil; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.openapi.editor.markup.EffectType; 10 | import com.intellij.openapi.editor.markup.HighlighterLayer; 11 | import com.intellij.openapi.editor.markup.HighlighterTargetArea; 12 | import com.intellij.openapi.editor.markup.MarkupModel; 13 | import com.intellij.openapi.editor.markup.RangeHighlighter; 14 | import com.intellij.openapi.editor.markup.TextAttributes; 15 | import com.intellij.openapi.util.TextRange; 16 | import com.intellij.psi.PsiFile; 17 | import com.intellij.ui.JBColor; 18 | 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.Optional; 24 | 25 | public class HighlightingServiceImpl implements HighlightingService { 26 | 27 | private static final int HIGHLIGHT_LAYER = HighlighterLayer.ERROR + 200; 28 | 29 | private static final NotificationLogger notificationLogger = new NotificationLogger(HighlightingServiceImpl.class); 30 | 31 | private final Map highlighters; 32 | 33 | private final TextAttributes taGreen; 34 | private final TextAttributes taOrange; 35 | private final TextAttributes taSelectedNodeInEditor; 36 | private final TextAttributes taYellow; 37 | 38 | private Node selectedNode = null; 39 | 40 | 41 | public HighlightingServiceImpl() { 42 | 43 | this.highlighters = new HashMap<>(); 44 | 45 | // Setup colours 46 | this.taSelectedNodeInEditor = new TextAttributes(); 47 | this.taSelectedNodeInEditor.setBackgroundColor(JBColor.YELLOW); 48 | this.taSelectedNodeInEditor.withAdditionalEffect(EffectType.BOXED, JBColor.RED); 49 | 50 | this.taYellow = new TextAttributes(); 51 | this.taYellow.setBackgroundColor(JBColor.YELLOW); 52 | this.taYellow.withAdditionalEffect(EffectType.BOXED, JBColor.RED); 53 | 54 | this.taOrange = new TextAttributes(); 55 | this.taOrange.setBackgroundColor(JBColor.ORANGE); 56 | this.taOrange.withAdditionalEffect(EffectType.BOXED, JBColor.RED); 57 | 58 | this.taGreen = new TextAttributes(); 59 | this.taGreen.setBackgroundColor(JBColor.GREEN); 60 | this.taGreen.withAdditionalEffect(EffectType.BOXED, JBColor.RED); 61 | 62 | } 63 | 64 | 65 | /** 66 | * Which character indicates the the last character of the line, at which point the line number increments? 67 | * 68 | * Examples: 69 | *
    70 | *
  • {@code \r} (CR) - Characters after the {@code \r} are considered to be on the next line.
  • 71 | *
  • {@code \n} (LF) - Characters after the {@code \n} are considered to be on the next line.
  • 72 | *
  • {@code \r\n} (CRLF) - Characters after the {@code \n} are considered to be on the next line.
  • 73 | *
74 | */ 75 | public static char lastLineSeparator(String lineSeparatorString) { 76 | char defaultSeparator = '\n'; 77 | char lineSeparatorChar; 78 | 79 | if (lineSeparatorString == null) { 80 | lineSeparatorChar = defaultSeparator; 81 | } else if (lineSeparatorString.length() == 1) { 82 | lineSeparatorChar = lineSeparatorString.toCharArray()[0]; 83 | } else if (lineSeparatorString.length() == 2) { 84 | lineSeparatorChar = lineSeparatorString.toCharArray()[1]; 85 | } else { 86 | lineSeparatorChar = defaultSeparator; 87 | } 88 | 89 | return lineSeparatorChar; 90 | } 91 | 92 | 93 | @Override 94 | public Optional getSelectedNode() { 95 | return Optional.ofNullable(this.selectedNode); 96 | } 97 | 98 | 99 | @Override 100 | public void setSelectedNode(Node node) { 101 | this.selectedNode = node; 102 | } 103 | 104 | 105 | @Override 106 | public void updateHighlight(PsiFile psiFile, Editor editor) { 107 | notificationLogger.traceEnter(); 108 | 109 | if (this.selectedNode != null) { 110 | if (this.selectedNode.getRange().isPresent()) { 111 | final Range range = this.selectedNode.getRange().get(); 112 | 113 | final MarkupModel markupModel = editor.getMarkupModel(); 114 | 115 | // Remove all current highlighters for this editor 116 | if (this.highlighters.containsKey(editor)) { 117 | RangeHighlighter highlighter = this.highlighters.get(editor); 118 | 119 | List allHighlighters = Arrays.asList(markupModel.getAllHighlighters()); 120 | if (allHighlighters.contains(highlighter)) { 121 | markupModel.removeHighlighter(highlighter); 122 | } 123 | } 124 | 125 | // Create a new highlighter. 126 | TextRange textRange = EditorUtil.javaParserRangeToIntellijOffsetRange(editor, range); 127 | final RangeHighlighter newHighlighter = markupModel.addRangeHighlighter( 128 | textRange.getStartOffset(), 129 | textRange.getEndOffset(), 130 | HIGHLIGHT_LAYER, 131 | this.taSelectedNodeInEditor, 132 | HighlighterTargetArea.EXACT_RANGE 133 | ); 134 | 135 | // Add to per-editor cache of highlighters. 136 | this.highlighters.put(editor, newHighlighter); 137 | 138 | // Scroll to the start of the highlighted range. 139 | EditorUtil.scrollToPosition(editor, newHighlighter.getStartOffset()); 140 | 141 | } else { 142 | notificationLogger.warn("Selected node does not have a range, thus unable to update highlighting."); 143 | } 144 | } 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/JavaParserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl; 2 | 3 | import com.github.javaparser.JavaParser; 4 | import com.github.javaparser.ParseResult; 5 | import com.github.javaparser.ParseStart; 6 | import com.github.javaparser.ParserConfiguration; 7 | import com.github.javaparser.Provider; 8 | import com.github.javaparser.ast.CompilationUnit; 9 | import com.github.javaparser.utils.SourceRoot; 10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.JavaParserService; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.vfs.VirtualFile; 13 | 14 | import java.nio.file.Paths; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | 19 | public class JavaParserServiceImpl implements JavaParserService { 20 | 21 | private final ParserConfiguration configuration; 22 | private final JavaParser javaParser; 23 | private final Project project; 24 | 25 | 26 | public JavaParserServiceImpl(Project project) { 27 | this.project = project; 28 | this.configuration = this.getDefaultConfiguration(); 29 | this.javaParser = new JavaParser(this.configuration); 30 | } 31 | 32 | 33 | @Override 34 | public ParserConfiguration getConfiguration() { 35 | return this.configuration; 36 | } 37 | 38 | 39 | @Override 40 | public ParserConfiguration getDefaultConfiguration() { 41 | return new ParserConfiguration(); 42 | } 43 | 44 | 45 | @Override 46 | public JavaParser getJavaParserInstance() { 47 | return this.javaParser; 48 | } 49 | 50 | 51 | @Override 52 | public ParseResult parseCu(Provider provider) { 53 | return this.javaParser.parse(ParseStart.COMPILATION_UNIT, provider); 54 | } 55 | 56 | 57 | @Override 58 | public List vFilesToSourceRoots(VirtualFile[] vFiles) { 59 | return Arrays.stream(vFiles) 60 | .map(VirtualFile::getPath) 61 | .map(Paths::get) 62 | .map(SourceRoot::new) 63 | .collect(Collectors.toList()); 64 | } 65 | 66 | 67 | @Override 68 | public String vFilesToSourceRoots(VirtualFile[] vFiles, String delimiter) { 69 | return this.vFilesToSourceRoots(vFiles).stream() 70 | .map(sourceRoot -> sourceRoot.getRoot().toString()) 71 | .collect(Collectors.joining(delimiter)); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/PrinterServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl; 2 | 3 | import com.github.javaparser.ast.CompilationUnit; 4 | import com.github.javaparser.ast.Node; 5 | import com.github.javaparser.printer.DotPrinter; 6 | import com.github.javaparser.printer.XmlPrinter; 7 | import com.github.javaparser.printer.YamlPrinter; 8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 9 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.ASCIITreePrinter; 10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CustomDotPrinter; 11 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CustomJsonPrinter; 12 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CypherPrinter; 13 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.GraphMLPrinter; 14 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.PrinterService; 15 | import com.intellij.openapi.project.Project; 16 | 17 | public class PrinterServiceImpl implements PrinterService { 18 | 19 | private static final boolean defaultOutputNodeType = true; 20 | 21 | private static final NotificationLogger notificationLogger = new NotificationLogger(PrinterServiceImpl.class); 22 | private final Project project; 23 | 24 | 25 | public PrinterServiceImpl(Project project) { 26 | this.project = project; 27 | } 28 | 29 | 30 | @Override 31 | public String asAsciiTreeText(Node node) { 32 | return this.asAsciiTreeText(node, defaultOutputNodeType); 33 | } 34 | 35 | 36 | @Override 37 | public String asAsciiTreeText(Node node, boolean outputNodeType) { 38 | ASCIITreePrinter printer = new ASCIITreePrinter(); 39 | return printer.output(node); 40 | } 41 | 42 | 43 | @Override 44 | public String asCypher(Node node) { 45 | return this.asCypher(node, defaultOutputNodeType); 46 | } 47 | 48 | 49 | @Override 50 | public String asCypher(Node node, boolean outputNodeType) { 51 | CypherPrinter printer = new CypherPrinter(outputNodeType); 52 | return printer.output(node); 53 | } 54 | 55 | 56 | @Override 57 | public String asDot(Node node) { 58 | return this.asDot(node, defaultOutputNodeType); 59 | } 60 | 61 | 62 | @Override 63 | public String asDot(Node node, boolean outputNodeType) { 64 | DotPrinter printer = new DotPrinter(outputNodeType); 65 | return printer.output(node); 66 | } 67 | 68 | 69 | @Override 70 | public String asDotCustom(Node node) { 71 | return this.asDotCustom(node, defaultOutputNodeType); 72 | } 73 | 74 | 75 | @Override 76 | public String asDotCustom(Node node, boolean outputNodeType) { 77 | CustomDotPrinter printer = new CustomDotPrinter(outputNodeType); 78 | return printer.output(node); 79 | } 80 | 81 | 82 | @Override 83 | public String asGraphMl(Node node) { 84 | return this.asGraphMl(node, defaultOutputNodeType); 85 | } 86 | 87 | 88 | @Override 89 | public String asGraphMl(Node node, boolean outputNodeType) { 90 | GraphMLPrinter printer = new GraphMLPrinter(outputNodeType); 91 | return printer.output(node); 92 | } 93 | 94 | 95 | @Override 96 | public String asJavaPrettyPrint(Node node) { 97 | return node.toString(); 98 | } 99 | 100 | 101 | @Override 102 | public String asJsonCustom(Node node) { 103 | return this.asJsonCustom(node, defaultOutputNodeType); 104 | } 105 | 106 | 107 | @Override 108 | public String asJsonCustom(Node node, boolean outputNodeType) { 109 | CustomJsonPrinter printer = new CustomJsonPrinter(outputNodeType); 110 | return printer.output(node); 111 | } 112 | 113 | 114 | @Override 115 | public String asXml(Node node, boolean outputNodeType) { 116 | XmlPrinter printer = new XmlPrinter(outputNodeType); 117 | return printer.output(node); 118 | } 119 | 120 | 121 | @Override 122 | public String asXml(Node node) { 123 | return this.asXml(node, defaultOutputNodeType); 124 | } 125 | 126 | 127 | @Override 128 | public String asYaml(Node node, boolean outputNodeType) { 129 | YamlPrinter printer = new YamlPrinter(outputNodeType); 130 | return printer.output(node); 131 | } 132 | 133 | 134 | @Override 135 | public String asYaml(Node node) { 136 | return this.asYaml(node, defaultOutputNodeType); 137 | } 138 | 139 | 140 | @Override 141 | public String outputAs(final String outputFormat, final CompilationUnit compilationUnit, boolean includeNodeType) { 142 | 143 | String output = null; 144 | 145 | if ("YAML".equals(outputFormat)) { 146 | output = this.asYaml(compilationUnit, includeNodeType); 147 | } else if ("XML".equals(outputFormat)) { 148 | output = this.asXml(compilationUnit, includeNodeType); 149 | } else if ("DOT".equals(outputFormat)) { 150 | output = this.asDot(compilationUnit, includeNodeType); 151 | // } else if ("Java (lexically preserving)".equals(outputFormat)) { 152 | // notificationLogger.info("Note that the lexically preserving printer does not use the setting 'include node type'. "); 153 | // output = this.asJavaPrettyPrint(compilationUnit); 154 | } else if ("Java (pretty print)".equals(outputFormat)) { 155 | notificationLogger.info("Note that the pretty printer does not use the setting 'include node type'. "); 156 | output = this.asJavaPrettyPrint(compilationUnit); 157 | } else if ("ASCII Tree".equals(outputFormat)) { 158 | output = this.asAsciiTreeText(compilationUnit, includeNodeType); 159 | } else if ("Custom DOT".equals(outputFormat)) { 160 | output = this.asDotCustom(compilationUnit, includeNodeType); 161 | } else if ("Custom DOT Image".equals(outputFormat)) { 162 | output = this.asDotCustom(compilationUnit, includeNodeType); 163 | } else if ("Custom JSON".equals(outputFormat)) { 164 | output = this.asJsonCustom(compilationUnit, includeNodeType); 165 | } else if ("Cypher".equals(outputFormat)) { 166 | output = this.asCypher(compilationUnit, includeNodeType); 167 | } else if ("GraphML".equals(outputFormat)) { 168 | output = this.asGraphMl(compilationUnit, includeNodeType); 169 | } else { 170 | notificationLogger.error("Unrecognised output format: " + outputFormat); 171 | } 172 | 173 | return output; 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/notifications/NotificationsNotifier.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.notifications; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.openapi.project.Project; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public interface NotificationsNotifier { 9 | 10 | @NotNull 11 | default Notification notify(@NotNull final String content) { 12 | return this.notify(null, content); 13 | } 14 | 15 | @NotNull 16 | Notification notify(@Nullable Project project, @NotNull String content); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/NodeDetailsTextPane.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components; 2 | 3 | import com.github.javaparser.ast.Node; 4 | import com.github.javaparser.ast.NodeList; 5 | import com.github.javaparser.ast.expr.ObjectCreationExpr; 6 | import com.github.javaparser.metamodel.NodeMetaModel; 7 | import com.github.javaparser.metamodel.PropertyMetaModel; 8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.ASCIITreePrinter; 9 | import com.intellij.ui.JBColor; 10 | 11 | import javax.swing.*; 12 | import javax.swing.text.BadLocationException; 13 | import javax.swing.text.SimpleAttributeSet; 14 | import javax.swing.text.StyleConstants; 15 | import javax.swing.text.StyledDocument; 16 | import java.util.List; 17 | 18 | import static java.util.stream.Collectors.toList; 19 | 20 | public class NodeDetailsTextPane extends JTextPane { 21 | 22 | private static final String EOL = System.lineSeparator(); 23 | private static final String H_LINE = "----------------------------------------"; 24 | 25 | private SimpleAttributeSet styleBoldBlue; 26 | private SimpleAttributeSet styleHighAlert; 27 | private SimpleAttributeSet styleNormal; 28 | 29 | 30 | public NodeDetailsTextPane() { 31 | super(); 32 | this.setupStyles(); 33 | } 34 | 35 | 36 | public NodeDetailsTextPane(StyledDocument doc) { 37 | super(doc); 38 | this.setupStyles(); 39 | } 40 | 41 | 42 | public void addLineSeparator() { 43 | this.appendLine(H_LINE, this.styleBoldBlue); 44 | } 45 | 46 | 47 | public void appendHeading(String s) { 48 | this.appendString(s + EOL, this.styleBoldBlue); 49 | } 50 | 51 | 52 | public void appendLine(String s) { 53 | this.appendString(s + EOL, this.styleNormal); 54 | } 55 | 56 | 57 | public void appendLine(String s, SimpleAttributeSet style) { 58 | this.appendString(s + EOL, style); 59 | } 60 | 61 | 62 | public void appendString(String s) { 63 | this.appendString(s, this.styleNormal); 64 | } 65 | 66 | 67 | public void appendString(String s, SimpleAttributeSet style) { 68 | try { 69 | final StyledDocument doc = this.getStyledDocument(); 70 | doc.insertString(doc.getLength(), s, style); 71 | } catch (BadLocationException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | 76 | 77 | public void clear() { 78 | this.setText(""); 79 | } 80 | 81 | 82 | @Override 83 | public StyledDocument getStyledDocument() { 84 | return (StyledDocument) this.getDocument(); 85 | } 86 | 87 | 88 | public void logNodeToTextPane(Node selectedNode) { 89 | 90 | // Update the side panel 91 | final NodeMetaModel metaModel = selectedNode.getMetaModel(); 92 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels(); 93 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList()); 94 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList()); 95 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList()); 96 | 97 | 98 | this.appendHeading("DETAILS "); 99 | 100 | this.addLineSeparator(); 101 | this.appendLine(" - TYPE: " + metaModel.getTypeName()); 102 | this.appendString(" - RANGE: "); 103 | if (selectedNode.getRange().isPresent()) { 104 | this.appendLine(selectedNode.getRange().get().toString()); 105 | } else { 106 | this.appendLine("[NOT PRESENT]"); 107 | } 108 | this.appendLine(" - NODE SUMMARY: " + ASCIITreePrinter.printNodeSummary(selectedNode)); 109 | 110 | 111 | // Object creation 112 | if ("ObjectCreationExpr".equals(selectedNode.getClass().getSimpleName())) { 113 | this.appendHeading(""); 114 | this.appendHeading(""); 115 | this.appendHeading("ObjectCreationExpr"); 116 | this.addLineSeparator(); 117 | 118 | final ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr) selectedNode; 119 | this.appendLine(" - _typeNameString:" + objectCreationExpr.getType().getName().asString()); 120 | } 121 | 122 | 123 | this.appendLine(""); 124 | this.appendLine(""); 125 | this.appendHeading("ATTRIBUTES "); 126 | this.addLineSeparator(); 127 | for (final PropertyMetaModel attributeMetaModel : attributes) { 128 | this.appendLine(" - " + attributeMetaModel.getName() + ":" + attributeMetaModel.getValue(selectedNode).toString()); 129 | } 130 | 131 | 132 | this.appendLine(""); 133 | this.appendLine(""); 134 | this.appendHeading("SubNode Meta Model" + " (count: " + subNodes.size() + ")"); 135 | this.addLineSeparator(); 136 | for (final PropertyMetaModel subNodeMetaModel : subNodes) { 137 | final Node value = (Node) subNodeMetaModel.getValue(selectedNode); 138 | if (value != null) { 139 | this.appendLine(" - " + subNodeMetaModel.getName() + ": " + value); 140 | } 141 | } 142 | 143 | this.appendLine(""); 144 | this.appendLine(""); 145 | this.appendHeading("SubList Meta Model" + " (count: " + subLists.size() + ")"); 146 | this.addLineSeparator(); 147 | for (int index_allSublists = 0; index_allSublists < subLists.size(); index_allSublists++) { 148 | final PropertyMetaModel subListMetaModel = subLists.get(index_allSublists); 149 | final NodeList subList = (NodeList) subListMetaModel.getValue(selectedNode); 150 | if (subList != null && !subList.isEmpty()) { 151 | this.appendLine(subListMetaModel.getName() + " (count: " + subList.size() + ")"); 152 | for (int index_sublist = 0; index_sublist < subList.size(); index_sublist++) { 153 | Node subListNode = subList.get(index_sublist); 154 | this.appendLine(index_sublist + ": " + ASCIITreePrinter.CLASS_RANGE_SUMMARY_FORMAT.apply(subListNode)); 155 | } 156 | } 157 | if (index_allSublists < (subLists.size() - 1)) { 158 | this.appendLine(""); 159 | } 160 | } 161 | } 162 | 163 | 164 | private void setupStyles() { 165 | // Setup styles 166 | this.styleNormal = new SimpleAttributeSet(); 167 | StyleConstants.setFontFamily(this.styleNormal, "Monospaced"); 168 | 169 | this.styleBoldBlue = new SimpleAttributeSet(this.styleNormal); 170 | StyleConstants.setBold(this.styleBoldBlue, true); 171 | StyleConstants.setForeground(this.styleBoldBlue, JBColor.BLUE); 172 | 173 | this.styleHighAlert = new SimpleAttributeSet(this.styleBoldBlue); 174 | StyleConstants.setItalic(this.styleHighAlert, true); 175 | StyleConstants.setForeground(this.styleHighAlert, JBColor.RED); 176 | } 177 | 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/CharacterEncodingComboBox.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel; 2 | 3 | import com.github.javaparser.Providers; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.nio.charset.Charset; 7 | 8 | public class CharacterEncodingComboBox extends CustomComboBox { 9 | 10 | public CharacterEncodingComboBox() { 11 | super(); 12 | this.setToolTipText("Which language features should be considered valid or invalid when validating the AST?"); 13 | this.setupOptions(); 14 | } 15 | 16 | 17 | @Override 18 | protected void setupOptions() { 19 | // Populate 20 | this.addItem(new CharacterEncodingComboItem("UTF-8", Providers.UTF8)); 21 | } 22 | 23 | 24 | private static class CharacterEncodingComboItem extends CustomComboItem { 25 | 26 | public CharacterEncodingComboItem(@NotNull String key, @NotNull Charset value) { 27 | super(key, value); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/CustomComboBox.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel; 2 | 3 | import com.intellij.openapi.ui.ComboBox; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | import java.util.Objects; 9 | 10 | public abstract class CustomComboBox extends ComboBox> { 11 | 12 | public static > void setSelectedValue(@NotNull JComboBox comboBox, @Nullable E value) { 13 | I item; 14 | for (int i = 0; i < comboBox.getItemCount(); i++) { 15 | item = comboBox.getItemAt(i); 16 | if (Objects.equals(item.getValue(), value)) { 17 | comboBox.setSelectedIndex(i); 18 | break; 19 | } 20 | } 21 | } 22 | 23 | 24 | @Nullable 25 | public T getSelected() { 26 | Object itemObject = this.getSelectedItem(); 27 | if (itemObject == null) { 28 | return null; 29 | } 30 | 31 | final CustomComboItem item = (CustomComboItem) itemObject; 32 | return item.getValue(); 33 | } 34 | 35 | 36 | public void setSelectedByValue(@NotNull T value) { 37 | setSelectedValue(this, value); 38 | } 39 | 40 | 41 | protected abstract void setupOptions(); 42 | 43 | 44 | protected static class CustomComboItem { 45 | 46 | @NotNull 47 | protected final String key; 48 | 49 | protected final E value; 50 | 51 | 52 | public CustomComboItem(@NotNull String key, E value) { 53 | this.key = key; 54 | this.value = value; 55 | } 56 | 57 | 58 | @NotNull 59 | public String getKey() { 60 | return this.key; 61 | } 62 | 63 | 64 | public E getValue() { 65 | return this.value; 66 | } 67 | 68 | 69 | @Override 70 | public int hashCode() { 71 | return Objects.hash(this.value); 72 | } 73 | 74 | 75 | @Override 76 | public boolean equals(final Object obj) { 77 | if (obj == this) { 78 | return true; 79 | } 80 | if (!(obj instanceof CustomComboBox.CustomComboItem)) { 81 | return false; 82 | } 83 | 84 | final CustomComboItem other = (CustomComboItem) obj; 85 | return Objects.equals(this.value, other.value); 86 | } 87 | 88 | 89 | // @Override 90 | // public String toString() { 91 | // return "CustomComboItem{" + 92 | // "key='" + key + '\'' + 93 | // ", value='" + String.valueOf(value) + '\'' + 94 | // '}'; 95 | // } 96 | 97 | 98 | /** 99 | * Note that this is used as the text on the combo item. 100 | */ 101 | @Override 102 | public String toString() { 103 | return this.key; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/ExportAsComboBox.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public class ExportAsComboBox extends CustomComboBox { 6 | 7 | public ExportAsComboBox() { 8 | super(); 9 | this.setToolTipText("Output format."); 10 | this.setupOptions(); 11 | } 12 | 13 | 14 | @Override 15 | protected void setupOptions() { 16 | // Populate 17 | this.addItem(new ExportAsComboItem("DOT", "DOT")); 18 | this.addItem(new ExportAsComboItem("XML", "XML")); 19 | // this.addItem(new ExportAsComboItem("Java (lexically preserving)", "Java (lexically preserving)")); 20 | this.addItem(new ExportAsComboItem("Java (pretty print)", "Java (pretty print)")); 21 | this.addItem(new ExportAsComboItem("ASCII Tree", "ASCII Tree")); 22 | this.addItem(new ExportAsComboItem("YAML", "YAML")); 23 | this.addItem(new ExportAsComboItem("Custom DOT", "Custom DOT")); 24 | this.addItem(new ExportAsComboItem("Custom DOT Image", "Custom DOT Image")); 25 | this.addItem(new ExportAsComboItem("Custom JSON", "Custom JSON")); 26 | this.addItem(new ExportAsComboItem("Cypher", "Cypher")); 27 | this.addItem(new ExportAsComboItem("GraphML", "GraphML")); 28 | } 29 | 30 | 31 | private static class ExportAsComboItem extends CustomComboItem { 32 | 33 | public ExportAsComboItem(@NotNull String key, @NotNull String value) { 34 | super(key, value); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/LanguageLevelComboBox.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel; 2 | 3 | import com.github.javaparser.ParserConfiguration; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class LanguageLevelComboBox extends CustomComboBox { 7 | 8 | public LanguageLevelComboBox() { 9 | super(); 10 | this.setToolTipText("Which language features should be considered valid or invalid when validating the AST?"); 11 | this.setupOptions(); 12 | } 13 | 14 | 15 | @Override 16 | protected void setupOptions() { 17 | // this.addItem(new LanguageLevelComboItem("CURRENT (" + ParserConfiguration.LanguageLevel.CURRENT.name() + ")", ParserConfiguration.LanguageLevel.CURRENT)); 18 | // this.addItem(new LanguageLevelComboItem("BLEEDING EDGE (" + ParserConfiguration.LanguageLevel.BLEEDING_EDGE.name() + ")", ParserConfiguration.LanguageLevel.BLEEDING_EDGE)); 19 | // this.addItem(new LanguageLevelComboItem("POPULAR (" + ParserConfiguration.LanguageLevel.POPULAR.name() + ")", ParserConfiguration.LanguageLevel.POPULAR)); 20 | 21 | // The "RAW" language level doesn't perform any validations (e.g. checking if 'yield' is permitted as an identifier). 22 | this.addItem(new LanguageLevelComboItem("RAW", ParserConfiguration.LanguageLevel.RAW)); 23 | 24 | // List all available language levels (in descending order - recent to older). 25 | // Note that ordering of the options depends on the order they're declared within JavaParser. 26 | ParserConfiguration.LanguageLevel[] languageLevels = ParserConfiguration.LanguageLevel.values(); 27 | for (int i = languageLevels.length - 1; i >= 0; i--) { 28 | this.addItem(new LanguageLevelComboItem(languageLevels[i].name(), languageLevels[i])); 29 | } 30 | 31 | } 32 | 33 | 34 | private static class LanguageLevelComboItem extends CustomComboItem { 35 | 36 | public LanguageLevelComboItem(@NotNull String key, ParserConfiguration.LanguageLevel value) { 37 | super(key, value); 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/AstInspectorToolWindow.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
120 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/AstInspectorToolWindow.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms; 2 | 3 | import com.github.javaparser.JavaParser; 4 | import com.github.javaparser.ParseResult; 5 | import com.github.javaparser.ParserConfiguration; 6 | import com.github.javaparser.ast.CompilationUnit; 7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel.ConfigPanel; 9 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs.ParseResultsTabPane; 10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs.ParseResultsTabPanesContainer; 11 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.Constants; 12 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.PsiUtil; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.util.IconLoader; 15 | import com.intellij.openapi.wm.ToolWindow; 16 | import com.intellij.psi.PsiFile; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import javax.swing.*; 20 | import java.awt.*; 21 | import java.io.IOException; 22 | import java.net.URI; 23 | import java.nio.file.Path; 24 | import java.util.Optional; 25 | 26 | public class AstInspectorToolWindow implements Form { 27 | 28 | private static final NotificationLogger notificationLogger = new NotificationLogger(AstInspectorToolWindow.class); 29 | 30 | @NotNull 31 | private final Project project; 32 | 33 | @NotNull 34 | private final ToolWindow toolWindow; 35 | 36 | private final ParserConfiguration parserConfiguration; 37 | 38 | private ConfigPanel configPanel; 39 | private JButton gitHubButton; 40 | private JButton javaParserButton; 41 | private JPanel mainPanel; 42 | private JButton parseButton; 43 | private ParseResultsTabPanesContainer parseResultsTabPanesContainer1; 44 | private JButton resetButton; 45 | 46 | 47 | public AstInspectorToolWindow(@NotNull final Project project, @NotNull final ToolWindow toolWindow, @NotNull ParserConfiguration parserConfiguration) { 48 | this.project = project; 49 | this.toolWindow = toolWindow; 50 | this.parserConfiguration = parserConfiguration; 51 | } 52 | 53 | 54 | private static void browseToUrl(@NotNull final String url) { 55 | notificationLogger.info("BUTTON CLICK: URL=" + url); 56 | try { 57 | Desktop.getDesktop().browse(URI.create(url)); 58 | } catch (IOException ioException) { 59 | ioException.printStackTrace(); 60 | notificationLogger.warn(ioException.getMessage(), ioException); 61 | } 62 | } 63 | 64 | 65 | private JButton buttonWithIcon(@NotNull final String resourcePath) { 66 | final JButton jButton = new JButton(); 67 | 68 | final Icon icon = IconLoader.getIcon(resourcePath); 69 | jButton.setIcon(icon); 70 | 71 | return jButton; 72 | } 73 | 74 | 75 | private void createUIComponents() { 76 | notificationLogger.traceEnter(this.project); 77 | 78 | // 79 | this.initButtons(); 80 | 81 | // 82 | this.configPanel = new ConfigPanel(this.parserConfiguration); 83 | this.parseResultsTabPanesContainer1 = new ParseResultsTabPanesContainer(); 84 | this.parseResultsTabPanesContainer1.doReset(this.project); 85 | } 86 | 87 | 88 | @Override 89 | public Optional getMainPanel() { 90 | return Optional.ofNullable(this.mainPanel); 91 | } 92 | 93 | 94 | private void initButtons() { 95 | notificationLogger.traceEnter(this.project); 96 | 97 | // Create buttons 98 | this.parseButton = new JButton(); 99 | this.resetButton = new JButton(); 100 | this.gitHubButton = new JButton(); 101 | this.javaParserButton = this.buttonWithIcon("/logos/jp-logo_13x13.png"); 102 | 103 | // Add button click handlers 104 | this.parseButton.addActionListener(e -> this.parseButtonClickHandler()); 105 | this.resetButton.addActionListener(e -> this.resetButtonClickHandler()); 106 | this.gitHubButton.addActionListener(e -> browseToUrl(Constants.URL_GITHUB_PLUGIN)); 107 | this.javaParserButton.addActionListener(e -> browseToUrl(Constants.URL_WEBSITE_JP)); 108 | 109 | } 110 | 111 | 112 | private void parseButtonClickHandler() { 113 | notificationLogger.traceEnter(this.project); 114 | 115 | // this.parseResultsTabPanesContainer1. 116 | 117 | final Optional currentFileInEditor = PsiUtil.getCurrentFileInEditor(this.project); 118 | if (currentFileInEditor.isPresent()) { 119 | final PsiFile psiFile = currentFileInEditor.get(); 120 | final JavaParser javaParser = new JavaParser(this.configPanel.getConfigFromForm()); 121 | 122 | 123 | // parse result 124 | // final Optional> optionalParseResult = parsePsiFile_editorContents(psiFile); 125 | final Optional> optionalParseResult = this.parsePsiFile_diskContents(javaParser, psiFile); 126 | 127 | if (optionalParseResult.isPresent()) { 128 | final ParseResult parseResult = optionalParseResult.get(); 129 | 130 | // FIXME 131 | final ParseResultsTabPane pane = this.parseResultsTabPanesContainer1.addParseResultPane(this.project, psiFile, parseResult); 132 | pane.handleParseResult(this.configPanel, psiFile, parseResult); 133 | 134 | this.parseResultsTabPanesContainer1.setSelectedComponent(pane); 135 | 136 | } else { 137 | notificationLogger.warn(this.project, "No parse result available for file: " + psiFile); 138 | } 139 | } else { 140 | notificationLogger.warn(this.project, "No file selected in editor."); 141 | } 142 | 143 | } 144 | 145 | 146 | private Optional> parsePsiFile_diskContents(JavaParser javaParser, PsiFile psiFile) { 147 | notificationLogger.traceEnter(this.project); 148 | try { 149 | final Path path = PsiUtil.pathForPsi(psiFile); 150 | final ParseResult result = javaParser.parse(path); 151 | return Optional.of(result); 152 | } catch (IOException e) { 153 | notificationLogger.warn(this.project, "Error trying to parse file.", e); 154 | e.printStackTrace(); 155 | return Optional.empty(); 156 | } 157 | } 158 | 159 | 160 | private void resetButtonClickHandler() { 161 | notificationLogger.traceEnter(this.project); 162 | this.parseResultsTabPanesContainer1.doReset(this.project); 163 | } 164 | 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/Form.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms; 2 | 3 | import javax.swing.*; 4 | import java.util.Optional; 5 | 6 | public interface Form { 7 | Optional getMainPanel(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/output_results_tabs/ParseResultsTabPanesContainer.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs; 2 | 3 | import com.github.javaparser.ParseResult; 4 | import com.github.javaparser.ast.CompilationUnit; 5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.ui.components.JBTabbedPane; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class ParseResultsTabPanesContainer extends JBTabbedPane { 17 | 18 | private static final NotificationLogger notificationLogger = new NotificationLogger(ParseResultsTabPanesContainer.class); 19 | 20 | @NotNull 21 | private final List panes; 22 | 23 | 24 | public ParseResultsTabPanesContainer() { 25 | super(); 26 | notificationLogger.traceEnter(); 27 | 28 | this.panes = new ArrayList<>(); 29 | 30 | this.doReset(null); 31 | } 32 | 33 | 34 | public ParseResultsTabPane addParseResultPane(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull ParseResult parseResult) { 35 | notificationLogger.traceEnter(project); 36 | 37 | // Remove previous 38 | this.removeAll(); 39 | 40 | // Add new 41 | final ParseResultsTabPane parseResultsTabPane = new ParseResultsTabPane(project, psiFile, parseResult); 42 | this.add(parseResultsTabPane.getPaneTitle(), parseResultsTabPane); 43 | this.setSelectedComponent(parseResultsTabPane); 44 | 45 | return parseResultsTabPane; 46 | } 47 | 48 | 49 | public void doReset(@Nullable Project project) { 50 | notificationLogger.traceEnter(project); 51 | 52 | // Remove previous 53 | this.removeAll(); 54 | 55 | // Add blank 56 | final EmptyPane newPanel = new EmptyPane(); 57 | this.add(newPanel); 58 | this.setSelectedComponent(newPanel); 59 | } 60 | 61 | 62 | @NotNull 63 | public List getPanes() { 64 | notificationLogger.traceEnter(); 65 | return this.panes; 66 | } 67 | 68 | 69 | private static class EmptyPane extends JPanel { 70 | 71 | EmptyPane() { 72 | super(); 73 | this.add(new JLabel("No files parsed."), SwingConstants.CENTER); 74 | } 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/tool_window/AstBrowserToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.tool_window; 2 | 3 | import com.github.javaparser.ParserConfiguration; 4 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms.AstInspectorToolWindow; 6 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms.Form; 7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.LanguageLevelUtil; 8 | import com.intellij.openapi.project.DumbAware; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.roots.ProjectRootManager; 11 | import com.intellij.openapi.vfs.VirtualFile; 12 | import com.intellij.openapi.wm.ToolWindow; 13 | import com.intellij.openapi.wm.ToolWindowFactory; 14 | import com.intellij.ui.content.Content; 15 | import com.intellij.ui.content.ContentFactory; 16 | import com.intellij.ui.content.ContentManager; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import javax.swing.*; 20 | import java.util.Arrays; 21 | import java.util.Optional; 22 | import java.util.stream.Collectors; 23 | 24 | public class AstBrowserToolWindowFactory implements ToolWindowFactory, DumbAware { 25 | 26 | private static final NotificationLogger notificationLogger = new NotificationLogger(AstBrowserToolWindowFactory.class); 27 | 28 | 29 | /** 30 | * Helper method for adding content (tabs) to the tool window. 31 | */ 32 | public void addContent(final ToolWindow toolWindow, String panelTitle, Form form) { 33 | 34 | /* 35 | * https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/tool_windows.html 36 | * """As mentioned previously, tool windows can contain multiple tabs, or contents. 37 | * To manage the contents of a tool window, you can call ToolWindow.getContentManager(). 38 | * To add a tab (content), you first need to create it by calling ContentManager.getFactory().createContent(), 39 | * and then to add it to the tool window using ContentManager.addContent().""" 40 | */ 41 | final ContentManager contentManager = toolWindow.getContentManager(); 42 | final ContentFactory contentManagerFactory = contentManager.getFactory(); 43 | 44 | final Optional mainPanel = form.getMainPanel(); 45 | if (mainPanel.isPresent()) { 46 | final Content panelContent = contentManagerFactory.createContent(mainPanel.get(), panelTitle, false); 47 | contentManager.addContent(panelContent); 48 | } else { 49 | notificationLogger.warn("The panel is unexpectedly null -- unable to produce the tool window."); 50 | } 51 | 52 | } 53 | 54 | 55 | /** 56 | * https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/tool_windows.html 57 | * In addition to that, you specify the factory class - the name of a class implementing the ToolWindowFactory interface. 58 | * 59 | * When the user clicks on the tool window button, the createToolWindowContent() method of the factory class is called, 60 | * and initializes the UI of the tool window. 61 | * 62 | * This procedure ensures that unused tool windows don’t cause any overhead in startup time or memory usage: if a user 63 | * does not interact with the tool window of your plugin, no plugin code will be loaded or executed. 64 | */ 65 | @Override 66 | public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { 67 | notificationLogger.traceEnter(project); 68 | 69 | 70 | // Parse Only Panel 71 | ParserConfiguration parserConfiguration = new ParserConfiguration(); 72 | 73 | ParserConfiguration.LanguageLevel defaultJpLanguageLevel = ParserConfiguration.LanguageLevel.CURRENT; 74 | ParserConfiguration.LanguageLevel languageLevelFromProject = LanguageLevelUtil 75 | .getLanguageLevelFromProject(project, defaultJpLanguageLevel); 76 | parserConfiguration.setLanguageLevel(languageLevelFromProject); 77 | 78 | String projectName = project.getName(); 79 | ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project); 80 | VirtualFile[] vFiles = projectRootManager.getContentSourceRoots(); 81 | String sourceRootsList = Arrays.stream(vFiles) 82 | .map(VirtualFile::getUrl) 83 | .map(s -> " - " + s) 84 | .collect(Collectors.joining(String.format("%n"))); 85 | 86 | notificationLogger.info(project, String.format("Detected JavaParser language level for %s:%n%s", projectName, parserConfiguration.getLanguageLevel().toString())); 87 | notificationLogger.info(project, String.format("Source roots for the %s plugin:%n%s", projectName, sourceRootsList)); 88 | 89 | final AstInspectorToolWindow parseOnlyPanel = new AstInspectorToolWindow(project, toolWindow, parserConfiguration); 90 | this.addContent(toolWindow, "Parse Only", parseOnlyPanel); 91 | 92 | 93 | // Parse and Resolve Panel 94 | // ... 95 | 96 | 97 | // Parse and Export Panel 98 | // ... 99 | 100 | } 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/Constants.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import java.io.File; 4 | 5 | public final class Constants { 6 | 7 | public static final String PLUGIN_NAME = "JavaParser AST Inspector"; 8 | public static final String TOOL_WINDOW_ID = "JavaParser AST Inspector"; // see plugin.xml 9 | public static final String DEFAULT_EXPORT_DIR = System.getProperty("user.home") + File.separatorChar + TOOL_WINDOW_ID; 10 | 11 | public static final String URL_GITHUB_JAVAPARSER = "https://github.com/JavaParser/JavaParser"; 12 | public static final String URL_GITHUB_PLUGIN = "https://github.com/MysterAitch/JavaParser-AST-Inspector"; 13 | public static final String URL_WEBSITE_JP = "http://javaparser.org/"; 14 | 15 | 16 | private Constants() { 17 | // Empty private constructor, to prevent instantiation. 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/EditorUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import com.github.javaparser.Range; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.editor.LogicalPosition; 6 | import com.intellij.openapi.editor.ScrollType; 7 | import com.intellij.openapi.util.TextRange; 8 | 9 | public final class EditorUtil { 10 | 11 | private EditorUtil() { 12 | // Empty private constructor, to prevent instantiation. 13 | } 14 | 15 | 16 | public static TextRange javaParserRangeToIntellijOffsetRange(final Editor editor, final Range range) { 17 | // Note that the JavaParser Range has 1-indexed lines, while the IntelliJ LogicalPosition is 0-indexed. 18 | final LogicalPosition startPosition = new LogicalPosition(range.begin.line - 1, range.begin.column - 1); // start highlighting just before the given character/column 19 | final LogicalPosition endPosition = new LogicalPosition(range.end.line - 1, range.end.column); // end highlighting at the end of the given character/column 20 | 21 | final int startOffset = editor.logicalPositionToOffset(startPosition); 22 | final int endOffset = editor.logicalPositionToOffset(endPosition); 23 | 24 | return new TextRange(startOffset, endOffset); 25 | } 26 | 27 | 28 | public static void scrollToPosition(final Editor editor, final int offset) { 29 | // Scroll to position 30 | editor.getCaretModel().moveToOffset(offset); 31 | editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/FontUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import java.awt.*; 4 | import java.awt.font.TextAttribute; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public final class FontUtil { 9 | 10 | /** 11 | * A style that represents the title font. 12 | */ 13 | public static final Font TITLE_FONT; 14 | 15 | static { 16 | Map attributes = new HashMap<>(); 17 | 18 | attributes.put(TextAttribute.FAMILY, Font.DIALOG); 19 | // attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_SEMIBOLD); 20 | attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD); 21 | attributes.put(TextAttribute.SIZE, 12); 22 | 23 | TITLE_FONT = Font.getFont(attributes); 24 | } 25 | 26 | private FontUtil() { 27 | // Empty private constructor, to prevent instantiation. 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/LanguageLevelUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import com.github.javaparser.ParserConfiguration; 4 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.roots.LanguageLevelProjectExtension; 7 | import com.intellij.pom.java.LanguageLevel; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public final class LanguageLevelUtil { 16 | 17 | private static final NotificationLogger notificationLogger = new NotificationLogger(LanguageLevelUtil.class); 18 | 19 | private static final HashMap languageLevelMap = new HashMap<>(); 20 | 21 | static { 22 | initMappings(); 23 | } 24 | 25 | 26 | private LanguageLevelUtil() { 27 | // Prevent initialisation 28 | } 29 | 30 | 31 | public static ParserConfiguration.LanguageLevel getLanguageLevelFromProject(@NotNull Project project, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) { 32 | final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(project); 33 | final LanguageLevel projectLanguageLevel = languageLevelProjectExtension.getLanguageLevel(); 34 | 35 | return mapIntellijLanguageLevelToJavaParserLanguageLevel(projectLanguageLevel, defaultJpLanguageLevel); 36 | } 37 | 38 | 39 | public static Optional getLanguageLevelFromProject(@NotNull Project project) { 40 | final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(project); 41 | final LanguageLevel projectLanguageLevel = languageLevelProjectExtension.getLanguageLevel(); 42 | 43 | return mapIntellijLanguageLevelToJavaParserLanguageLevel(projectLanguageLevel); 44 | } 45 | 46 | 47 | private static List getLanguageLevelNameVariants(String plainLanguageLevelName) { 48 | List variants = new ArrayList<>(10); 49 | 50 | // No prefix 51 | variants.add(plainLanguageLevelName); 52 | 53 | // JavaParser uses `JAVA_{}` 54 | variants.add("JAVA_" + plainLanguageLevelName); 55 | 56 | // IntelliJ uses `JDK_{}`. 57 | variants.add("JDK_" + plainLanguageLevelName); 58 | 59 | // Some versions have a `1.` prefix (e.g. JDK 6 is also known as 1.6, thus is listed as JDK_1_6 as opposed to JDK_6). 60 | // This is a bit of a hacky catch-all, given that not all places are consistent (e.g. JP has JAVA_6, but IntelliJ has JDK_1_6 61 | variants.add("JAVA_1_" + plainLanguageLevelName); 62 | variants.add("JDK_1_" + plainLanguageLevelName); 63 | 64 | return variants; 65 | } 66 | 67 | 68 | private static void initMappings() { 69 | /* 70 | * Iterate over all known JavaParser language levels. 71 | * Note that this will include preview versions too. 72 | */ 73 | for (ParserConfiguration.LanguageLevel javaParserLanguageLevel : ParserConfiguration.LanguageLevel.values()) { 74 | String plainLanguageLevelName = javaParserLanguageLevel 75 | .name() 76 | .replace("JAVA_1_", "JAVA_") 77 | .replace("JAVA_", ""); 78 | 79 | getLanguageLevelNameVariants(plainLanguageLevelName) 80 | .forEach(s -> languageLevelMap.put(s, javaParserLanguageLevel)); 81 | } 82 | 83 | // IntelliJ also contains "JDK X" which includes "experimental features" - i.e., latest preview 84 | getLanguageLevelNameVariants("X") 85 | .forEach(s -> languageLevelMap.put(s, ParserConfiguration.LanguageLevel.BLEEDING_EDGE)); 86 | } 87 | 88 | 89 | @NotNull 90 | public static ParserConfiguration.LanguageLevel mapIntellijLanguageLevelToJavaParserLanguageLevel(@NotNull LanguageLevel intellijLanguageLevel, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) { 91 | final String intellijLanguageLevelName = intellijLanguageLevel.name(); 92 | return mapStringLanguageLevelToJavaParserLanguageLevel(intellijLanguageLevelName, defaultJpLanguageLevel); 93 | } 94 | 95 | 96 | @NotNull 97 | public static Optional mapIntellijLanguageLevelToJavaParserLanguageLevel(@NotNull LanguageLevel intellijLanguageLevel) { 98 | final String intellijLanguageLevelName = intellijLanguageLevel.name(); 99 | return mapStringLanguageLevelToJavaParserLanguageLevel(intellijLanguageLevelName); 100 | } 101 | 102 | 103 | @NotNull 104 | public static ParserConfiguration.LanguageLevel mapStringLanguageLevelToJavaParserLanguageLevel(@NotNull final String languageLevelName, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) { 105 | return mapStringLanguageLevelToJavaParserLanguageLevel(languageLevelName).orElseGet(() -> { 106 | notificationLogger.warn("Mapping for IntelliJ Language Level (`" + languageLevelName + "`) not found, defaulting to JavaParser's `" + defaultJpLanguageLevel + "`."); 107 | return defaultJpLanguageLevel; 108 | }); 109 | } 110 | 111 | 112 | @NotNull 113 | public static Optional mapStringLanguageLevelToJavaParserLanguageLevel(@NotNull final String languageLevelName) { 114 | ParserConfiguration.LanguageLevel mappedLanguageLevel = languageLevelMap.get(languageLevelName); 115 | return Optional.ofNullable(mappedLanguageLevel); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/PsiUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import com.intellij.openapi.fileEditor.FileEditorManager; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiManager; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.util.Objects; 14 | import java.util.Optional; 15 | 16 | public final class PsiUtil { 17 | 18 | private PsiUtil() { 19 | // Empty private constructor, to prevent instantiation. 20 | } 21 | 22 | 23 | public static Optional getCurrentFileInEditor(@NotNull Project project) { 24 | FileEditorManager manager = FileEditorManager.getInstance(project); 25 | VirtualFile[] files = manager.getSelectedFiles(); 26 | if (files.length == 0) { 27 | return Optional.empty(); 28 | } 29 | 30 | final VirtualFile currentFile = files[0]; 31 | final PsiFile psiFile = PsiManager.getInstance(project).findFile(currentFile); 32 | 33 | return Optional.ofNullable(psiFile); 34 | } 35 | 36 | 37 | public static Optional getInputText(@NotNull Project project) { 38 | final Optional psiFile = getCurrentFileInEditor(project); 39 | return psiFile.map(PsiElement::getText); 40 | } 41 | 42 | 43 | public static Path pathForPsi(@NotNull PsiFile psiFile) { 44 | Objects.requireNonNull(psiFile); 45 | 46 | final VirtualFile virtualFile = Objects.requireNonNull(psiFile.getVirtualFile()); 47 | final String canonicalPathString = virtualFile.getCanonicalPath(); 48 | if (canonicalPathString == null) { 49 | throw new IllegalStateException("Unable to get the path because the canonical path to the PSI virtual file is null."); 50 | } 51 | 52 | return Paths.get(canonicalPathString); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public final class StringUtil { 6 | 7 | 8 | private StringUtil() { 9 | // Empty private constructor, to prevent instantiation. 10 | } 11 | 12 | /** 13 | * @param input The text to have padding applied. 14 | * @param desiredMinimumLength The minimum length of the output. 15 | * @return When the input length is > desired minimum length, the input. 16 | * Otherwise the input padded with spaces at the END, up to the desiredMinimumLength 17 | */ 18 | public static String padEnd(String input, int desiredMinimumLength) { 19 | if (input.length() > desiredMinimumLength) { 20 | return input; 21 | } 22 | 23 | StringBuilder inputBuilder = new StringBuilder(input); 24 | while (inputBuilder.length() < desiredMinimumLength) { 25 | inputBuilder.append(" "); 26 | } 27 | 28 | return inputBuilder.toString(); 29 | } 30 | 31 | /** 32 | * @param input The text to have padding applied. 33 | * @param desiredMinimumLength The minimum length of the output. 34 | * @return When the input length is > desired minimum length, the input. 35 | * Otherwise the input padded with spaces at the START, up to the desiredMinimumLength 36 | */ 37 | public static String padStart(String input, int desiredMinimumLength) { 38 | if (input.length() > desiredMinimumLength) { 39 | return input; 40 | } 41 | 42 | StringBuilder inputBuilder = new StringBuilder(input); 43 | while (inputBuilder.length() < desiredMinimumLength) { 44 | inputBuilder.insert(0, " "); 45 | } 46 | 47 | return inputBuilder.toString(); 48 | } 49 | 50 | @SuppressWarnings({"HardcodedFileSeparator"}) 51 | @NotNull 52 | public static String escapeTab(String string) { 53 | return string.replace("\t", "\\t"); 54 | } 55 | 56 | @SuppressWarnings({"HardcodedLineSeparator", "HardcodedFileSeparator"}) 57 | @NotNull 58 | public static String escapeNewlines(String string) { 59 | return string 60 | .replace("\n", "\\n") 61 | .replace("\r", "\\r") 62 | .replace("\r\n", "\\r\\n"); 63 | } 64 | 65 | @NotNull 66 | public static String escapeWhitespace(String string) { 67 | return escapeTab(escapeNewlines(string)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/JavaCodeBrowser/graph icon -- 13x13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon -- 13x13.png -------------------------------------------------------------------------------- /src/main/resources/JavaCodeBrowser/graph icon -- error -- 13x13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon -- error -- 13x13.png -------------------------------------------------------------------------------- /src/main/resources/JavaCodeBrowser/graph icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon.xcf -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.github.rogerhowell.JavaCodeBrowser 3 | JavaParser AST Inspector 4 | Roger Howell 5 | 6 | 7 | 8 | 9 | 10 | 11 | com.intellij.modules.platform 12 | 13 | 14 | 15 | 16 | com.intellij.java 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 32 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/logos/jp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/logos/jp-logo.png -------------------------------------------------------------------------------- /src/main/resources/logos/jp-logo_13x13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/logos/jp-logo_13x13.png -------------------------------------------------------------------------------- /src/test/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/StringUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class StringUtilTest { 7 | 8 | @Test 9 | public void padEndTest0() { 10 | String input = "ABC"; 11 | String output = StringUtil.padEnd(input, 0); 12 | Assertions.assertEquals("ABC", output); 13 | } 14 | @Test 15 | public void padEndTest1() { 16 | String input = "ABC"; 17 | String output = StringUtil.padEnd(input, 1); 18 | Assertions.assertEquals("ABC", output); 19 | } 20 | @Test 21 | public void padEndTest2() { 22 | String input = "ABC"; 23 | String output = StringUtil.padEnd(input, 2); 24 | Assertions.assertEquals("ABC", output); 25 | } 26 | @Test 27 | public void padEndTest3() { 28 | String input = "ABC"; 29 | String output = StringUtil.padEnd(input, 3); 30 | Assertions.assertEquals("ABC", output); 31 | } 32 | @Test 33 | public void padEndTest4() { 34 | String input = "ABC"; 35 | String output = StringUtil.padEnd(input, 4); 36 | Assertions.assertEquals("ABC ", output); 37 | } 38 | @Test 39 | public void padEndTest5() { 40 | String input = "ABC"; 41 | String output = StringUtil.padEnd(input, 5); 42 | Assertions.assertEquals("ABC ", output); 43 | } 44 | @Test 45 | public void padEndTest10() { 46 | String input = "ABC"; 47 | String output = StringUtil.padEnd(input, 10); 48 | Assertions.assertEquals("ABC ", output); 49 | } 50 | @Test 51 | public void padStartTest0() { 52 | String input = "ABC"; 53 | String output = StringUtil.padStart(input, 0); 54 | Assertions.assertEquals("ABC", output); 55 | } 56 | @Test 57 | public void padStartTest1() { 58 | String input = "ABC"; 59 | String output = StringUtil.padStart(input, 1); 60 | Assertions.assertEquals("ABC", output); 61 | } 62 | @Test 63 | public void padStartTest2() { 64 | String input = "ABC"; 65 | String output = StringUtil.padStart(input, 2); 66 | Assertions.assertEquals("ABC", output); 67 | } 68 | @Test 69 | public void padStartTest3() { 70 | String input = "ABC"; 71 | String output = StringUtil.padStart(input, 3); 72 | Assertions.assertEquals("ABC", output); 73 | } 74 | @Test 75 | public void padStartTest4() { 76 | String input = "ABC"; 77 | String output = StringUtil.padStart(input, 4); 78 | Assertions.assertEquals(" ABC", output); 79 | } 80 | @Test 81 | public void padStartTest5() { 82 | String input = "ABC"; 83 | String output = StringUtil.padStart(input, 5); 84 | Assertions.assertEquals(" ABC", output); 85 | } 86 | @Test 87 | public void padStartTest10() { 88 | String input = "ABC"; 89 | String output = StringUtil.padStart(input, 10); 90 | Assertions.assertEquals(" ABC", output); 91 | } 92 | } 93 | --------------------------------------------------------------------------------