├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── BUILD.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _config.yml ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imges ├── context_menu.png ├── generate.png ├── lambda_expr.png ├── new.svg ├── sd1x.svg ├── sd2x.svg ├── sequence.svg ├── smart_interface.png └── tools_menu.png ├── rider.md ├── settings.gradle.kts └── src ├── main ├── java │ ├── icons │ │ └── SequencePluginIcons.java │ └── vanstudio │ │ └── sequence │ │ ├── SequencePanel.java │ │ ├── SequenceParamsEditor.java │ │ ├── SequenceService.java │ │ ├── SequenceToolWindowsFactory.java │ │ ├── ShowSequenceAction.java │ │ ├── ShowSequenceActionGroup.java │ │ ├── config │ │ ├── ColorConverter.java │ │ ├── ColorMapEntry.java │ │ ├── ColorSupport.java │ │ ├── ConfigListener.java │ │ ├── ExcludeEntry.java │ │ ├── SequenceParamsState.java │ │ ├── SequenceSettingsComponent.form │ │ ├── SequenceSettingsComponent.java │ │ ├── SequenceSettingsConfigurable.java │ │ └── SequenceSettingsState.java │ │ ├── diagram │ │ ├── Call.java │ │ ├── CallReturn.java │ │ ├── Diagram.java │ │ ├── Display.java │ │ ├── DisplayCall.java │ │ ├── DisplayCallReturn.java │ │ ├── DisplayLink.java │ │ ├── DisplayMethod.java │ │ ├── DisplayObject.java │ │ ├── DisplaySelfCall.java │ │ ├── DisplaySelfCallReturn.java │ │ ├── DisplaySelfLink.java │ │ ├── Info.java │ │ ├── LambdaExprInfo.java │ │ ├── Link.java │ │ ├── MethodInfo.java │ │ ├── Model.java │ │ ├── ModelTextEvent.java │ │ ├── ModelTextListener.java │ │ ├── Numbering.java │ │ ├── ObjectInfo.java │ │ ├── Parser.java │ │ ├── PreviewFrame.java │ │ ├── PreviewPanel.java │ │ ├── ScreenObject.java │ │ ├── SequenceListener.java │ │ ├── TextBox.java │ │ └── app │ │ │ ├── Editor.java │ │ │ ├── Sequence.java │ │ │ └── actions │ │ │ ├── ExampleAction.java │ │ │ ├── ExitAction.java │ │ │ ├── ExportAction.java │ │ │ ├── ModelAction.java │ │ │ ├── ModifiedConfirmAction.java │ │ │ ├── ModifiedEnabledAction.java │ │ │ ├── NewAction.java │ │ │ ├── OpenAction.java │ │ │ ├── SaveAction.java │ │ │ ├── SaveAsAction.java │ │ │ └── SequenceAction.java │ │ ├── formatter │ │ ├── IFormatter.java │ │ ├── MermaidFormatter.java │ │ ├── PlantUMLFormatter.java │ │ └── SdtFormatter.java │ │ ├── generator │ │ ├── EmptySequenceNavigable.java │ │ ├── JavaActionFinder.java │ │ ├── JavaElementTypeFinder.java │ │ ├── JavaGeneratorFactory.java │ │ ├── JavaSequenceNavigable.java │ │ ├── SequenceGenerator.java │ │ └── filters │ │ │ ├── ImplementClassFilter.java │ │ │ ├── NoConstructorsFilter.java │ │ │ ├── NoGetterSetterFilter.java │ │ │ ├── NoPrivateMethodsFilter.java │ │ │ ├── SingleClassFilter.java │ │ │ └── SingleMethodFilter.java │ │ ├── impl │ │ └── SequenceServiceImpl.java │ │ ├── openapi │ │ ├── ActionFinder.java │ │ ├── Constants.java │ │ ├── ElementTypeFinder.java │ │ ├── GenerateFinishedListener.java │ │ ├── GeneratorFactory.java │ │ ├── IGenerator.java │ │ ├── SequenceNavigable.java │ │ ├── SequenceNavigableFactory.java │ │ ├── SequenceParams.java │ │ ├── filters │ │ │ ├── CompositeElementFilter.java │ │ │ ├── ImplementationWhiteList.java │ │ │ ├── MethodFilter.java │ │ │ ├── PackageFilter.java │ │ │ └── ProjectOnlyFilter.java │ │ └── model │ │ │ ├── CallStack.java │ │ │ ├── ClassDescription.java │ │ │ ├── GenericType.java │ │ │ ├── LambdaExprDescription.java │ │ │ └── MethodDescription.java │ │ ├── ui │ │ ├── ButtonTabComponent.java │ │ ├── MyButtonlessScrollBarUI.java │ │ ├── OptionsUI.java │ │ ├── PlasticTabbedPaneUI.java │ │ └── Welcome.java │ │ └── util │ │ ├── MyNotifier.java │ │ ├── MyPsiUtil.java │ │ └── MyUastUtil.kt └── resources │ ├── META-INF │ ├── SequenceDiagram-kotlin.xml │ ├── plugin.xml │ ├── pluginIcon.svg │ └── pluginIcon_dark.svg │ ├── icons │ ├── folder.svg │ ├── image.svg │ ├── play.svg │ ├── puml.svg │ ├── save.svg │ ├── sequence.svg │ ├── sequence_13.svg │ └── settings.svg │ ├── vanstudio │ └── sequence │ │ └── diagram │ │ └── app │ │ └── Sequence.properties │ └── welcome.html └── test └── java └── vanstudio └── sequence └── generator └── MethodDescriptionTest.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 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Plugin & IDE info:** 27 | - Plugin Version [e.g. 2.2.0] 28 | - IDE Name & Version [e.g. IDEA Comunity 2022.2] 29 | - Which language (e.g: Java, Kotlin, Scala, Groovy) you try to generate? 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | - code fragment 34 | ```java 35 | 36 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,windows,macos,linux 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij,windows,macos,linux 4 | 5 | ### Intellij ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # AWS User-specific 17 | .idea/**/aws.xml 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/artifacts 40 | # .idea/compiler.xml 41 | # .idea/jarRepositories.xml 42 | # .idea/modules.xml 43 | # .idea/*.iml 44 | # .idea/modules 45 | # *.iml 46 | # *.ipr 47 | 48 | # CMake 49 | cmake-build-*/ 50 | 51 | # Mongo Explorer plugin 52 | .idea/**/mongoSettings.xml 53 | 54 | # File-based project format 55 | *.iws 56 | 57 | # IntelliJ 58 | out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Cursive Clojure plugin 67 | .idea/replstate.xml 68 | 69 | # SonarLint plugin 70 | .idea/sonarlint/ 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | # Editor-based Rest Client 79 | .idea/httpRequests 80 | 81 | # Android studio 3.1+ serialized cache file 82 | .idea/caches/build_file_checksums.ser 83 | 84 | ### Intellij Patch ### 85 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 86 | 87 | # *.iml 88 | # modules.xml 89 | # .idea/misc.xml 90 | # *.ipr 91 | 92 | # Sonarlint plugin 93 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 94 | .idea/**/sonarlint/ 95 | 96 | # SonarQube Plugin 97 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 98 | .idea/**/sonarIssues.xml 99 | 100 | # Markdown Navigator plugin 101 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 102 | .idea/**/markdown-navigator.xml 103 | .idea/**/markdown-navigator-enh.xml 104 | .idea/**/markdown-navigator/ 105 | 106 | # Cache file creation bug 107 | # See https://youtrack.jetbrains.com/issue/JBR-2257 108 | .idea/$CACHE_FILE$ 109 | 110 | # CodeStream plugin 111 | # https://plugins.jetbrains.com/plugin/12206-codestream 112 | .idea/codestream.xml 113 | 114 | ### Java ### 115 | # Compiled class file 116 | *.class 117 | 118 | # Log file 119 | *.log 120 | 121 | # BlueJ files 122 | *.ctxt 123 | 124 | # Mobile Tools for Java (J2ME) 125 | .mtj.tmp/ 126 | 127 | # Package Files # 128 | *.jar 129 | *.war 130 | *.nar 131 | *.ear 132 | *.zip 133 | *.tar.gz 134 | *.rar 135 | 136 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 137 | hs_err_pid* 138 | replay_pid* 139 | 140 | ### Linux ### 141 | *~ 142 | 143 | # temporary files which can be created if a process still has a handle open of a deleted file 144 | .fuse_hidden* 145 | 146 | # KDE directory preferences 147 | .directory 148 | 149 | # Linux trash folder which might appear on any partition or disk 150 | .Trash-* 151 | 152 | # .nfs files are created when an open file is removed but is still being accessed 153 | .nfs* 154 | 155 | ### macOS ### 156 | # General 157 | .DS_Store 158 | .AppleDouble 159 | .LSOverride 160 | 161 | # Icon must end with two \r 162 | Icon 163 | 164 | 165 | # Thumbnails 166 | ._* 167 | 168 | # Files that might appear in the root of a volume 169 | .DocumentRevisions-V100 170 | .fseventsd 171 | .Spotlight-V100 172 | .TemporaryItems 173 | .Trashes 174 | .VolumeIcon.icns 175 | .com.apple.timemachine.donotpresent 176 | 177 | # Directories potentially created on remote AFP share 178 | .AppleDB 179 | .AppleDesktop 180 | Network Trash Folder 181 | Temporary Items 182 | .apdisk 183 | 184 | ### Windows ### 185 | # Windows thumbnail cache files 186 | Thumbs.db 187 | Thumbs.db:encryptable 188 | ehthumbs.db 189 | ehthumbs_vista.db 190 | 191 | # Dump file 192 | *.stackdump 193 | 194 | # Folder config file 195 | [Dd]esktop.ini 196 | 197 | # Recycle Bin used on file shares 198 | $RECYCLE.BIN/ 199 | 200 | # Windows Installer files 201 | *.cab 202 | *.msi 203 | *.msix 204 | *.msm 205 | *.msp 206 | 207 | # Windows shortcuts 208 | *.lnk 209 | 210 | ### Gradle ### 211 | .gradle 212 | **/build/ 213 | !src/**/build/ 214 | 215 | # Ignore Gradle GUI config 216 | gradle-app.setting 217 | 218 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 219 | !gradle-wrapper.jar 220 | 221 | # Cache of project 222 | .gradletasknamecache 223 | 224 | # Eclipse Gradle plugin generated files 225 | # Eclipse Core 226 | .project 227 | # JDT-specific (Eclipse Java Development Tools) 228 | .classpath 229 | 230 | # End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,windows,macos,linux 231 | # Add by me 232 | .sandbox/ 233 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Deploy plugin 2 | 3 | Run the command to publish to plugin repository. 4 | 5 | gradle publishPlugin 6 | 7 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.changelog.markdownToHTML 2 | 3 | fun properties(key: String) = project.findProperty(key).toString() 4 | 5 | buildscript { 6 | repositories { 7 | maven("https://maven.aliyun.com/repository/public") 8 | maven("https://maven.aliyun.com/repository/gradle-plugin") 9 | } 10 | } 11 | 12 | plugins { 13 | id("java") 14 | id("org.jetbrains.intellij") version "1.9.0" 15 | id("org.jetbrains.kotlin.jvm") version "1.7.10" 16 | id("org.jetbrains.changelog") version "1.3.1" 17 | } 18 | 19 | group = properties("pluginGroup") 20 | version = properties("pluginVersion") 21 | 22 | repositories { 23 | mavenLocal() 24 | google() 25 | maven("https://maven.aliyun.com/repository/public") 26 | maven("https://maven.aliyun.com/repository/gradle-plugin") 27 | maven("https://www.jetbrains.com/intellij-repository/releases") 28 | maven("https://cache-redirector.jetbrains.com/intellij-dependencies") 29 | } 30 | 31 | intellij { 32 | version.set(properties("platformVersion")) 33 | type.set(properties("platformType")) 34 | // localPath.set("/Applications/IntelliJ IDEA CE.app") 35 | pluginName.set(properties("pluginName")) 36 | plugins.set(properties("platformPlugins").split(',').map(String::trim).filter(String::isNotEmpty)) 37 | updateSinceUntilBuild.set(true) 38 | sandboxDir.set("${project.rootDir}/.sandbox") 39 | downloadSources.set(properties("platformDownloadSources").toBoolean()) 40 | } 41 | 42 | changelog { 43 | version.set(properties("pluginVersion")) 44 | groups.set(emptyList()) 45 | } 46 | 47 | java { 48 | sourceCompatibility = JavaVersion.VERSION_11 49 | } 50 | 51 | tasks { 52 | // withType { 53 | // options.encoding = "UTF-8" 54 | // sourceCompatibility = "1.9" 55 | // targetCompatibility = "1.9" 56 | // } 57 | 58 | patchPluginXml { 59 | version.set(properties("pluginVersion")) 60 | sinceBuild.set(properties("pluginSinceBuild")) 61 | untilBuild.set(properties("pluginUntilBuild")) 62 | pluginDescription.set( 63 | File(projectDir, "README.md").readText().lines().run { 64 | val start = "" 65 | val end = "" 66 | 67 | if (!containsAll(listOf(start, end))) { 68 | throw GradleException("Plugin description section not found in README.md:\n$start ... $end") 69 | } 70 | subList(indexOf(start) + 1, indexOf(end)) 71 | }.joinToString("\n").run { markdownToHTML(this) } 72 | ) 73 | changeNotes.set(provider { changelog.getLatest().toHTML() }) 74 | } 75 | 76 | runPluginVerifier { 77 | ideVersions.set(properties("pluginVerifierIdeVersions").split(',').map(String::trim).filter(String::isNotEmpty)) 78 | } 79 | 80 | publishPlugin { 81 | dependsOn("patchChangelog") 82 | token.set(System.getenv("PUGLISH_TOKEN")) 83 | // pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3 84 | // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more: 85 | // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel 86 | channels.set(listOf(properties("pluginVersion").split('-').getOrElse(1) { "default" }.split('.').first())) 87 | } 88 | 89 | compileKotlin { 90 | kotlinOptions.jvmTarget = "11" 91 | } 92 | 93 | compileTestKotlin { 94 | kotlinOptions.jvmTarget = "11" 95 | } 96 | } 97 | 98 | dependencies { 99 | // implementation("ar.com.hjg:pngj:2.1.0") 100 | implementation("org.apache.xmlgraphics:batik-svggen:1.14") 101 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # IntelliJ Platform Artifacts Repositories 2 | # -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html 3 | 4 | pluginGroup = vanstudio 5 | pluginName = SequenceDiagram 6 | pluginVersion = 2.2.7 7 | 8 | # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html 9 | # for insight into build numbers and IntelliJ Platform versions. 10 | pluginSinceBuild = 201 11 | pluginUntilBuild = 223.* 12 | 13 | # Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl 14 | # See https://jb.gg/intellij-platform-builds-list for available build versions. 15 | pluginVerifierIdeVersions = 2020.1.4, 2020.2.4, 2020.3.4, 2021.1.3, 2021.2.4, 2021.3.3, 2022.1.4, 2022.2.3 16 | 17 | platformType = IC 18 | platformVersion = 2022.2.1 19 | platformDownloadSources = true 20 | 21 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html 22 | # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 23 | platformPlugins = com.intellij.java, org.jetbrains.kotlin 24 | 25 | # Opt-out flag for bundling Kotlin standard library. 26 | # See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details. 27 | kotlin.stdlib.default.dependency = false 28 | 29 | #org.gradle.unsafe.configuration-cache = true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /imges/context_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/imges/context_menu.png -------------------------------------------------------------------------------- /imges/generate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/imges/generate.png -------------------------------------------------------------------------------- /imges/lambda_expr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/imges/lambda_expr.png -------------------------------------------------------------------------------- /imges/new.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /imges/sequence.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /imges/smart_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/imges/smart_interface.png -------------------------------------------------------------------------------- /imges/tools_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vanco/SequencePlugin/a40bbb36106dcf5e7243077d26bc7fc1634b4c5b/imges/tools_menu.png -------------------------------------------------------------------------------- /rider.md: -------------------------------------------------------------------------------- 1 | # Frontend's backend changes processing flow 2 | When backend changes are received, they are processed as follows: 3 | ```mermaid 4 | sequenceDiagram 5 | box Backend 6 | participant BackendPatchEngine 7 | end 8 | box Frontend 9 | participant FrontendAsyncRequestExecutor 10 | participant FrontendRdPatchHandlerRegistry 11 | end 12 | BackendPatchEngine->>+FrontendAsyncRequestExecutor: protocolModel.backendPatches (patch) 13 | FrontendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: FrontendAsyncRequestExecutor.backendChanged 14 | FrontendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: rollbackConflictingRequests 15 | Note over FrontendAsyncRequestExecutor,FrontendRdPatchHandlerRegistry: applyBackendPatch:

validate(patch.expectedItemVersions)
apply(patch.changes)
validate(patch.updatedItemVersions)
update(patch.updatedItemVersions)
apply(patch.features) 16 | FrontendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: rollForwardRevertedRequests 17 | ``` 18 | 19 | # Backend's frontend changes processing flow 20 | When frontend changes are received, they are processed as follows: 21 | ```mermaid 22 | sequenceDiagram 23 | box Frontend 24 | participant FrontendAsyncRequestExecutor 25 | end 26 | box Backend 27 | participant BackendAsyncRequestExecutor 28 | participant BackendAsyncRequestDelegatesEP 29 | participant BackendAsyncRequestDelegate 30 | end 31 | 32 | FrontendAsyncRequestExecutor->>+BackendAsyncRequestExecutor: protocolModel.requestsWithTime 33 | BackendAsyncRequestExecutor->>+BackendAsyncRequestDelegatesEP: getDelegate 34 | BackendAsyncRequestDelegatesEP->>+BackendAsyncRequestExecutor: delegate 35 | BackendAsyncRequestExecutor->>+BackendAsyncRequestDelegate: execute 36 | BackendAsyncRequestDelegate->>+BackendAsyncRequestExecutor: response 37 | BackendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: responses 38 | ``` 39 | 40 | # Frontend requests flow 41 | When frontend requests are sent, they are processed as follows: 42 | ```mermaid 43 | sequenceDiagram 44 | box Frontend 45 | participant FrontendRdPatchHandlerRegistry 46 | participant FrontendAsyncRequestExecutor 47 | end 48 | box Backend 49 | participant BackendAsyncRequestExecutor 50 | end 51 | FrontendAsyncRequestExecutor->>+BackendAsyncRequestExecutor: protocolModel.requestsWithTime 52 | Note over BackendAsyncRequestExecutor: frontend request processing:
see Backend's frontend changes processing flow 53 | BackendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: protocolModel.responsesWithTime 54 | Note over FrontendAsyncRequestExecutor: handle backend result 55 | Note over FrontendAsyncRequestExecutor: In case of patch result: 56 | FrontendAsyncRequestExecutor->>+FrontendAsyncRequestExecutor: rollbackConflictingChanges 57 | Note over FrontendAsyncRequestExecutor,FrontendRdPatchHandlerRegistry: applyBackendPatch:

validate(patch.expectedItemVersions)
apply(patch.changes)
validate(patch.updatedItemVersions)
update(patch.updatedItemVersions)
apply(patch.features) 58 | ``` 59 | 60 | # RdPatchHandlers 61 | For purpose of extensible patch content handling we have RdPatchItemHandler interfaces on frontend and backend. 62 | It's used through corresponding RdPatchHandlerRegistry over collection of items to handle. 63 | It has the following methods: 64 | - validate runs over collection of RdPatchItemVersion and typically used to check if version is right. 65 | - update runs over collection of RdPatchItemVersion and updates corresponding entity version. 66 | - apply runs over collection of RdPatchStep which and intended to use for actual patche's changes application. It returns FrontendRdPatchApplyResult which is used to return some results from aplly operation. 67 | 68 | Each of these methods also has context parameter which is used to provide additional information for operations such as project, operation type and so on. 69 | 70 | # Custom settings during patch application 71 | Sometimes, we want to set some settings for patch operations. For example - we set DISABLE_CARET_SHIFT_ON_WHITESPACE_INSERTION during patch application. 72 | 73 | For this purpose, we have PatchAccessToken EP. It provides usual access token that we get via corresponding EP (frontend/backend). 74 | Check BackendEditorSettingsAccessTokenProvider for example. -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "SequencePlugin" 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/icons/SequencePluginIcons.java: -------------------------------------------------------------------------------- 1 | package icons; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | /** 8 | * © fanhuagang@gmail.com 9 | * Created by van on 10/12/2016. 10 | */ 11 | public interface SequencePluginIcons { 12 | //svg can't display 13 | Icon SEQUENCE_ICON = icon("/icons/sequence.svg"); 14 | Icon SEQUENCE_ICON_13 = icon("/icons/sequence_13.svg"); 15 | 16 | Icon EXPORT_ICON = icon("/icons/image.svg"); 17 | Icon PLAY_ICON = icon("/icons/play.svg"); 18 | Icon SAVE_ICON = icon("/icons/save.svg"); 19 | Icon OPEN_ICON = icon("/icons/folder.svg"); 20 | Icon PUML_ICON = icon("/icons/puml.svg"); 21 | Icon SETTING_ICON = icon("/icons/settings.svg"); 22 | 23 | static Icon icon(String path) {return IconLoader.getIcon(path, SequencePluginIcons.class);} 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/SequenceParamsEditor.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.project.Project; 6 | import icons.SequencePluginIcons; 7 | import vanstudio.sequence.config.SequenceParamsState; 8 | import vanstudio.sequence.ui.OptionsUI; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * Show Sequence generate options dialog. 13 | */ 14 | public class SequenceParamsEditor extends AnAction { 15 | 16 | public SequenceParamsEditor() { 17 | super("Settings", "Change default settings", SequencePluginIcons.SETTING_ICON); 18 | } 19 | 20 | public void actionPerformed(@NotNull AnActionEvent event) { 21 | Project project = event.getProject(); 22 | if (project == null) return; 23 | 24 | OptionsUI.OptionsDialogWrapper dialogWrapper = new OptionsUI.OptionsDialogWrapper(project); 25 | dialogWrapper.show(); 26 | if (dialogWrapper.isOK()) { 27 | SequenceParamsState state = SequenceParamsState.getInstance(); 28 | 29 | state.callDepth = dialogWrapper.getCallStackDepth(); 30 | state.projectClassesOnly = dialogWrapper.isProjectClassesOnly(); 31 | state.noGetterSetters = dialogWrapper.isNoGetterSetters(); 32 | state.noPrivateMethods = dialogWrapper.isNoPrivateMethods(); 33 | state.noConstructors = dialogWrapper.isNoConstructors(); 34 | // state.smartInterface = dialogWrapper.isSmartInterface(); 35 | 36 | // Notify parameter change. 37 | state.fireConfigChanged(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/SequenceService.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | /** 6 | * © fanhuagang@gmail.com 7 | * Created by van on 2020/2/23. 8 | */ 9 | public interface SequenceService { 10 | String PLUGIN_ID = "SequenceDiagram"; 11 | String PLUGIN_NAME = "Sequence Diagram"; 12 | 13 | void showSequence(PsiElement psiElement); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/SequenceToolWindowsFactory.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.wm.ToolWindow; 5 | import com.intellij.openapi.wm.ToolWindowFactory; 6 | import com.intellij.ui.content.Content; 7 | import com.intellij.ui.content.ContentManager; 8 | import vanstudio.sequence.ui.Welcome; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * © fanhuagang@gmail.com 13 | * Created by van on 2020/4/11. 14 | */ 15 | public class SequenceToolWindowsFactory implements ToolWindowFactory { 16 | 17 | @Override 18 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 19 | 20 | addEmptyContent(project, toolWindow); 21 | } 22 | 23 | private void addEmptyContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 24 | Welcome welcome = new Welcome(); 25 | ContentManager contentManager = toolWindow.getContentManager(); 26 | Content emptyDiagram = contentManager.getFactory().createContent(welcome.getMainPanel(), "Welcome", false); 27 | emptyDiagram.setCloseable(false); 28 | contentManager.addContent(emptyDiagram); 29 | 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/ShowSequenceAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence; 2 | 3 | import com.intellij.openapi.actionSystem.*; 4 | import com.intellij.openapi.application.ModalityState; 5 | import com.intellij.openapi.application.ReadAction; 6 | import com.intellij.openapi.editor.Caret; 7 | import com.intellij.openapi.project.DumbAware; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.ui.popup.JBPopupFactory; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.util.PsiTreeUtil; 13 | import com.intellij.util.concurrency.NonUrgentExecutor; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | import vanstudio.sequence.openapi.ActionFinder; 17 | import vanstudio.sequence.openapi.ElementTypeFinder; 18 | 19 | /** 20 | * Show Sequence generate options dialog. 21 | */ 22 | public class ShowSequenceAction extends AnAction implements DumbAware { 23 | 24 | public ShowSequenceAction() { 25 | } 26 | 27 | /** 28 | * Enable or disable the menu base on file type. Current only java file will enable the menu. 29 | * 30 | * @param event event 31 | */ 32 | public void update(@NotNull AnActionEvent event) { 33 | super.update(event); 34 | 35 | Presentation presentation = event.getPresentation(); 36 | 37 | @Nullable PsiElement psiElement = event.getData(CommonDataKeys.PSI_FILE); 38 | presentation.setEnabled(isEnabled(psiElement)); 39 | 40 | } 41 | 42 | private boolean isEnabled(PsiElement psiElement) { 43 | return psiElement != null 44 | && ActionFinder.isValid(psiElement.getLanguage()); 45 | } 46 | 47 | public void actionPerformed(@NotNull AnActionEvent event) { 48 | Project project = event.getProject(); 49 | if (project == null) return; 50 | 51 | SequenceService plugin = project.getService(SequenceService.class); 52 | 53 | PsiElement psiElement = event.getData(CommonDataKeys.PSI_ELEMENT); 54 | final PsiFile psiFile = event.getData(CommonDataKeys.PSI_FILE); 55 | 56 | if (psiElement == null) { 57 | final Caret caret = event.getData(CommonDataKeys.CARET); 58 | 59 | if (psiFile != null && caret != null) { 60 | // try to find the enclosed PsiMethod / KtFunction of caret 61 | ElementTypeFinder typeFinder = ElementTypeFinder.EP_NAME.forLanguage(psiFile.getLanguage()); 62 | if (typeFinder != null) { 63 | Class method = typeFinder.findMethod(); 64 | psiElement = PsiTreeUtil.findElementOfClassAtOffset(psiFile, caret.getOffset(), method, false); 65 | 66 | // try to get top PsiClass / KtClass 67 | if (psiElement == null) { 68 | Class aClass = typeFinder.findClass(); 69 | psiElement = PsiTreeUtil.findElementOfClassAtOffset(psiFile, caret.getOffset(), aClass, false); 70 | if (psiElement != null) { 71 | chooseMethodToGenerate(event, plugin, psiElement, project); 72 | return; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | if (psiElement != null) { 80 | plugin.showSequence(psiElement); 81 | } else { 82 | if (psiFile != null) { 83 | chooseMethodToGenerate(event, plugin, psiFile, project); 84 | } 85 | } 86 | } 87 | 88 | private void chooseMethodToGenerate(@NotNull AnActionEvent event, SequenceService plugin, PsiElement psiElement, @NotNull Project project) { 89 | 90 | // for PsiClass, show popup menu list method to choose 91 | // AnAction[] list; 92 | 93 | /* 94 | For each PsiElement (PsiMethod/KtFunction) found, invoke {@code SequenceService.showSequence(psiElement)} 95 | */ 96 | ActionFinder.Task task = (method, myProject) -> plugin.showSequence(method); 97 | 98 | /* 99 | Get {@code ActionMenuFinder} by PsiFile's Language and find all PsiMethod/KtFunction with gaven task. 100 | */ 101 | // list = ReadAction.compute(() -> { 102 | // ActionFinder actionFinder = ActionFinder.getInstance(psiElement.getLanguage()); 103 | // if (actionFinder == null) { 104 | // return AnAction.EMPTY_ARRAY; 105 | // } else { 106 | // return actionFinder.find(project, psiElement, task); 107 | // } 108 | // }); 109 | 110 | ReadAction.nonBlocking(() -> { 111 | ActionFinder actionFinder = ActionFinder.getInstance(psiElement.getLanguage()); 112 | if (actionFinder == null) { 113 | return AnAction.EMPTY_ARRAY; 114 | } else { 115 | return actionFinder.find(project, psiElement, task); 116 | } 117 | }).inSmartMode(project).finishOnUiThread(ModalityState.defaultModalityState(), list -> { 118 | ActionGroup actionGroup = new ActionGroup() { 119 | @Override 120 | public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) { 121 | return list; 122 | } 123 | }; 124 | 125 | JBPopupFactory.getInstance().createActionGroupPopup("Choose Method ...", actionGroup, event.getDataContext(), 126 | null, false).showInBestPositionFor(event.getDataContext()); 127 | }).submit(NonUrgentExecutor.getInstance()); 128 | } 129 | 130 | // @Override 131 | // public @NotNull ActionUpdateThread getActionUpdateThread() { 132 | // return ActionUpdateThread.BGT; 133 | // } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/ShowSequenceActionGroup.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence; 2 | 3 | import com.intellij.openapi.actionSystem.ActionGroup; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.CommonDataKeys; 7 | import com.intellij.openapi.application.ReadAction; 8 | import com.intellij.openapi.project.DumbAware; 9 | import com.intellij.openapi.project.DumbService; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.psi.PsiElement; 12 | import com.intellij.psi.PsiFile; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jetbrains.annotations.Nullable; 15 | import vanstudio.sequence.openapi.ActionFinder; 16 | 17 | public class ShowSequenceActionGroup extends ActionGroup implements DumbAware { 18 | 19 | @Override 20 | public void update(@NotNull AnActionEvent e) { 21 | 22 | 23 | @Nullable PsiElement psiElement = e.getData(CommonDataKeys.PSI_FILE); 24 | 25 | boolean disabled = psiElement == null 26 | || ActionFinder.getInstance(psiElement.getLanguage()) == null; 27 | 28 | e.getPresentation().setEnabled(!disabled); 29 | 30 | } 31 | 32 | @Override 33 | public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) { 34 | if (e == null) return AnAction.EMPTY_ARRAY; 35 | 36 | Project project = e.getProject(); 37 | if (project == null) return AnAction.EMPTY_ARRAY; 38 | 39 | if (DumbService.isDumb(project)) return AnAction.EMPTY_ARRAY; 40 | 41 | final PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE); 42 | if (psiFile == null) return AnAction.EMPTY_ARRAY; 43 | /* 44 | For each PsiElement (PsiMethod/KtFunction) found, invoke {@code SequenceService.showSequence(psiElement)} 45 | */ 46 | ActionFinder.Task task = (psiElement, myProj) -> myProj.getService(SequenceService.class).showSequence(psiElement); 47 | 48 | /* 49 | Get {@code ActionMenuFinder} by PsiFile's Language and find all PsiMethod/KtFunction as AnAction with gaven task. 50 | */ 51 | return ReadAction.compute(() -> { 52 | ActionFinder actionFinder = ActionFinder.getInstance(psiFile.getLanguage()); 53 | return actionFinder == null ? AnAction.EMPTY_ARRAY : actionFinder.find(project, psiFile, task); 54 | }); 55 | } 56 | 57 | // @Override 58 | // public @NotNull ActionUpdateThread getActionUpdateThread() { 59 | // return ActionUpdateThread.BGT; 60 | // } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/ColorConverter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.ui.JBColor; 4 | import com.intellij.util.xmlb.Converter; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.awt.*; 9 | 10 | /** 11 | * © fanhuagang@gmail.com 12 | * Created by van on 4/6/16. 13 | */ 14 | public class ColorConverter extends Converter { 15 | 16 | public ColorConverter() {} 17 | 18 | @Nullable 19 | @Override 20 | public Color fromString(@NotNull String s) { 21 | int rgb = (int) Long.parseLong(s, 16); 22 | return new JBColor(new Color(rgb), new Color(rgb)); 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public String toString(@NotNull Color color) { 28 | return Integer.toHexString(color.getRGB()); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/ColorMapEntry.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.util.xmlb.annotations.OptionTag; 4 | import com.intellij.util.xmlb.annotations.Tag; 5 | 6 | import java.awt.*; 7 | import java.util.function.Predicate; 8 | import java.util.regex.Pattern; 9 | 10 | @Tag("colorMapEntry") 11 | public class ColorMapEntry { 12 | private String regex; 13 | @OptionTag(converter = ColorConverter.class) 14 | private Color color; 15 | 16 | public ColorMapEntry() {} 17 | 18 | public ColorMapEntry(String regex, Color color) { 19 | this.regex = regex; 20 | this.color = color; 21 | } 22 | 23 | public String getRegex() { 24 | return regex; 25 | } 26 | 27 | public void setRegex(String regex) { 28 | matcher = null; // invalidate 29 | this.regex = regex; 30 | } 31 | 32 | public Color getColor() { 33 | return color; 34 | } 35 | 36 | public void setColor(Color color) { 37 | this.color = color; 38 | } 39 | 40 | private Predicate matcher; 41 | 42 | public boolean matches(String fullName) { 43 | if(matcher==null) { 44 | // simply pattern to actual regex 45 | String regex = getRegex().replace(".", "\\.").replace("*", ".*"); 46 | matcher = Pattern.compile(regex).asMatchPredicate(); 47 | } 48 | if(fullName==null) { 49 | return false; 50 | } 51 | return matcher.test(fullName); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/ColorSupport.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.ui.JBColor; 4 | 5 | import javax.swing.*; 6 | import javax.swing.border.Border; 7 | import javax.swing.table.TableCellEditor; 8 | import javax.swing.table.TableCellRenderer; 9 | import java.awt.*; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public class ColorSupport { 16 | 17 | public static Optional lookupMappedColorFor(SequenceSettingsState sequenceSettingsState, String fullName) { 18 | final List colorMappings = sequenceSettingsState.getColorMappingList(); 19 | if(colorMappings!=null) { 20 | return sequenceSettingsState.getColorMappingList() 21 | .stream() 22 | .filter(entry->entry.matches(fullName)) 23 | .findFirst() 24 | .map(ColorMapEntry::getColor); 25 | } 26 | return Optional.empty(); 27 | } 28 | 29 | public static Color withTransparency(Color c, float alpha) { 30 | int transparency = Math.round(alpha * 255.f); 31 | return new JBColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), transparency), new Color(c.getRed(), c.getGreen(), c.getBlue(), transparency)); 32 | } 33 | 34 | public static class ColorEditor extends AbstractCellEditor 35 | implements TableCellEditor, 36 | ActionListener { 37 | Color currentColor; 38 | JButton button; 39 | protected static final String EDIT = "edit"; 40 | 41 | public ColorEditor() { 42 | //Set up the editor (from the table's point of view), 43 | //which is a button. 44 | //This button brings up the color chooser dialog, 45 | //which is the editor from the user's point of view. 46 | button = new JButton(); 47 | button.setActionCommand(EDIT); 48 | button.addActionListener(this); 49 | button.setBorderPainted(false); 50 | 51 | } 52 | 53 | /** 54 | * Handles events from the editor button and from 55 | * the dialog's OK button. 56 | */ 57 | public void actionPerformed(ActionEvent e) { 58 | if (EDIT.equals(e.getActionCommand())) { 59 | //The user has clicked the cell, so 60 | //bring up the dialog. 61 | button.setBackground(currentColor); 62 | 63 | Color newColor = JColorChooser.showDialog(button, "Pick a Color", 64 | currentColor); 65 | if(newColor != null) 66 | currentColor = newColor; 67 | 68 | //Make the renderer reappear. 69 | fireEditingStopped(); 70 | 71 | } 72 | } 73 | 74 | //Implement the one CellEditor method that AbstractCellEditor doesn't. 75 | public Object getCellEditorValue() { 76 | return currentColor; 77 | } 78 | 79 | //Implement the one method defined by TableCellEditor. 80 | public Component getTableCellEditorComponent(JTable table, 81 | Object value, 82 | boolean isSelected, 83 | int row, 84 | int column) { 85 | currentColor = (Color)value; 86 | return button; 87 | } 88 | } 89 | 90 | public static class ColorRenderer extends JLabel 91 | implements TableCellRenderer { 92 | Border unselectedBorder = null; 93 | Border selectedBorder = null; 94 | boolean isBordered; 95 | 96 | public ColorRenderer(boolean isBordered) { 97 | this.isBordered = isBordered; 98 | setOpaque(true); //MUST do this for background to show up. 99 | } 100 | 101 | public Component getTableCellRendererComponent( 102 | JTable table, Object color, 103 | boolean isSelected, boolean hasFocus, 104 | int row, int column) { 105 | Color newColor = (Color)color; 106 | setBackground(newColor); 107 | if (isBordered) { 108 | if (isSelected) { 109 | if (selectedBorder == null) { 110 | selectedBorder = BorderFactory.createMatteBorder(2,5,2,5, 111 | table.getSelectionBackground()); 112 | } 113 | setBorder(selectedBorder); 114 | } else { 115 | if (unselectedBorder == null) { 116 | unselectedBorder = BorderFactory.createMatteBorder(2,5,2,5, 117 | table.getBackground()); 118 | } 119 | setBorder(unselectedBorder); 120 | } 121 | } 122 | 123 | setToolTipText("RGB value: " + newColor.getRed() + ", " 124 | + newColor.getGreen() + ", " 125 | + newColor.getBlue()); 126 | return this; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/ConfigListener.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | public interface ConfigListener { 4 | void configChanged(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/ExcludeEntry.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.util.xmlb.annotations.Tag; 4 | 5 | @Tag("excludeEntry") 6 | public class ExcludeEntry { 7 | private String _excludeName; 8 | private boolean _isEnabled; 9 | 10 | public ExcludeEntry() {} 11 | 12 | public ExcludeEntry(String excludeName, boolean enabled) { 13 | _excludeName = excludeName; 14 | _isEnabled = enabled; 15 | } 16 | 17 | public String getExcludeName() { 18 | return _excludeName; 19 | } 20 | 21 | public void setExcludeName(String excludeName) { 22 | _excludeName = excludeName; 23 | } 24 | 25 | public boolean isEnabled() { 26 | return _isEnabled; 27 | } 28 | 29 | public void setEnabled(boolean enabled) { 30 | _isEnabled = enabled; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/SequenceParamsState.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.components.PersistentStateComponent; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import com.intellij.util.xmlb.annotations.Transient; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @State(name = "sequenceParams", storages = {@Storage("sequencePlugin.xml")}) 16 | public class SequenceParamsState implements PersistentStateComponent { 17 | public int callDepth = 5; 18 | public boolean projectClassesOnly = true; 19 | public boolean noGetterSetters = true; 20 | public boolean noPrivateMethods = false; 21 | public boolean noConstructors = false; 22 | @Deprecated(since = "2.2.0", forRemoval = true) 23 | public boolean smartInterface = false; 24 | 25 | @Transient 26 | private final List _listeners = new ArrayList<>(); 27 | 28 | public SequenceParamsState() { 29 | } 30 | 31 | public static @NotNull SequenceParamsState getInstance() { 32 | return ApplicationManager.getApplication().getService(SequenceParamsState.class); 33 | } 34 | 35 | public void addConfigListener(ConfigListener listener) { 36 | _listeners.add(listener); 37 | } 38 | 39 | public void removeConfigListener(ConfigListener listener) { 40 | _listeners.remove(listener); 41 | } 42 | 43 | public void fireConfigChanged() { 44 | for (ConfigListener configListener : _listeners) { 45 | configListener.configChanged(); 46 | } 47 | } 48 | 49 | @Override 50 | public @Nullable SequenceParamsState getState() { 51 | return this; 52 | } 53 | 54 | @Override 55 | public void loadState(@NotNull SequenceParamsState state) { 56 | XmlSerializerUtil.copyBean(state, this); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/SequenceSettingsConfigurable.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.openapi.options.SearchableConfigurable; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public class SequenceSettingsConfigurable implements SearchableConfigurable { 10 | 11 | private SequenceSettingsComponent _sequenceSettingsComponent; 12 | private SequenceSettingsState sequenceSettingsState; 13 | 14 | @Override 15 | public String getDisplayName() { 16 | return "Sequence Diagram"; 17 | } 18 | 19 | @Override 20 | public String getHelpTopic() { 21 | return null; 22 | } 23 | 24 | @Override 25 | public JComponent createComponent() { 26 | sequenceSettingsState = SequenceSettingsState.getInstance(); 27 | return getForm().getMainPanel(); 28 | } 29 | 30 | @Override 31 | public boolean isModified() { 32 | return getForm().isModified(sequenceSettingsState); 33 | } 34 | 35 | @Override 36 | public void apply() { 37 | getForm().apply(sequenceSettingsState); 38 | fireConfigChanged(); 39 | } 40 | 41 | @Override 42 | public void reset() { 43 | getForm().reset(sequenceSettingsState); 44 | } 45 | 46 | @Override 47 | public void disposeUIResources() { 48 | _sequenceSettingsComponent = null; 49 | } 50 | 51 | private void fireConfigChanged() { 52 | sequenceSettingsState.fireConfigChanged(); 53 | } 54 | 55 | @NotNull 56 | private SequenceSettingsComponent getForm() { 57 | if (_sequenceSettingsComponent == null) { 58 | _sequenceSettingsComponent = new SequenceSettingsComponent(); 59 | } 60 | return _sequenceSettingsComponent; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public String getId() { 66 | return "Settings.Sequence.Configuration"; 67 | } 68 | 69 | @Nullable 70 | @Override 71 | public Runnable enableSearch(String s) { 72 | return null; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/config/SequenceSettingsState.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.config; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.components.PersistentStateComponent; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.ui.JBColor; 8 | import com.intellij.util.xmlb.XmlSerializerUtil; 9 | import com.intellij.util.xmlb.annotations.OptionTag; 10 | import com.intellij.util.xmlb.annotations.Transient; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.awt.*; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Vector; 18 | 19 | @State(name = "sequencePlugin", storages = {@Storage("sequencePlugin.xml")}) 20 | public class SequenceSettingsState implements PersistentStateComponent { 21 | @OptionTag(converter = ColorConverter.class) 22 | public Color CLASS_COLOR = new JBColor(new Color(0xFFFFC0), new Color(0xFFFFC0)); 23 | @OptionTag(converter = ColorConverter.class) 24 | public Color EXTERNAL_CLASS_COLOR = new JBColor(new Color(0xFFD1CE), new Color(0xFFD1CE)); 25 | @OptionTag(converter = ColorConverter.class) 26 | public Color METHOD_BAR_COLOR = new JBColor(new Color(0xFFE0A7), new Color(0xFFE0A7)); 27 | @OptionTag(converter = ColorConverter.class) 28 | public Color SELECTED_METHOD_BAR_COLOR = new JBColor(new Color(0x85C1FF), new Color(0x85C1FF)); 29 | @OptionTag(converter = ColorConverter.class) 30 | public Color INTERFACE_COLOR = new JBColor(new Color(0xCCFACF), new Color(0xCCFACF)); 31 | public boolean USE_3D_VIEW = false; 32 | public boolean USE_ANTIALIASING = true; 33 | public boolean SHOW_RETURN_ARROWS = true; 34 | public boolean SHOW_CALL_NUMBERS = true; 35 | public boolean SHOW_SIMPLIFY_CALL_NAME = true; 36 | public boolean SHOW_LAMBDA_CALL = true; 37 | public boolean SMART_INTERFACE = true; 38 | public String FONT_NAME = "Dialog"; 39 | public int FONT_SIZE = 11; 40 | 41 | @Transient 42 | private final List _listeners = new ArrayList<>(); 43 | private java.util.List _excludeList = new Vector<>(); 44 | private java.util.List _colorMappingList = new Vector<>(); 45 | 46 | public SequenceSettingsState() {} 47 | 48 | public static SequenceSettingsState getInstance() { 49 | return ApplicationManager.getApplication().getService(SequenceSettingsState.class); 50 | } 51 | 52 | public void addConfigListener(ConfigListener listener) { 53 | _listeners.add(listener); 54 | } 55 | 56 | public void removeConfigListener(ConfigListener listener) { 57 | _listeners.remove(listener); 58 | } 59 | 60 | public List getExcludeList() { 61 | return _excludeList; 62 | } 63 | 64 | public void setExcludeList(List excludeList) { 65 | this._excludeList = excludeList; 66 | } 67 | 68 | public List getColorMappingList() { 69 | return _colorMappingList; 70 | } 71 | 72 | public void setColorMappingList(List colorMappingList) { 73 | this._colorMappingList = colorMappingList; 74 | } 75 | 76 | public void fireConfigChanged() { 77 | for (ConfigListener configListener : _listeners) { 78 | configListener.configChanged(); 79 | } 80 | } 81 | 82 | @Nullable 83 | @Override 84 | public SequenceSettingsState getState() { 85 | return this; 86 | } 87 | 88 | @Override 89 | public void loadState(@NotNull SequenceSettingsState sequenceSettingsState) { 90 | XmlSerializerUtil.copyBean(sequenceSettingsState, this); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/Call.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import vanstudio.sequence.config.SequenceSettingsState; 4 | 5 | public class Call extends Link { 6 | 7 | public Call(ObjectInfo from, ObjectInfo to) { 8 | super(from, to); 9 | } 10 | 11 | public String getName() { 12 | if(getMethodInfo() == null) 13 | return super.getName(); 14 | if(SequenceSettingsState.getInstance().SHOW_CALL_NUMBERS) 15 | return getMethodInfo().getNumbering().getName() + ':' + super.getName(); 16 | else 17 | return super.getName(); 18 | } 19 | 20 | public String toString() { 21 | return "Calling " + getName() + " on " + _to + " from " + _from; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/CallReturn.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | public class CallReturn extends Link { 4 | 5 | public CallReturn(ObjectInfo from, ObjectInfo to) { 6 | super(from, to); 7 | } 8 | 9 | public String getName() { 10 | // todo should return methodinfo return type name 11 | return ""; 12 | } 13 | 14 | public String toString() { 15 | return "Returning " + getName() + " on " + _from + " to " + _to; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplayCall.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | 5 | public class DisplayCall extends DisplayLink { 6 | 7 | private static final Stroke DASH_STROKE = new BasicStroke(1.0f, 8 | BasicStroke.CAP_SQUARE, 9 | BasicStroke.JOIN_MITER, 10 | 6.0f, 11 | new float[]{6.0f, 3.0f}, 12 | 0.0f); 13 | 14 | DisplayCall(Link link, DisplayObject from, DisplayObject to, int seq) { 15 | super(link, from, to, seq); 16 | from.addCall(this); 17 | } 18 | 19 | @Override 20 | void drawLine(Graphics2D g2) { 21 | if (_from.getObjectInfo().hasAttribute(Info.INTERFACE_ATTRIBUTE) || _from.getObjectInfo().hasAttribute(Info.ABSTRACT_ATTRIBUTE)) { 22 | Stroke oldStroke = g2.getStroke(); 23 | g2.setStroke(DASH_STROKE); 24 | super.drawLine(g2); 25 | g2.setStroke(oldStroke); 26 | } else { 27 | super.drawLine(g2); 28 | } 29 | } 30 | 31 | 32 | public String toString() { 33 | return "DisplayCall " + _link.getName() + " from " + _from + " to " + _to + " seq " + _seq; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplayCallReturn.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | 5 | public class DisplayCallReturn extends DisplayLink { 6 | 7 | private static final Stroke DASH_STROKE = new BasicStroke(1.0f, 8 | BasicStroke.CAP_SQUARE, 9 | BasicStroke.JOIN_MITER, 10 | 10.0f, 11 | new float[]{10.0f, 5.0f}, 12 | 0.0f); 13 | 14 | DisplayCallReturn(Link link, DisplayObject from, DisplayObject to, int seq) { 15 | super(link, from, to, seq); 16 | from.addCallReturn(this); 17 | } 18 | 19 | void drawText(Graphics2D g2) { 20 | } 21 | 22 | void drawLine(Graphics2D g2) { 23 | Stroke oldStroke = g2.getStroke(); 24 | g2.setStroke(DASH_STROKE); 25 | super.drawLine(g2); 26 | g2.setStroke(oldStroke); 27 | } 28 | 29 | public String toString() { 30 | return "DisplayCallReturn " + _link.getName() + " from <" + _from + "> to <" + _to + "> seq " + _seq; 31 | } 32 | 33 | public boolean isReturnLink() { 34 | return true; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplayLink.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import com.intellij.ui.JBColor; 4 | 5 | import java.awt.*; 6 | import java.awt.geom.GeneralPath; 7 | 8 | public class DisplayLink extends ScreenObject { 9 | private static final Paint TEXT_COLOR = JBColor.foreground(); 10 | private static final Paint LINK_COLOR = JBColor.foreground(); 11 | private static final Stroke DASH_STROKE = new BasicStroke(1.0f, 12 | BasicStroke.CAP_SQUARE, 13 | BasicStroke.JOIN_MITER, 14 | 4.0f, 15 | new float[]{4.0f, 2.0f}, 16 | 0.0f); 17 | 18 | protected Link _link; 19 | protected DisplayObject _from; 20 | protected DisplayObject _to; 21 | protected TextBox _textBox; 22 | int _textXOffset = -1; 23 | 24 | int _y = -1; 25 | int _seq; 26 | 27 | int _lineStartX = -1; 28 | int _lineEndX = -1; 29 | 30 | DisplayLink(Link link, DisplayObject from, DisplayObject to, int seq) { 31 | _from = from; 32 | _to = to; 33 | _seq = seq; 34 | _link = link; 35 | _textBox = new TextBox(link.getName()); 36 | } 37 | 38 | void initOne(Graphics2D g2) { 39 | _textBox.init(g2); 40 | } 41 | 42 | void initTwo() { 43 | if(_from.getSeq() < _to.getSeq()) { 44 | _lineStartX = _from.getRightX(_seq); 45 | _lineEndX = _to.getLeftX(_seq); 46 | } else { 47 | _lineStartX = _from.getLeftX(_seq); 48 | _lineEndX = _to.getRightX(_seq); 49 | } 50 | 51 | int gap = Math.abs(_lineEndX - _lineStartX); 52 | _textXOffset = gap - _textBox.getWidth(); 53 | } 54 | 55 | public Link getLink() { 56 | return _link; 57 | } 58 | 59 | DisplayObject getFrom() { 60 | return _from; 61 | } 62 | 63 | DisplayObject getTo() { 64 | return _to; 65 | } 66 | 67 | public Info getMethodInfo() { 68 | return _link.getMethodInfo(); 69 | } 70 | 71 | int getSeq() { 72 | return _seq; 73 | } 74 | 75 | void setY(int y) { 76 | _y = y; 77 | } 78 | 79 | public int getY() { 80 | return _y; 81 | } 82 | 83 | public int getX() { 84 | return _from.getSeq() <= _to.getSeq()? _lineStartX: _lineEndX; 85 | } 86 | 87 | public int getWidth() { 88 | return Math.max(Math.abs(_lineEndX - _lineStartX), getTextWidth()); 89 | } 90 | 91 | public int getHeight() { 92 | return getEndY() - getY(); 93 | } 94 | 95 | public int getTextWidth() { 96 | return _textBox.getWidth(); 97 | } 98 | 99 | public int getTextHeight() { 100 | return _textBox.getHeight(); 101 | } 102 | 103 | int getLinkHeight() { 104 | return 0; 105 | } 106 | 107 | public String getToolTip() { 108 | return _link.getName(); 109 | } 110 | 111 | boolean isSelfCall() { 112 | return _from.getSeq() == _to.getSeq(); 113 | } 114 | 115 | public void paint(Graphics2D g2) { 116 | Rectangle clipBounds = g2.getClipBounds(); 117 | if(clipBounds != null && !clipBounds.intersects(getX(), getY(), getWidth(), getHeight())) 118 | return; 119 | Stroke oldStroke = g2.getStroke(); 120 | if(isSelected()) 121 | g2.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 122 | drawText(g2); 123 | drawLine(g2); 124 | if (isReturnLink()) { 125 | drawArrow(g2); 126 | } else { 127 | fillArrow(g2); 128 | } 129 | g2.setStroke(oldStroke); 130 | } 131 | 132 | void drawText(Graphics2D g2) { 133 | g2.setPaint(TEXT_COLOR); 134 | int textX = getX() + _textXOffset + _textBox.getPad(); 135 | int textY = getY() + _textBox.getTextOffset(); 136 | if(isSelected()) { 137 | Stroke oldStroke = g2.getStroke(); 138 | g2.setStroke(DASH_STROKE); 139 | g2.drawRect(textX - 2, getY() + _textBox.getPad(), 140 | _textBox.getRealWidth() + 2, _textBox.getRealHeight() + 2); 141 | g2.setStroke(oldStroke); 142 | } 143 | Font oldFont = g2.getFont(); 144 | if(_link.getMethodInfo().hasAttribute(Info.ABSTRACT_ATTRIBUTE)) 145 | g2.setFont(new Font(oldFont.getFontName(), Font.ITALIC, oldFont.getSize())); 146 | g2.drawString(_link.getName(), textX, textY); 147 | if(_link.getMethodInfo().hasAttribute(Info.STATIC_ATTRIBUTE)) { 148 | int y = textY + g2.getFontMetrics().getDescent() - 1; 149 | g2.drawLine(textX, y, textX + _textBox.getRealWidth(), y); 150 | } 151 | g2.setFont(oldFont); 152 | } 153 | 154 | void drawLine(Graphics2D g2) { 155 | g2.setPaint(LINK_COLOR); 156 | g2.drawLine(_lineStartX, getEndY(), _lineEndX, getEndY()); 157 | } 158 | 159 | protected int getEndY() { 160 | int lineY = getY() + _textBox.getHeight(); 161 | if(isSelected()) 162 | lineY++; 163 | return lineY; 164 | } 165 | 166 | void drawArrow(Graphics2D g2) { 167 | int arrowTailX = _lineEndX; 168 | 169 | if(_lineStartX < _lineEndX) 170 | arrowTailX -= 4; 171 | else 172 | arrowTailX += 4; 173 | 174 | g2.drawLine(arrowTailX, getEndY() - 3, _lineEndX, getEndY()); 175 | g2.drawLine(arrowTailX, getEndY() + 3, _lineEndX, getEndY()); 176 | } 177 | 178 | void fillArrow(Graphics2D g2) { 179 | int arrowTailX = _lineEndX; 180 | 181 | if(_lineStartX < _lineEndX) 182 | arrowTailX -= 4; 183 | else 184 | arrowTailX += 4; 185 | 186 | int[] xPoints = {arrowTailX, _lineEndX, arrowTailX}; 187 | int[] yPoints = {getEndY() - 3, getEndY(), getEndY() + 3}; 188 | fillPolygon(g2, xPoints, yPoints); 189 | } 190 | 191 | protected void fillPolygon(Graphics2D g2, int[] xPoints, int[] yPoints) { 192 | GeneralPath filledPolygon = new GeneralPath(); 193 | filledPolygon.moveTo(xPoints[0], yPoints[0]); 194 | for (int idx = 1; idx < xPoints.length; idx ++ ) { 195 | filledPolygon.lineTo(xPoints[idx], yPoints[idx]); 196 | } 197 | filledPolygon.closePath(); 198 | g2.fill(filledPolygon); 199 | } 200 | 201 | public boolean isReturnLink() { 202 | return false; 203 | } 204 | } 205 | 206 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplayMethod.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import com.intellij.ui.JBColor; 5 | import vanstudio.sequence.config.SequenceSettingsState; 6 | 7 | import java.awt.*; 8 | 9 | public class DisplayMethod extends ScreenObject { 10 | 11 | private static final Logger LOGGER = Logger.getInstance(DisplayMethod.class); 12 | 13 | private static final Paint LINE_COLOR = JBColor.foreground(); 14 | private static final Paint SHADOW_COLOR = JBColor.LIGHT_GRAY; 15 | 16 | private ObjectInfo _objectInfo; 17 | private MethodInfo _methodInfo; 18 | private DisplayLink _call; 19 | private DisplayLink _callReturn; 20 | private int _horizontalSeq; 21 | 22 | DisplayMethod(ObjectInfo objectInfo, MethodInfo methodInfo, 23 | DisplayLink call, DisplayLink callReturn) { 24 | _objectInfo = objectInfo; 25 | _methodInfo = methodInfo; 26 | _call = call; 27 | _callReturn = callReturn; 28 | } 29 | 30 | void setHorizontalSeq(int horizontalSeq) { 31 | if(LOGGER.isDebugEnabled()) 32 | LOGGER.debug("DisplayMethod setHorizontalSeq(" + horizontalSeq + ")"); 33 | _horizontalSeq = horizontalSeq; 34 | } 35 | 36 | public MethodInfo getMethodInfo() { 37 | return _methodInfo; 38 | } 39 | 40 | int getStartSeq() { 41 | return _call.getSeq(); 42 | } 43 | 44 | int getEndSeq() { 45 | return _callReturn.getSeq(); 46 | } 47 | 48 | public ObjectInfo getObjectInfo() { 49 | return _objectInfo; 50 | } 51 | 52 | public String getToolTip() { 53 | return _methodInfo.getHtmlDescription(); 54 | } 55 | 56 | public void paint(Graphics2D g2) { 57 | SequenceSettingsState sequenceSettingsState = SequenceSettingsState.getInstance(); 58 | if(sequenceSettingsState.USE_3D_VIEW) { 59 | g2.setPaint(SHADOW_COLOR); 60 | g2.fillRect(getX() + 2, getY() + 2, getWidth(), getHeight()); 61 | } 62 | g2.setPaint(isSelected() ? sequenceSettingsState.SELECTED_METHOD_BAR_COLOR : sequenceSettingsState.METHOD_BAR_COLOR); 63 | g2.fillRect(getX(), getY(), getWidth(), getHeight()); 64 | g2.setPaint(LINE_COLOR); 65 | g2.drawRect(getX(), getY(), getWidth() - 1, getHeight() - 1); 66 | } 67 | 68 | public int getHeight() { 69 | return _callReturn.getY() + _callReturn.getTextHeight() - getY(); 70 | } 71 | 72 | public int getWidth() { 73 | return 9; 74 | } 75 | 76 | public int getY() { 77 | return _call.getY() + _call.getTextHeight(); 78 | } 79 | 80 | public int getX() { 81 | return _call.getTo().getCenterX() - 4 + (3 * _horizontalSeq); 82 | } 83 | 84 | public String toString() { 85 | return "DisplayMethod call <" + _call + "> return <" + _callReturn + ">"; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplaySelfCall.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | public class DisplaySelfCall extends DisplaySelfLink { 4 | 5 | DisplaySelfCall(Link link, DisplayObject from, DisplayObject to, int seq) { 6 | super(link, from, to, seq); 7 | from.addCall(this); 8 | } 9 | 10 | public String toString() { 11 | return "DisplaySelfCall " + _link.getName() + " from " + _from + " to " + _to + " seq " + _seq; 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplaySelfCallReturn.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | 5 | /** 6 | * Displays self call return 7 | */ 8 | public class DisplaySelfCallReturn extends DisplaySelfLink { 9 | 10 | private static final Stroke DASH_STROKE = new BasicStroke(1.0f, 11 | BasicStroke.CAP_SQUARE, 12 | BasicStroke.JOIN_MITER, 13 | 10.0f, 14 | new float[]{10.0f, 5.0f}, 15 | 0.0f); 16 | 17 | DisplaySelfCallReturn(Link link, DisplayObject from, DisplayObject to, int seq) { 18 | super(link, from, to, seq); 19 | from.addCallReturn(this); 20 | } 21 | 22 | void drawText(Graphics2D g2) { 23 | } 24 | 25 | void drawLine(Graphics2D g2) { 26 | Stroke oldStroke = g2.getStroke(); 27 | g2.setStroke(DASH_STROKE); 28 | super.drawLine(g2); 29 | g2.setStroke(oldStroke); 30 | } 31 | 32 | public boolean isReturnLink() { 33 | return true; 34 | } 35 | 36 | public String toString() { 37 | return "DisplaySelfCallReturn " + _link.getName() + " from <" + _from + "> to <" + _to + "> seq " + _seq; 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/DisplaySelfLink.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | 5 | public class DisplaySelfLink extends DisplayLink { 6 | 7 | DisplaySelfLink(Link link, DisplayObject from, DisplayObject to, int seq) { 8 | super(link, from, to, seq); 9 | } 10 | 11 | void initTwo() { 12 | 13 | _lineStartX = _from.getRightX(_seq); 14 | _lineEndX = _lineStartX + _textBox.getWidth(); 15 | 16 | int gap = _lineEndX - _lineStartX; 17 | _textXOffset = (gap - _textBox.getWidth()); 18 | _lineEndX = (_lineEndX - _lineStartX)/2 < 10? _lineEndX: (_lineEndX - _lineStartX)/2 + _lineStartX; 19 | } 20 | 21 | int getLinkHeight() { 22 | return getTextHeight() / 3; 23 | } 24 | 25 | void drawLine(Graphics2D g2) { 26 | super.drawLine(g2); 27 | g2.drawLine(_lineEndX, getEndY(), _lineEndX, getEndY() + getLinkHeight()); 28 | g2.drawLine(_lineStartX, getEndY() + getLinkHeight(), _lineEndX, getEndY() + getLinkHeight()); 29 | } 30 | 31 | void drawArrow(Graphics2D g2) { 32 | int lineY = getEndY() + getLinkHeight(); 33 | int arrowTailX = _lineStartX + 4; 34 | g2.drawLine(arrowTailX, lineY - 3, _lineStartX, lineY); 35 | g2.drawLine(arrowTailX, lineY + 3, _lineStartX, lineY); 36 | } 37 | 38 | @Override 39 | void fillArrow(Graphics2D g2) { 40 | int lineY = getEndY() + getLinkHeight(); 41 | int arrowTailX = _lineStartX + 4; 42 | 43 | int[] xPoints = {arrowTailX, _lineStartX, arrowTailX}; 44 | int[] yPoints = {lineY - 3, lineY, lineY + 3}; 45 | fillPolygon(g2, xPoints, yPoints); 46 | } 47 | 48 | public String toString() { 49 | return "DisplaySelfLink " + _link.getName() + " from " + _from + " to " + _to + " seq " + _seq; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/Info.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | 6 | public class Info { 7 | public static final String PRIVATE_ATTRIBUTE = "private"; 8 | public static final String PROTECTED_ATTRIBUTE = "protected"; 9 | public static final String PUBLIC_ATTRIBUTE = "public"; 10 | public static final String PACKAGE_LOCAL_ATTRIBUTE = "packageLocal"; 11 | public static final String ABSTRACT_ATTRIBUTE = "abstract"; 12 | public static final String NATIVE_ATTRIBUTE = "native"; 13 | public static final String SYNCHRONIZED_ATTRIBUTE = "synchronized"; 14 | public static final String STATIC_ATTRIBUTE = "static"; 15 | public static final String FINAL_ATTRIBUTE = "final"; 16 | public static final String STRICTFP_ATTRIBUTE = "strictfp"; 17 | 18 | public static final String[] RECOGNIZED_METHOD_ATTRIBUTES = 19 | { 20 | PRIVATE_ATTRIBUTE, PUBLIC_ATTRIBUTE, PROTECTED_ATTRIBUTE, 21 | ABSTRACT_ATTRIBUTE, SYNCHRONIZED_ATTRIBUTE, FINAL_ATTRIBUTE, 22 | NATIVE_ATTRIBUTE, STATIC_ATTRIBUTE, PACKAGE_LOCAL_ATTRIBUTE, 23 | STRICTFP_ATTRIBUTE 24 | }; 25 | 26 | public static final String EXTERNAL_ATTRIBUTE = "external"; 27 | /** 28 | * Indicate a class is an interface. 29 | */ 30 | public static final String INTERFACE_ATTRIBUTE = "interface"; 31 | 32 | protected List _attributes; 33 | 34 | public Info(List attributes) { 35 | _attributes = attributes; 36 | } 37 | 38 | public boolean hasAttribute(String attribute) { 39 | return _attributes.contains(attribute); 40 | } 41 | 42 | protected String getAttributesStr() { 43 | StringBuilder buffer = new StringBuilder(); 44 | for(Iterator iterator = _attributes.iterator(); iterator.hasNext();) { 45 | String attribute = iterator.next(); 46 | buffer.append(attribute); 47 | if(iterator.hasNext()) 48 | buffer.append(", "); 49 | } 50 | return buffer.toString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/LambdaExprInfo.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * © fanhuagang@gmail.com 7 | * Created by van on 2020/3/23. 8 | */ 9 | public class LambdaExprInfo extends MethodInfo { 10 | private final String _enclosedMethodName; 11 | private final List _enclosedMethodArgTypes; 12 | 13 | public LambdaExprInfo(ObjectInfo obj, Numbering numbering, List attributes, 14 | String method, String returnType, List argNames, List argTypes, 15 | int startSeq, int endSeq, 16 | String enclosedMethodName, List enclosedMethodArgTypes) { 17 | super(obj, numbering, attributes, method, returnType, argNames, argTypes, startSeq, endSeq); 18 | this._enclosedMethodName = enclosedMethodName; 19 | this._enclosedMethodArgTypes = enclosedMethodArgTypes; 20 | } 21 | 22 | public String getEnclosedMethodName() { 23 | return _enclosedMethodName; 24 | } 25 | 26 | public List getEnclosedMethodArgTypes() { 27 | return _enclosedMethodArgTypes; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/Link.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import vanstudio.sequence.config.SequenceSettingsState; 4 | 5 | public class Link { 6 | 7 | protected ObjectInfo _from; 8 | protected ObjectInfo _to; 9 | protected MethodInfo _methodInfo; 10 | protected MethodInfo _callerMethodInfo; 11 | protected int _seq; 12 | 13 | public Link(ObjectInfo from, ObjectInfo to) { 14 | _from = from; 15 | _to = to; 16 | } 17 | 18 | public String getName() { 19 | if (_methodInfo == null) 20 | return ""; 21 | if (SequenceSettingsState.getInstance().SHOW_SIMPLIFY_CALL_NAME) 22 | return _methodInfo.getName(); 23 | else 24 | return _methodInfo.getFullName(); 25 | } 26 | 27 | public MethodInfo getMethodInfo() { 28 | return _methodInfo; 29 | } 30 | 31 | public void setMethodInfo(MethodInfo methodInfo) { 32 | _methodInfo = methodInfo; 33 | } 34 | 35 | public MethodInfo getCallerMethodInfo() { 36 | return _callerMethodInfo; 37 | } 38 | 39 | public void setCallerMethodInfo(MethodInfo callerMethodInfo) { 40 | _callerMethodInfo = callerMethodInfo; 41 | } 42 | 43 | public void setVerticalSeq(int y) { 44 | _seq = y; 45 | } 46 | 47 | public int getVerticalSeq() { 48 | return _seq; 49 | } 50 | 51 | public ObjectInfo getFrom() { 52 | return _from; 53 | } 54 | 55 | public ObjectInfo getTo() { 56 | return _to; 57 | } 58 | 59 | public boolean isBootstrap() { 60 | return _from.isActor() || _to.isActor(); 61 | } 62 | 63 | public String toString() { 64 | return "Link from " + _from + " to " + _to; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import vanstudio.sequence.openapi.Constants; 4 | import vanstudio.sequence.openapi.model.GenericType; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.List; 8 | 9 | public class MethodInfo extends Info { 10 | 11 | private final ObjectInfo _objectInfo; 12 | private final Numbering _numbering; 13 | private final String _name; 14 | private String _returnType; 15 | private final List _argNames; 16 | private final List _argTypes; 17 | private final int _startSeq; 18 | private final int _endSeq; 19 | private String _htmlDescription; 20 | 21 | public MethodInfo(ObjectInfo obj, Numbering numbering, List attributes, 22 | String method, String returnType, List argNames, List argTypes, 23 | int startSeq, int endSeq) { 24 | super(attributes); 25 | _objectInfo = obj; 26 | _numbering = numbering; 27 | _name = method; 28 | _returnType = returnType; 29 | if(_returnType == null) 30 | _returnType = "void"; 31 | _argNames = argNames; 32 | _argTypes = argTypes; 33 | _startSeq = startSeq; 34 | _endSeq = endSeq; 35 | } 36 | 37 | public String getName() { 38 | return Constants.CONSTRUCTOR_METHOD_NAME.equals(_name)? "<>" : _name; 39 | } 40 | 41 | public String getRealName() { 42 | return Constants.CONSTRUCTOR_METHOD_NAME.equals(_name)? _objectInfo.getName() : _name; 43 | } 44 | 45 | public String getFullName() { 46 | String name = Constants.CONSTRUCTOR_METHOD_NAME.equals(_name) ? "<>" : _name; 47 | StringBuilder sb = new StringBuilder(); 48 | sb.append(name).append("("); 49 | for (int i = 0; i < _argNames.size(); i++) { 50 | if (i > 0) sb.append(", "); 51 | String argName = _argNames.get(i); 52 | String argType = _argTypes.get(i); 53 | argType = shortTypeName(argType); 54 | sb.append(argName).append(": ").append(argType); 55 | } 56 | sb.append(")").append(": ").append(shortTypeName(_returnType)); 57 | return sb.toString(); 58 | } 59 | 60 | @NotNull 61 | private String shortTypeName(String argType) { 62 | GenericType genericType = GenericType.create(argType); 63 | return genericType.getName(); 64 | } 65 | 66 | public ObjectInfo getObjectInfo() { 67 | return _objectInfo; 68 | } 69 | 70 | public Numbering getNumbering() { 71 | return _numbering; 72 | } 73 | 74 | public String getHtmlName() { 75 | return Constants.CONSTRUCTOR_METHOD_NAME.equals(_name)? "<constructor>": _name; 76 | } 77 | 78 | public List getArgNames() { 79 | return _argNames; 80 | } 81 | 82 | public List getArgTypes() { 83 | return _argTypes; 84 | } 85 | 86 | public String getReturnType() { 87 | return _returnType; 88 | } 89 | 90 | public int getStartSeq() { 91 | return _startSeq; 92 | } 93 | 94 | public int getEndSeq() { 95 | return _endSeq; 96 | } 97 | 98 | public String getHtmlDescription() { 99 | if(_htmlDescription == null) { 100 | StringBuffer buffer = new StringBuffer(); 101 | 102 | buffer.append(""); 103 | 104 | appendTitleValue(buffer, "Class", _objectInfo.getName()); 105 | appendTitleValue(buffer, "Package", _objectInfo.getHtmlPackageName()); 106 | appendTitleValue(buffer, "Method", getHtmlName()); 107 | appendTitleValue(buffer, "Returns", _returnType); 108 | if(_attributes.size() != 0) { 109 | appendTitleValue(buffer, "Modifiers", getAttributesStr()); 110 | } 111 | appendTitleValue(buffer, "No", getNumbering().getName()); 112 | 113 | buffer.append(""); 116 | for(int i = 0; i < getArgNames().size(); i++) { 117 | appendArgValue(buffer, getArgNames().get(i), 118 | getArgTypes().get(i)); 119 | } 120 | } 121 | else 122 | buffer.append(" None"); 123 | buffer.append("
Arguments:"); 114 | if(!getArgNames().isEmpty()) { 115 | buffer.append("
"); 124 | _htmlDescription = buffer.toString(); 125 | } 126 | return _htmlDescription; 127 | } 128 | 129 | private void appendArgValue(StringBuffer buffer, String argName, String argType) { 130 | buffer.append(""); 131 | buffer.append("").append(argName).append(""); 132 | appendValue(buffer, argType); 133 | buffer.append(""); 134 | 135 | } 136 | 137 | private void appendTitleValue(StringBuffer buffer, String title, String value) { 138 | buffer.append(""); 139 | appendTitle(buffer, title); 140 | appendValue(buffer, value); 141 | buffer.append(""); 142 | } 143 | 144 | private void appendTitle(StringBuffer buffer, String title) { 145 | buffer.append("").append(title).append(":"); 146 | } 147 | 148 | private void appendValue(StringBuffer buffer, String value) { 149 | String htmlValue = value.replace("\u003c","<") 150 | .replace("\u003e",">").replace(",", ",
"); 151 | buffer.append("").append(htmlValue).append(""); 152 | } 153 | 154 | 155 | public String toString() { 156 | return "Method " + _name + " on " + _objectInfo + " from " + _startSeq + " to " + _endSeq; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/Model.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | 4 | import com.intellij.openapi.diagnostic.Logger; 5 | 6 | import javax.swing.event.EventListenerList; 7 | import javax.swing.event.SwingPropertyChangeSupport; 8 | import java.beans.PropertyChangeListener; 9 | import java.io.*; 10 | 11 | public class Model { 12 | 13 | private static final Logger LOGGER = Logger.getInstance(Model.class); 14 | 15 | private String _queryString = ""; 16 | 17 | private SwingPropertyChangeSupport _changeSupport; 18 | 19 | private File _file = null; 20 | private boolean _modified = false; 21 | 22 | private final EventListenerList _listenerList = new EventListenerList(); 23 | 24 | public Model() { 25 | _changeSupport = new SwingPropertyChangeSupport(this); 26 | } 27 | 28 | public void addPropertyChangeListener(String propName, PropertyChangeListener listener) { 29 | _changeSupport.addPropertyChangeListener(propName, listener); 30 | } 31 | 32 | public void removePropertyChangeListener(String propName, PropertyChangeListener listener) { 33 | _changeSupport.removePropertyChangeListener(propName, listener); 34 | } 35 | 36 | public boolean loadNew() { 37 | setFile(null); 38 | internalSetText("", this); 39 | setModified(false); 40 | return true; 41 | } 42 | 43 | public boolean readFromFile(File f) { 44 | try (BufferedReader br = new BufferedReader(new FileReader(f))) { 45 | StringBuilder sb = new StringBuilder(1024); 46 | 47 | String s; 48 | while ((s = br.readLine()) != null) { 49 | sb.append(s); 50 | sb.append("\n"); 51 | } 52 | setFile(f); 53 | internalSetText(sb.toString(), this); 54 | setModified(false); 55 | return true; 56 | } catch (IOException ioe) { 57 | ioe.printStackTrace(); 58 | return false; 59 | } 60 | } 61 | 62 | public boolean writeToFile(File f) { 63 | try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f)))){ 64 | 65 | BufferedReader br = new BufferedReader(new StringReader(getText())); 66 | String s; 67 | while ((s = br.readLine()) != null) { 68 | out.println(s); 69 | } 70 | setFile(f); 71 | setModified(false); 72 | return true; 73 | } catch (IOException ioe) { 74 | ioe.printStackTrace(); 75 | return false; 76 | } 77 | } 78 | 79 | public boolean isModified() { 80 | return _modified; 81 | } 82 | 83 | public void setModified(boolean modified) { 84 | boolean oldModified = _modified; 85 | _modified = modified; 86 | if (LOGGER.isDebugEnabled()) 87 | LOGGER.debug("setModified(...) oldModified " + oldModified + " modified " + modified); 88 | if (modified != oldModified) 89 | _changeSupport.firePropertyChange("modified", oldModified, modified); 90 | } 91 | 92 | private void setFile(File f) { 93 | File oldFile = _file; 94 | _file = f; 95 | _changeSupport.firePropertyChange("file", oldFile, _file); 96 | } 97 | 98 | public String getText() { 99 | return _queryString; 100 | } 101 | 102 | public void setText(String s, Object setter) { 103 | internalSetText(s, setter); 104 | setModified(true); 105 | } 106 | 107 | private void internalSetText(String s, Object setter) { 108 | _queryString = s; 109 | fireModelTextEvent(s, setter); 110 | } 111 | 112 | public File getFile() { 113 | return _file; 114 | } 115 | 116 | public void addModelTextListener(ModelTextListener l) { 117 | _listenerList.add(ModelTextListener.class, l); 118 | } 119 | 120 | public void removeModelTextListener(ModelTextListener l) { 121 | _listenerList.remove(ModelTextListener.class, l); 122 | } 123 | 124 | private synchronized void fireModelTextEvent(String s, Object setter) { 125 | ModelTextEvent mte = new ModelTextEvent(setter, s); 126 | Object[] listeners = _listenerList.getListenerList(); 127 | for (int i = listeners.length - 2; i >= 0; i -= 2) { 128 | if (listeners[i] == ModelTextListener.class) 129 | ((ModelTextListener) listeners[i + 1]).modelTextChanged(mte); 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/ModelTextEvent.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.util.EventObject; 4 | 5 | public class ModelTextEvent extends EventObject { 6 | 7 | private String _text = null; 8 | 9 | ModelTextEvent(Object source, String text) { 10 | super(source); 11 | _text = text; 12 | } 13 | 14 | public String getText() { 15 | return _text; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/ModelTextListener.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.util.EventListener; 4 | 5 | public interface ModelTextListener extends EventListener { 6 | 7 | public void modelTextChanged(ModelTextEvent mte); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/Numbering.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | public class Numbering { 8 | private List _numbers; 9 | 10 | private Numbering(List numbers) { 11 | _numbers = numbers; 12 | } 13 | 14 | public Numbering(Numbering fromNumbering) { 15 | _numbers = fromNumbering != null? new ArrayList<>(fromNumbering._numbers): new ArrayList<>(); 16 | } 17 | 18 | public int level() { 19 | return _numbers.size(); 20 | } 21 | 22 | public void addNewLevel() { 23 | _numbers.add(1); 24 | } 25 | 26 | public void incrementLevel(int level) { 27 | _numbers.set(level, _numbers.get(level) + 1); 28 | } 29 | 30 | public String getName() { 31 | StringBuilder buffer = new StringBuilder(); 32 | for(Iterator iterator = _numbers.iterator(); iterator.hasNext();) { 33 | Integer number = iterator.next(); 34 | buffer.append(number); 35 | if(iterator.hasNext()) 36 | buffer.append('.'); 37 | } 38 | return buffer.toString(); 39 | } 40 | 41 | public int getTopLevel() { 42 | if(_numbers.isEmpty()) 43 | return 0; 44 | return _numbers.get(_numbers.size() - 1); 45 | } 46 | 47 | public Numbering getPreviousNumbering() { 48 | if(_numbers.size() < 2) 49 | return null; 50 | List numbers = new ArrayList<>(_numbers); 51 | numbers.remove(_numbers.size() - 1); 52 | return new Numbering(numbers); 53 | } 54 | 55 | public boolean equals(Object o) { 56 | if(this == o) return true; 57 | if(!(o instanceof Numbering)) return false; 58 | 59 | final Numbering numbering = (Numbering)o; 60 | 61 | return _numbers.equals(numbering._numbers); 62 | } 63 | 64 | public int hashCode() { 65 | return _numbers.hashCode(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/ObjectInfo.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | 4 | import com.intellij.openapi.diagnostic.Logger; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ObjectInfo extends Info { 10 | private static final Logger LOGGER = Logger.getInstance(ObjectInfo.class); 11 | public static final String ACTOR_NAME = "Actor"; 12 | 13 | private final String _name; 14 | private final String _fullName; 15 | private final int _seq; 16 | private final List _methods = new ArrayList<>(); 17 | 18 | public ObjectInfo(String name, List attributes, int seq) { 19 | super(attributes); 20 | _name = name.substring(name.lastIndexOf('.') + 1); 21 | _fullName = name; 22 | _seq = seq; 23 | } 24 | 25 | public void addMethod(MethodInfo mi) { 26 | if(LOGGER.isDebugEnabled()) 27 | LOGGER.debug("addMethod(" + mi + ")"); 28 | int possible = -1; 29 | for(int i = 0; i < _methods.size(); ++i) { 30 | MethodInfo otherMethod = _methods.get(i); 31 | if(otherMethod.getStartSeq() > mi.getStartSeq()) { 32 | possible = i; 33 | break; 34 | } 35 | } 36 | if(possible == -1) 37 | _methods.add(mi); 38 | else 39 | _methods.add(possible, mi); 40 | } 41 | 42 | public List getMethods() { 43 | return _methods; 44 | } 45 | 46 | public String getName() { 47 | return _name; 48 | } 49 | 50 | public String getFullName() { 51 | return _fullName; 52 | } 53 | 54 | public String getHtmlPackageName() { 55 | int index = _fullName.lastIndexOf('.'); 56 | return index != -1 ? _fullName.substring(0, index) : "<default>"; 57 | } 58 | 59 | public int getSeq() { 60 | return _seq; 61 | } 62 | 63 | public String toString() { 64 | return "Object " + _name + " seq " + _seq; 65 | } 66 | 67 | public int hashCode() { 68 | return _name.hashCode(); 69 | } 70 | 71 | public boolean equals(Object o) { 72 | return _name.equals(((ObjectInfo)o)._name); 73 | } 74 | 75 | public boolean isActor() { 76 | return _name.equals(ACTOR_NAME); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/PreviewFrame.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.FocusEvent; 6 | import java.awt.event.FocusListener; 7 | 8 | public class PreviewFrame extends JDialog { 9 | private static final int INIT_WIDTH = 200; 10 | private static final int INIT_HEIGHT = 150; 11 | public PreviewFrame(JScrollPane scrollPane, Display display) throws HeadlessException { 12 | super(findFrame(scrollPane)); 13 | setUndecorated(true); 14 | setResizable(true); 15 | getContentPane().add(new PreviewPanel(scrollPane, display)); 16 | Point point = new Point(scrollPane.getWidth(), scrollPane.getHeight()); 17 | SwingUtilities.convertPointToScreen(point, scrollPane); 18 | setBounds((int)point.getX() - INIT_WIDTH, (int)point.getY() - INIT_HEIGHT, 19 | INIT_WIDTH, INIT_HEIGHT); 20 | addFocusListener(new DisposeFocusListener()); 21 | } 22 | 23 | private static Frame findFrame(JComponent jComponent) { 24 | Frame frame = (Frame)SwingUtilities.getAncestorOfClass(Frame.class, jComponent); 25 | if(frame == null) 26 | frame = JOptionPane.getRootFrame(); 27 | return frame; 28 | } 29 | 30 | private class DisposeFocusListener implements FocusListener { 31 | public void focusGained(FocusEvent e) { 32 | 33 | } 34 | 35 | public void focusLost(FocusEvent e) { 36 | dispose(); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/PreviewPanel.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import com.intellij.ui.JBColor; 4 | import com.intellij.util.ui.ImageUtil; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.MouseAdapter; 9 | import java.awt.event.MouseEvent; 10 | import java.awt.event.MouseMotionAdapter; 11 | import java.awt.geom.AffineTransform; 12 | import java.awt.image.BufferedImage; 13 | 14 | public class PreviewPanel extends JPanel { 15 | private final JScrollPane _scrollPane; 16 | private final Display _display; 17 | private BufferedImage _image; 18 | private double _xScale; 19 | private double _yScale; 20 | 21 | public PreviewPanel(JScrollPane scrollPane, Display display) { 22 | super(false); 23 | _scrollPane = scrollPane; 24 | _display = display; 25 | setBorder(BorderFactory.createCompoundBorder( 26 | BorderFactory.createRaisedBevelBorder(), 27 | BorderFactory.createEmptyBorder(2, 2, 2, 2))); 28 | addMouseListener(new MouseAdapter() { 29 | public void mousePressed(MouseEvent e) { 30 | moveViewport(e); 31 | } 32 | 33 | public void mouseClicked(MouseEvent e) { 34 | moveViewport(e); 35 | } 36 | }); 37 | addMouseMotionListener(new MouseMotionAdapter() { 38 | public void mouseDragged(MouseEvent e) { 39 | moveViewport(e); 40 | } 41 | }); 42 | } 43 | 44 | private void updateImage() { 45 | Dimension displaySize = _display.getFullSize(); 46 | _xScale = (double)getWidth()/displaySize.width; 47 | _yScale = (double)getHeight()/displaySize.height; 48 | if(_xScale > 1.) 49 | _xScale = 1.; 50 | if(_yScale > 1.) 51 | _yScale = 1.; 52 | // _xScale = _yScale = Math.min(_xScale, _yScale); 53 | _image = ImageUtil.createImage(getWidth(), getHeight(), BufferedImage.TYPE_USHORT_555_RGB); 54 | Graphics2D g2 = _image.createGraphics(); 55 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 56 | g2.setColor(JBColor.WHITE); 57 | g2.fillRect(0, 0, getWidth(), getHeight()); 58 | AffineTransform at = AffineTransform.getScaleInstance(_xScale, _yScale); 59 | g2.setTransform(at); 60 | _display.paintComponentWithHeader(g2); 61 | } 62 | 63 | private void moveViewport(MouseEvent e) { 64 | int newX = (int)Math.round(e.getX() / _xScale); 65 | int newY = (int)Math.round(e.getY() / _yScale); 66 | 67 | int eW = _scrollPane.getViewport().getExtentSize().width; 68 | int eH = _scrollPane.getViewport().getExtentSize().height; 69 | newX -= eW / 2; 70 | newY -= eH / 2; 71 | 72 | newX = Math.max(newX, 0); 73 | newY = Math.max(newY, 0); 74 | 75 | Dimension fullSize = _display.getFullSize(); 76 | newX = Math.min(newX, fullSize.width - eW); 77 | newY = Math.min(newY, fullSize.height - eH); 78 | 79 | if(eW >= fullSize.width) 80 | newX = 0; 81 | if(eH >= fullSize.height) 82 | newY = 0; 83 | 84 | Point newPos = new Point(newX, newY); 85 | _scrollPane.getViewport().setViewPosition(newPos); 86 | repaint(); 87 | } 88 | 89 | protected void paintComponent(Graphics g) { 90 | super.paintComponent(g); 91 | if(_image == null) 92 | updateImage(); 93 | Graphics2D g2 = (Graphics2D)g; 94 | g2.drawImage(_image, 0, 0, this); 95 | Rectangle viewRect = _scrollPane.getViewport().getViewRect(); 96 | int x = (int)Math.round(viewRect.x * _xScale) + 2; 97 | int y = (int)Math.round(viewRect.y * _yScale) + 2; 98 | int width = (int)Math.round(viewRect.width * _xScale); 99 | if(width > getWidth()) 100 | width = getWidth(); 101 | if(width > 6) width -= 6; 102 | int height = (int)Math.round(viewRect.height * _yScale); 103 | if(height > getHeight()) 104 | height = getHeight(); 105 | if(height > 6) height -= 6; 106 | g2.drawRect(x, y, width, height); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/ScreenObject.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | 5 | public abstract class ScreenObject { 6 | 7 | private boolean _selected; 8 | 9 | public abstract String getToolTip(); 10 | 11 | public abstract int getX(); 12 | 13 | public abstract int getY(); 14 | 15 | public abstract int getWidth(); 16 | 17 | public abstract int getHeight(); 18 | 19 | public boolean isInRange(int x, int y) { 20 | return getX() <= x && x <= getX() + getWidth() && 21 | getY() <= y && y <= getY() + getHeight(); 22 | } 23 | 24 | public boolean isSelected() { 25 | return _selected; 26 | } 27 | 28 | public void setSelected(boolean selected) { 29 | this._selected = selected; 30 | } 31 | 32 | public abstract void paint(Graphics2D g2); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/SequenceListener.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | public interface SequenceListener { 4 | void selectedScreenObject(ScreenObject screenObject); 5 | 6 | void displayMenuForScreenObject(ScreenObject screenObject, int x, int y); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/TextBox.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram; 2 | 3 | import java.awt.*; 4 | import java.awt.geom.Rectangle2D; 5 | 6 | class TextBox { 7 | 8 | private final int _pad = 5; 9 | 10 | private String _text = null; 11 | private int _boxWidth = -1; 12 | private int _boxHeight = -1; 13 | private int _textOffset = -1; 14 | private Rectangle2D _rect; 15 | 16 | TextBox(String text) { 17 | _text = text; 18 | } 19 | 20 | public int getPad() { 21 | return _pad; 22 | } 23 | 24 | public int getWidth() { 25 | return _boxWidth; 26 | } 27 | 28 | public int getHeight() { 29 | return _boxHeight; 30 | } 31 | 32 | public int getTextOffset() { 33 | return _textOffset; 34 | } 35 | 36 | public int getRealWidth() { 37 | return (int)_rect.getWidth(); 38 | } 39 | 40 | public int getRealHeight() { 41 | return (int)_rect.getHeight(); 42 | } 43 | 44 | void init(Graphics2D g2) { 45 | FontMetrics fm = g2.getFontMetrics(); 46 | int height = fm.getMaxAscent() + fm.getMaxDescent(); 47 | 48 | _rect = fm.getStringBounds(_text, g2); 49 | 50 | _boxHeight = height + (_pad * 2); 51 | _boxWidth = (int)_rect.getWidth() + (_pad * 2); 52 | _textOffset = fm.getMaxAscent() + _pad; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/Editor.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import vanstudio.sequence.diagram.Model; 5 | import vanstudio.sequence.diagram.ModelTextEvent; 6 | import vanstudio.sequence.diagram.ModelTextListener; 7 | 8 | import javax.swing.*; 9 | import javax.swing.event.DocumentEvent; 10 | import javax.swing.event.DocumentListener; 11 | import javax.swing.text.BadLocationException; 12 | import javax.swing.text.DefaultEditorKit; 13 | import javax.swing.text.Document; 14 | import java.awt.*; 15 | import java.net.URL; 16 | import java.util.ResourceBundle; 17 | 18 | public class Editor extends JPanel implements DocumentListener, ModelTextListener { 19 | 20 | private static final Logger LOGGER = Logger.getInstance(Editor.class); 21 | 22 | private Model _model = null; 23 | private final JEditorPane _editPane = new JEditorPane(); 24 | 25 | private Action _cutAction = null; 26 | private Action _copyAction = null; 27 | private Action _pasteAction = null; 28 | 29 | private ResourceBundle _bundle = null; 30 | 31 | private boolean _ignoreChange = false; 32 | private boolean _documentChanged = false; 33 | private long _lastChangeTime = 0; 34 | 35 | public Editor(Model model) { 36 | _model = model; 37 | setLayout(new BorderLayout()); 38 | _editPane.setFont(new Font("Monospaced", 39 | Font.PLAIN, 40 | _editPane.getFont().getSize() + 1)); 41 | add(_editPane, BorderLayout.CENTER); 42 | 43 | _bundle = ResourceBundle.getBundle("org.intellij.sequencer.diagram.app.Sequence"); 44 | 45 | _editPane.setText(model.getText()); 46 | _editPane.getDocument().addDocumentListener(this); 47 | new Thread(new ChangeNotifier()).start(); 48 | } 49 | 50 | public synchronized Action getCutAction() { 51 | if(_cutAction == null) { 52 | _cutAction = new DefaultEditorKit.CutAction(); 53 | initAction(_cutAction, "CutAction"); 54 | } 55 | return _cutAction; 56 | } 57 | 58 | public synchronized Action getPasteAction() { 59 | if(_pasteAction == null) { 60 | _pasteAction = new DefaultEditorKit.PasteAction(); 61 | initAction(_pasteAction, "PasteAction"); 62 | } 63 | return _pasteAction; 64 | } 65 | 66 | public synchronized Action getCopyAction() { 67 | if(_copyAction == null) { 68 | _copyAction = new DefaultEditorKit.CopyAction(); 69 | initAction(_copyAction, "CopyAction"); 70 | } 71 | return _copyAction; 72 | } 73 | 74 | private void initAction(Action act, String resourcePrefix) { 75 | act.putValue(Action.NAME, 76 | getResource(resourcePrefix, "name")); 77 | act.putValue(Action.SHORT_DESCRIPTION, 78 | getResource(resourcePrefix, "shortDesc")); 79 | act.putValue(Action.SMALL_ICON, 80 | getIcon(resourcePrefix, "icon")); 81 | } 82 | 83 | String getResource(String resourcePrefix, String key) { 84 | return _bundle.getString(resourcePrefix + "." + key); 85 | } 86 | 87 | ImageIcon getIcon(String resourcePrefix, String key) { 88 | URL iconURL = 89 | ClassLoader.getSystemResource(getResource(resourcePrefix, key)); 90 | return new ImageIcon(iconURL, key); 91 | } 92 | 93 | public void modelTextChanged(ModelTextEvent mte) { 94 | if(mte.getSource() == this) 95 | return; 96 | 97 | if(LOGGER.isDebugEnabled()) 98 | LOGGER.debug("modelTextChanged(...) changing"); 99 | 100 | Document doc = _editPane.getDocument(); 101 | try { 102 | _ignoreChange = true; 103 | doc.remove(0, doc.getLength()); 104 | doc.insertString(0, mte.getText(), null); 105 | } catch(BadLocationException ble) { 106 | Sequence.getInstance().exception(ble); 107 | } finally { 108 | _ignoreChange = false; 109 | } 110 | } 111 | 112 | private void documentChanged(DocumentEvent e) { 113 | if(LOGGER.isDebugEnabled()) 114 | LOGGER.debug("documentChanged(...) ignoreChange " + _ignoreChange); 115 | if(_ignoreChange) 116 | return; 117 | _documentChanged = true; 118 | _lastChangeTime = System.currentTimeMillis(); 119 | } 120 | 121 | public void changedUpdate(DocumentEvent e) { 122 | documentChanged(e); 123 | } 124 | 125 | public void insertUpdate(DocumentEvent e) { 126 | documentChanged(e); 127 | } 128 | 129 | public void removeUpdate(DocumentEvent e) { 130 | documentChanged(e); 131 | } 132 | 133 | private class ChangeNotifier 134 | implements Runnable { 135 | 136 | public void run() { 137 | while(true) { 138 | synchronized(Editor.this) { 139 | if(_documentChanged) { 140 | if((System.currentTimeMillis() - _lastChangeTime) > 500) { 141 | try { 142 | Document doc = _editPane.getDocument(); 143 | String s = doc.getText(0, doc.getLength()); 144 | _model.setText(s, Editor.this); 145 | _documentChanged = false; 146 | } catch(BadLocationException ble) { 147 | Sequence.getInstance().exception(ble); 148 | } 149 | } 150 | } 151 | } 152 | try { 153 | Thread.sleep(250); 154 | } catch(InterruptedException ie) { 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ExampleAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | 5 | public class ExampleAction extends ModifiedConfirmAction { 6 | 7 | public ExampleAction(Model model) { 8 | super("ExampleAction", model, true); 9 | } 10 | 11 | public boolean doIt() { 12 | getModel().setText(getResource("exampleModelText"), this); 13 | getModel().setModified(false); 14 | return true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ExitAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | 5 | public class ExitAction extends ModifiedConfirmAction { 6 | 7 | public ExitAction(Model model) { 8 | super("ExitAction", model, false); 9 | } 10 | 11 | public boolean doIt() { 12 | System.exit(0); 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ExportAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Display; 4 | import vanstudio.sequence.diagram.app.Sequence; 5 | 6 | import javax.swing.*; 7 | import java.awt.event.ActionEvent; 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | public class ExportAction extends SequenceAction { 12 | 13 | private final Display _display; 14 | 15 | public ExportAction(Display display) { 16 | super("ExportAction", true); 17 | _display = display; 18 | } 19 | 20 | public void actionPerformed(ActionEvent e) { 21 | final JFileChooser chooser = new JFileChooser(); 22 | chooser.setDialogType(JFileChooser.SAVE_DIALOG); 23 | chooser.setDialogTitle(getResource("dialogTitle")); 24 | 25 | int returnVal = chooser.showOpenDialog(Sequence.getInstance()); 26 | if(returnVal == JFileChooser.APPROVE_OPTION) { 27 | export(chooser.getSelectedFile()); 28 | } 29 | } 30 | 31 | private void export(File file) { 32 | try { 33 | _display.saveImageToSvgFile(file, "svg"); 34 | } catch(IOException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ModelAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | 5 | import java.awt.event.ActionEvent; 6 | 7 | public abstract class ModelAction extends SequenceAction { 8 | 9 | private Model _model = null; 10 | 11 | ModelAction(String resourcePrefix, Model model) { 12 | this(resourcePrefix, model, true); 13 | } 14 | 15 | ModelAction(String resourcePrefix, Model model, boolean withIcon) { 16 | super(resourcePrefix, withIcon); 17 | _model = model; 18 | } 19 | 20 | protected Model getModel() { 21 | return _model; 22 | } 23 | 24 | public void actionPerformed(ActionEvent e) { 25 | doIt(); 26 | } 27 | 28 | public abstract boolean doIt(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ModifiedConfirmAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | import vanstudio.sequence.diagram.app.Sequence; 5 | 6 | import javax.swing.*; 7 | import java.awt.event.ActionEvent; 8 | 9 | public abstract class ModifiedConfirmAction extends ModelAction { 10 | 11 | protected ModifiedConfirmAction(String resourcePrefix, Model model, boolean withIcon) { 12 | super(resourcePrefix, model, withIcon); 13 | } 14 | 15 | public void actionPerformed(ActionEvent e) { 16 | if(confirmed()) 17 | doIt(); 18 | } 19 | 20 | boolean confirmed() { 21 | if(getModel().isModified()) { 22 | int ret = JOptionPane.showConfirmDialog(Sequence.getInstance(), 23 | getResource("confirmMessage"), 24 | getResource("confirmTitle"), 25 | JOptionPane.YES_NO_CANCEL_OPTION); 26 | if(ret == JOptionPane.CANCEL_OPTION) 27 | return false; 28 | if(ret == JOptionPane.YES_OPTION) 29 | // todo 30 | // if (!getModel().getSaveAction().doIt()) 31 | return false; 32 | } 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/ModifiedEnabledAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import vanstudio.sequence.diagram.Model; 5 | 6 | import java.beans.PropertyChangeEvent; 7 | import java.beans.PropertyChangeListener; 8 | 9 | public abstract class ModifiedEnabledAction extends ModelAction implements PropertyChangeListener { 10 | 11 | private static final Logger LOGGER = Logger.getInstance(ModifiedEnabledAction.class); 12 | 13 | ModifiedEnabledAction(String resourcePrefix, Model model) { 14 | super(resourcePrefix, model); 15 | setEnabled(false); 16 | model.addPropertyChangeListener("modified", this); 17 | } 18 | 19 | public void propertyChange(PropertyChangeEvent evt) { 20 | if(!evt.getPropertyName().equals("modified")) 21 | return; 22 | Boolean modified = (Boolean)evt.getNewValue(); 23 | if(LOGGER.isDebugEnabled()) 24 | LOGGER.debug("propertyChange(...) modified is " + 25 | modified); 26 | setEnabled(modified); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/NewAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | 5 | public class NewAction extends ModifiedConfirmAction { 6 | 7 | public NewAction(Model model) { 8 | super("NewAction", model, true); 9 | } 10 | 11 | public boolean doIt() { 12 | getModel().loadNew(); 13 | return true; 14 | } 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/OpenAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | import vanstudio.sequence.diagram.app.Sequence; 5 | 6 | import javax.swing.*; 7 | import java.io.File; 8 | 9 | public class OpenAction extends ModifiedConfirmAction { 10 | 11 | public OpenAction(Model model) { 12 | super("OpenAction", model, true); 13 | } 14 | 15 | public boolean doIt() { 16 | final JFileChooser chooser = new JFileChooser(); 17 | chooser.setDialogType(JFileChooser.OPEN_DIALOG); 18 | chooser.setDialogTitle(getResource("dialogTitle")); 19 | 20 | int returnVal = chooser.showOpenDialog(Sequence.getInstance()); 21 | if(returnVal == JFileChooser.APPROVE_OPTION) { 22 | File file = chooser.getSelectedFile(); 23 | return getModel().readFromFile(file); 24 | } else { 25 | return false; 26 | } 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/SaveAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | 5 | public class SaveAction extends ModifiedEnabledAction { 6 | 7 | public SaveAction(Model model) { 8 | super("SaveAction", model); 9 | } 10 | 11 | public boolean doIt() { 12 | if(getModel().getFile() != null) { 13 | return getModel().writeToFile(getModel().getFile()); 14 | } else { 15 | // todo 16 | return false; //getModel().getSaveAsAction().doIt(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/SaveAsAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import vanstudio.sequence.diagram.Model; 4 | import vanstudio.sequence.diagram.app.Sequence; 5 | 6 | import javax.swing.*; 7 | import java.io.File; 8 | 9 | public class SaveAsAction extends ModifiedEnabledAction { 10 | 11 | public SaveAsAction(Model model) { 12 | super("SaveAsAction", model); 13 | } 14 | 15 | public boolean doIt() { 16 | final JFileChooser chooser = new JFileChooser(); 17 | chooser.setDialogType(JFileChooser.SAVE_DIALOG); 18 | chooser.setDialogTitle(getResource("dialogTitle")); 19 | 20 | int returnVal = chooser.showSaveDialog(Sequence.getInstance()); 21 | if(returnVal == JFileChooser.APPROVE_OPTION) { 22 | File file = chooser.getSelectedFile(); 23 | return getModel().writeToFile(file); 24 | } else { 25 | return false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/diagram/app/actions/SequenceAction.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.diagram.app.actions; 2 | 3 | import javax.swing.*; 4 | import java.net.URL; 5 | import java.util.ResourceBundle; 6 | 7 | public abstract class SequenceAction extends AbstractAction { 8 | 9 | private ResourceBundle _bundle = null; 10 | private String _resourcePrefix = null; 11 | 12 | protected SequenceAction(String resourcePrefix) { 13 | this(resourcePrefix, true); 14 | } 15 | 16 | protected SequenceAction(String resourcePrefix, boolean withIcon) { 17 | 18 | this._resourcePrefix = resourcePrefix; 19 | _bundle = ResourceBundle.getBundle("org.intellij.sequencer.diagram.app.Sequence"); 20 | 21 | putValue(NAME, getResource("name")); 22 | 23 | putValue(SHORT_DESCRIPTION, getResource("shortDesc")); 24 | 25 | if(withIcon) 26 | putValue(SMALL_ICON, getIcon("icon")); 27 | } 28 | 29 | protected String getResource(String key) { 30 | return _bundle.getString(_resourcePrefix + "." + key); 31 | } 32 | 33 | protected ImageIcon getIcon(String key) { 34 | URL iconURL = ClassLoader.getSystemResource(getResource(key)); 35 | return new ImageIcon(iconURL, key); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/formatter/IFormatter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.formatter; 2 | 3 | import vanstudio.sequence.openapi.model.CallStack; 4 | 5 | public interface IFormatter { 6 | String format(CallStack callStack); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/formatter/MermaidFormatter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.formatter; 2 | 3 | import org.apache.commons.lang.StringEscapeUtils; 4 | import vanstudio.sequence.config.SequenceSettingsState; 5 | import vanstudio.sequence.openapi.model.CallStack; 6 | import vanstudio.sequence.openapi.model.MethodDescription; 7 | 8 | /** 9 | * Generate Mermaid Sequence Diagram format. 10 | * 11 | * Mermaid 12 | */ 13 | public class MermaidFormatter implements IFormatter{ 14 | @Override 15 | public String format(CallStack callStack) { 16 | StringBuffer buffer = new StringBuffer(); 17 | buffer.append("sequenceDiagram").append('\n'); 18 | buffer.append("actor User").append('\n'); 19 | String classA = callStack.getMethod().getClassDescription().getClassShortName(); 20 | String method = getMethodName(callStack.getMethod()); 21 | buffer.append("User").append(" ->> ").append(classA).append(" : ").append(escape(method)).append('\n'); 22 | buffer.append("activate ").append(classA).append('\n'); 23 | generate(buffer, callStack); 24 | buffer.append("deactivate ").append(classA).append('\n'); 25 | // buffer.append("@enduml"); 26 | return buffer.toString(); 27 | } 28 | 29 | private void generate(StringBuffer buffer, CallStack parent) { 30 | String classA = parent.getMethod().getClassDescription().getClassShortName(); 31 | 32 | for (CallStack callStack : parent.getCalls()) { 33 | String classB = callStack.getMethod().getClassDescription().getClassShortName(); 34 | String method = getMethodName(callStack.getMethod()); 35 | buffer.append(classA).append(" ->> ").append(classB).append(" : ").append(escape(method)).append('\n'); 36 | buffer.append("activate ").append(classB).append('\n'); 37 | generate(buffer, callStack); 38 | buffer.append(classB).append(" -->> ").append(classA).append(" : #32; ").append('\n'); 39 | buffer.append("deactivate ").append(classB).append('\n'); 40 | } 41 | 42 | } 43 | 44 | private String getMethodName(MethodDescription method) { 45 | if (method == null) return ""; 46 | 47 | if (SequenceSettingsState.getInstance().SHOW_SIMPLIFY_CALL_NAME) { 48 | return method.getMethodName(); 49 | } else { 50 | return method.getFullName(); 51 | } 52 | 53 | } 54 | 55 | private String escape(String method) { 56 | return StringEscapeUtils.escapeHtml(method); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/formatter/PlantUMLFormatter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.formatter; 2 | 3 | import vanstudio.sequence.openapi.Constants; 4 | import vanstudio.sequence.config.SequenceSettingsState; 5 | import vanstudio.sequence.openapi.model.CallStack; 6 | import vanstudio.sequence.openapi.model.MethodDescription; 7 | 8 | /** 9 | * Generate PlantUml sequence diagram format. 10 | * 11 | */ 12 | public class PlantUMLFormatter implements IFormatter{ 13 | @Override 14 | public String format(CallStack callStack) { 15 | StringBuffer buffer = new StringBuffer(); 16 | buffer.append("@startuml").append('\n'); 17 | buffer.append("participant Actor").append('\n'); 18 | String classA = callStack.getMethod().getClassDescription().getClassShortName(); 19 | String method = getMethodName(callStack.getMethod()); 20 | if (Constants.CONSTRUCTOR_METHOD_NAME.equals(callStack.getMethod().getMethodName())) { 21 | buffer.append("create ").append(classA).append('\n'); 22 | } 23 | buffer.append("Actor").append(" -> ").append(classA).append(" : ").append(method).append('\n'); 24 | buffer.append("activate ").append(classA).append('\n'); 25 | generate(buffer, callStack); 26 | buffer.append("return").append('\n'); 27 | buffer.append("@enduml"); 28 | return buffer.toString(); 29 | } 30 | 31 | private void generate(StringBuffer buffer, CallStack parent) { 32 | String classA = parent.getMethod().getClassDescription().getClassShortName(); 33 | 34 | for (CallStack callStack : parent.getCalls()) { 35 | String classB = callStack.getMethod().getClassDescription().getClassShortName(); 36 | String method = getMethodName(callStack.getMethod()); 37 | if (Constants.CONSTRUCTOR_METHOD_NAME.equals(callStack.getMethod().getMethodName())) { 38 | buffer.append("create ").append(classB).append('\n'); 39 | } 40 | buffer.append(classA).append(" -> ").append(classB).append(" : ").append(method).append('\n'); 41 | buffer.append("activate ").append(classB).append('\n'); 42 | generate(buffer, callStack); 43 | buffer.append(classB).append(" --> ").append(classA).append('\n'); 44 | buffer.append("deactivate ").append(classB).append('\n'); 45 | } 46 | 47 | } 48 | 49 | private String getMethodName(MethodDescription method) { 50 | if (method == null) return ""; 51 | 52 | if (SequenceSettingsState.getInstance().SHOW_SIMPLIFY_CALL_NAME) { 53 | return method.getMethodName(); 54 | } else { 55 | return method.getFullName(); 56 | } 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/formatter/SdtFormatter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.formatter; 2 | 3 | import vanstudio.sequence.openapi.model.CallStack; 4 | 5 | public class SdtFormatter implements IFormatter{ 6 | @Override 7 | public String format(CallStack callStack) { 8 | StringBuffer buffer = new StringBuffer(); 9 | generate(buffer, callStack); 10 | return buffer.toString(); 11 | } 12 | 13 | private void generate(StringBuffer buffer, CallStack parent) { 14 | buffer.append('(').append('\n').append(parent.getMethod().toJson()).append('\n'); 15 | for (CallStack callStack : parent.getCalls()) { 16 | generate(buffer, callStack); 17 | } 18 | buffer.append(')').append('\n'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/EmptySequenceNavigable.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator; 2 | 3 | import vanstudio.sequence.openapi.SequenceNavigable; 4 | 5 | import java.util.List; 6 | 7 | public class EmptySequenceNavigable implements SequenceNavigable { 8 | @Override 9 | public void openClassInEditor(String className) { 10 | 11 | } 12 | 13 | @Override 14 | public void openMethodInEditor(String className, String methodName, List argTypes) { 15 | 16 | } 17 | 18 | @Override 19 | public boolean isInsideAMethod() { 20 | return false; 21 | } 22 | 23 | @Override 24 | public void openMethodCallInEditor(String fromClass, String fromMethod, List fromArgTypes, String toClass, String toMethod, List toArgType, int offset) { 25 | 26 | } 27 | 28 | @Override 29 | public List findImplementations(String className) { 30 | return null; 31 | } 32 | 33 | @Override 34 | public List findImplementations(String className, String methodName, List argTypes) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public void openLambdaExprInEditor(String fromClass, String fromMethod, List fromArgTypes, List argTypes, String returnType, int integer) { 40 | 41 | } 42 | 43 | @Override 44 | public void openMethodCallInsideLambdaExprInEditor(String fromClass, String enclosedMethodName, List enclosedMethodArgTypes, List argTypes, String returnType, String toClass, String toMethod, List toArgTypes, int offset) { 45 | 46 | } 47 | 48 | @Override 49 | public String[] findSuperClass(String className) { 50 | return new String[0]; 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/JavaActionFinder.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.actionSystem.ActionGroup; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.psi.*; 9 | import com.intellij.psi.util.PsiTreeUtil; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | import vanstudio.sequence.openapi.ActionFinder; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.Collection; 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | 20 | public class JavaActionFinder implements ActionFinder { 21 | @NotNull 22 | @Override 23 | public AnAction[] find(@NotNull Project project, @NotNull PsiElement element, Task task) { 24 | 25 | if (element instanceof PsiClass) { 26 | return getActions((PsiClass) element, task); 27 | } 28 | 29 | final Collection psiClassCollection = PsiTreeUtil.findChildrenOfType(element, PsiClass.class); 30 | psiClassCollection.removeIf(psiClass -> psiClass instanceof PsiTypeParameter); 31 | 32 | 33 | ArrayList list = new ArrayList<>(); 34 | 35 | if (psiClassCollection.size() > 1) { 36 | 37 | for (PsiClass psiClass : psiClassCollection) { 38 | ActionGroup group = new ActionGroup(psiClass.getName(), psiClass.getQualifiedName(), AllIcons.Nodes.Class) { 39 | @NotNull 40 | @Override 41 | public AnAction[] getChildren(@Nullable AnActionEvent e) { 42 | return getActions(psiClass, task); 43 | } 44 | }; 45 | group.setPopup(true); 46 | list.add(group); 47 | 48 | } 49 | } else { 50 | for (PsiClass psiClass : psiClassCollection) { 51 | list.addAll(List.of(getActions(psiClass, task))); 52 | } 53 | } 54 | 55 | return list.toArray(new AnAction[0]); 56 | } 57 | 58 | private AnAction[] getActions(PsiClass psiClass, Task task) { 59 | PsiMethod[] methods = psiClass.getMethods(); 60 | ArrayList subList = new ArrayList<>(); 61 | 62 | for (PsiMethod method : methods) { 63 | subList.add(new AnAction(formatMethod(method), "Generate sequence " + method.getName() , AllIcons.Nodes.Method) { 64 | @Override 65 | public void actionPerformed(@NotNull AnActionEvent e) { 66 | Project project = e.getProject(); 67 | if (project == null) return; 68 | 69 | task.run(method, project); 70 | } 71 | }); 72 | } 73 | return subList.toArray(new AnAction[0]); 74 | } 75 | 76 | private String formatMethod(PsiMethod method) { 77 | String s = method.getName(); 78 | PsiParameter[] parameters = method.getParameterList().getParameters(); 79 | if (parameters.length > 0) 80 | s = s + "(" + Arrays.stream(parameters).map(p -> p.getType().getPresentableText()).collect(Collectors.joining(",")) + ")"; 81 | else 82 | s += "()"; 83 | if (method.getReturnType() != null) 84 | s = s + ": " + method.getReturnType().getPresentableText(); 85 | return s; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/JavaElementTypeFinder.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator; 2 | 3 | import com.intellij.psi.PsiClass; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiMethod; 6 | import vanstudio.sequence.openapi.ElementTypeFinder; 7 | 8 | public class JavaElementTypeFinder implements ElementTypeFinder { 9 | @Override 10 | public Class findMethod() { 11 | return (Class) PsiMethod.class; 12 | } 13 | 14 | @Override 15 | public Class findClass() { 16 | return (Class) PsiClass.class; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/JavaGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator; 2 | 3 | import vanstudio.sequence.config.ExcludeEntry; 4 | import vanstudio.sequence.config.SequenceParamsState; 5 | import vanstudio.sequence.config.SequenceSettingsState; 6 | import vanstudio.sequence.generator.filters.NoConstructorsFilter; 7 | import vanstudio.sequence.generator.filters.NoGetterSetterFilter; 8 | import vanstudio.sequence.generator.filters.NoPrivateMethodsFilter; 9 | import vanstudio.sequence.generator.filters.SingleClassFilter; 10 | import vanstudio.sequence.openapi.GeneratorFactory; 11 | import vanstudio.sequence.openapi.IGenerator; 12 | import vanstudio.sequence.openapi.SequenceParams; 13 | import vanstudio.sequence.openapi.filters.PackageFilter; 14 | import vanstudio.sequence.openapi.filters.ProjectOnlyFilter; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.List; 18 | 19 | public class JavaGeneratorFactory extends GeneratorFactory { 20 | @NotNull 21 | @Override 22 | public IGenerator getGenerator(@NotNull SequenceParams params) { 23 | return new SequenceGenerator(params); 24 | } 25 | 26 | @Override 27 | @NotNull 28 | public IGenerator getGenerator(@NotNull SequenceParams params, int offset) { 29 | return new SequenceGenerator(params, offset); 30 | } 31 | 32 | @Override 33 | public void loadParams(@NotNull SequenceParams params) { 34 | SequenceParamsState state = SequenceParamsState.getInstance(); 35 | 36 | params.setMaxDepth(state.callDepth); 37 | // params.setSmartInterface(state.smartInterface); 38 | params.getMethodFilter().addFilter(new ProjectOnlyFilter(state.projectClassesOnly)); 39 | params.getMethodFilter().addFilter(new NoGetterSetterFilter(state.noGetterSetters)); 40 | params.getMethodFilter().addFilter(new NoPrivateMethodsFilter(state.noPrivateMethods)); 41 | params.getMethodFilter().addFilter(new NoConstructorsFilter(state.noConstructors)); 42 | 43 | List excludeList = SequenceSettingsState.getInstance().getExcludeList(); 44 | for (ExcludeEntry excludeEntry : excludeList) { 45 | if (!excludeEntry.isEnabled()) 46 | continue; 47 | String excludeName = excludeEntry.getExcludeName(); 48 | if (excludeName.endsWith(SequenceParams.PACKAGE_INDICATOR)) { 49 | int index = excludeName.lastIndexOf(SequenceParams.PACKAGE_INDICATOR); 50 | params.getMethodFilter().addFilter(new PackageFilter(excludeName.substring(0, index))); 51 | } else if (excludeName.endsWith(SequenceParams.RECURSIVE_PACKAGE_INDICATOR)) { 52 | int index = excludeName.lastIndexOf(SequenceParams.RECURSIVE_PACKAGE_INDICATOR); 53 | params.getMethodFilter().addFilter(new PackageFilter(excludeName.substring(0, index), true)); 54 | } else 55 | params.getMethodFilter().addFilter(new SingleClassFilter(excludeName)); 56 | } 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/ImplementClassFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiMethod; 5 | import vanstudio.sequence.openapi.filters.MethodFilter; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * The implement class should be included. 11 | *

12 | * © fanhuagang@gmail.com 13 | * Created by van on 18/10/2016. 14 | */ 15 | public class ImplementClassFilter implements MethodFilter { 16 | 17 | private final String[] classNames; 18 | 19 | public ImplementClassFilter(String... className) { 20 | classNames = className; 21 | } 22 | 23 | @Override 24 | public boolean allow(PsiElement psiElement) { 25 | if (psiElement instanceof PsiMethod) { 26 | PsiMethod psiMethod = (PsiMethod) psiElement; 27 | return psiMethod.getContainingClass() != null 28 | && psiMethod.getContainingClass().getQualifiedName() != null 29 | && Arrays.stream(classNames).anyMatch(s -> s.equals(psiMethod.getContainingClass().getQualifiedName())); 30 | } 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/NoConstructorsFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiMethod; 5 | import vanstudio.sequence.openapi.filters.MethodFilter; 6 | 7 | /** 8 | * Exclude constructors. 9 | */ 10 | public class NoConstructorsFilter implements MethodFilter { 11 | private final boolean _noConstructors; 12 | 13 | public NoConstructorsFilter(boolean noConstructors) { 14 | _noConstructors = noConstructors; 15 | } 16 | 17 | public boolean allow(PsiElement psiElement) { 18 | if(_noConstructors 19 | && (psiElement instanceof PsiMethod) 20 | && ((PsiMethod) psiElement).isConstructor()) 21 | return false; 22 | return true; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) return true; 28 | return o instanceof NoConstructorsFilter; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/NoGetterSetterFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiMethod; 5 | import com.intellij.psi.util.PropertyUtil; 6 | import vanstudio.sequence.openapi.filters.MethodFilter; 7 | 8 | /** 9 | * Exclude getter/setter method. 10 | */ 11 | public class NoGetterSetterFilter implements MethodFilter { 12 | private final boolean _noGetterSetters; 13 | 14 | public NoGetterSetterFilter(boolean noGetterSetters) { 15 | _noGetterSetters = noGetterSetters; 16 | } 17 | 18 | public boolean allow(PsiElement psiElement) { 19 | if(_noGetterSetters 20 | && (psiElement instanceof PsiMethod) 21 | && isGetterSetter((PsiMethod) psiElement)) 22 | return false; 23 | return true; 24 | } 25 | 26 | private boolean isGetterSetter(PsiMethod psiMethod) { 27 | return PropertyUtil.isSimplePropertyGetter(psiMethod) || 28 | PropertyUtil.isSimplePropertySetter(psiMethod); 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) return true; 34 | return o instanceof NoGetterSetterFilter; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/NoPrivateMethodsFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiMethod; 5 | import vanstudio.sequence.openapi.filters.MethodFilter; 6 | 7 | /** 8 | * Exclude private method. 9 | */ 10 | public class NoPrivateMethodsFilter implements MethodFilter { 11 | private final boolean _noPrivateMethods; 12 | 13 | public NoPrivateMethodsFilter(boolean noPrivateMethods) { 14 | _noPrivateMethods = noPrivateMethods; 15 | } 16 | 17 | public boolean allow(PsiElement psiElement) { 18 | if(_noPrivateMethods 19 | && (psiElement instanceof PsiMethod) 20 | && isPrivateMethod((PsiMethod) psiElement)) 21 | return false; 22 | return true; 23 | } 24 | 25 | private boolean isPrivateMethod(PsiMethod psiMethod) { 26 | return psiMethod.getModifierList().hasModifierProperty("private"); 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) return true; 32 | return o instanceof NoPrivateMethodsFilter; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/SingleClassFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiMethod; 5 | import vanstudio.sequence.openapi.Constants; 6 | import vanstudio.sequence.openapi.filters.MethodFilter; 7 | 8 | import java.util.Objects; 9 | 10 | /** 11 | * The class should be excluded. 12 | */ 13 | public class SingleClassFilter implements MethodFilter { 14 | private final String _className; 15 | 16 | public SingleClassFilter(String className) { 17 | _className = className; 18 | } 19 | 20 | public boolean allow(PsiElement psiElement) { 21 | if (psiElement instanceof PsiMethod) { 22 | 23 | PsiMethod psiMethod = (PsiMethod) psiElement; 24 | if ( 25 | /* method in Anonymous class*/ 26 | _className.equals(Constants.ANONYMOUS_CLASS_NAME) 27 | && (psiMethod.getContainingClass() == null || psiMethod.getContainingClass().getQualifiedName() == null) 28 | || 29 | /* or method's classname is same as filtered classname */ 30 | psiMethod.getContainingClass() != null 31 | && psiMethod.getContainingClass().getQualifiedName() != null 32 | && _className.equals(psiMethod.getContainingClass().getQualifiedName()) 33 | ) 34 | return false; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (!(o instanceof SingleClassFilter)) return false; 44 | SingleClassFilter that = (SingleClassFilter) o; 45 | return _className.equals(that._className); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(_className); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/generator/filters/SingleMethodFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator.filters; 2 | 3 | import com.intellij.psi.PsiClass; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiMethod; 6 | import vanstudio.sequence.openapi.Constants; 7 | import vanstudio.sequence.openapi.filters.MethodFilter; 8 | import vanstudio.sequence.util.MyPsiUtil; 9 | 10 | import java.util.List; 11 | import java.util.Objects; 12 | 13 | /** 14 | * The method should be excluded. 15 | */ 16 | public class SingleMethodFilter implements MethodFilter { 17 | private final String _className; 18 | private final String _methodName; 19 | private final List _argTypes; 20 | 21 | public SingleMethodFilter(String className, String methodName, List argTypes) { 22 | _className = className; 23 | _methodName = methodName; 24 | _argTypes = argTypes; 25 | } 26 | 27 | public boolean allow(PsiElement psiElement) { 28 | if (psiElement instanceof PsiMethod) { 29 | PsiMethod psiMethod = (PsiMethod) psiElement; 30 | 31 | PsiClass containingClass = psiMethod.getContainingClass(); 32 | if (isSameClass(containingClass) && MyPsiUtil.isMethod(psiMethod, _methodName, _argTypes)) 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | private boolean isSameClass(PsiClass containingClass) { 39 | return _className.equals(Constants.ANONYMOUS_CLASS_NAME) && containingClass.getQualifiedName() == null || 40 | _className.equals(containingClass.getQualifiedName()); 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (!(o instanceof SingleMethodFilter)) return false; 47 | SingleMethodFilter that = (SingleMethodFilter) o; 48 | return _className.equals(that._className) && _methodName.equals(that._methodName) && _argTypes.equals(that._argTypes); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(_className, _methodName, _argTypes); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/impl/SequenceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.impl; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.wm.ToolWindow; 5 | import com.intellij.openapi.wm.ToolWindowManager; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.ui.content.Content; 8 | import com.intellij.ui.content.ContentManager; 9 | import org.jetbrains.annotations.NotNull; 10 | import vanstudio.sequence.SequencePanel; 11 | import vanstudio.sequence.SequenceService; 12 | 13 | /** 14 | * © fanhuagang@gmail.com 15 | * Created by van on 2020/2/23. 16 | */ 17 | public class SequenceServiceImpl implements SequenceService { 18 | 19 | private final Project _project; 20 | private final ToolWindow _toolWindow; 21 | 22 | public SequenceServiceImpl(Project project) { 23 | _project = project; 24 | _toolWindow = ToolWindowManager.getInstance(_project).getToolWindow(PLUGIN_NAME); 25 | 26 | } 27 | 28 | @Override 29 | public void showSequence(@NotNull PsiElement psiElement) { 30 | 31 | final SequencePanel sequencePanel = new SequencePanel(_project, psiElement); 32 | final Content content = addSequencePanel(sequencePanel); 33 | // register callback when generate finished, update the content title. 34 | sequencePanel.withFinishedListener(content::setDisplayName); 35 | 36 | Runnable postAction = sequencePanel::generate; 37 | if (_toolWindow.isActive()) 38 | _toolWindow.show(postAction); 39 | else 40 | _toolWindow.activate(postAction); 41 | } 42 | 43 | private Content addSequencePanel(final SequencePanel sequencePanel) { 44 | ContentManager contentManager = _toolWindow.getContentManager(); 45 | final Content content = contentManager.getFactory().createContent(sequencePanel, sequencePanel.getTitleName(), false); 46 | contentManager.addContent(content); 47 | contentManager.setSelectedContent(content); 48 | return content; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/ActionFinder.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.lang.LanguageExtension; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.Arrays; 12 | 13 | public interface ActionFinder { 14 | static String[] UAST_Language = new String[] { 15 | "JAVA", "kotlin", "Groovy", "Scala" 16 | }; 17 | @NotNull 18 | LanguageExtension EP_NAME = new LanguageExtension<>("SequenceDiagram.actionFinder"); 19 | static boolean isValid(@NotNull Language language) { 20 | boolean match = Arrays.stream(UAST_Language).anyMatch(p -> p.equals(language.getID())); 21 | 22 | return match || EP_NAME.forLanguage(language) != null; 23 | } 24 | 25 | @Nullable 26 | static ActionFinder getInstance(@NotNull Language language) { 27 | 28 | return EP_NAME.forLanguage(language); 29 | } 30 | 31 | AnAction[] find(@NotNull Project project, @NotNull PsiElement element, Task task); 32 | 33 | /** 34 | * {@code Task.run} should be called in {@code AnAction.actionPerformed} 35 | */ 36 | @FunctionalInterface 37 | interface Task { 38 | void run(@NotNull PsiElement element, @NotNull Project project); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/Constants.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | public interface Constants { 4 | String ANONYMOUS_CLASS_NAME = "Anonymous"; 5 | String CONSTRUCTOR_METHOD_NAME = "new"; 6 | String Lambda_Invoke = "() ->"; 7 | String TOP_LEVEL_FUN = "Global"; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/ElementTypeFinder.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import com.intellij.lang.LanguageExtension; 4 | import com.intellij.psi.PsiElement; 5 | 6 | public interface ElementTypeFinder { 7 | LanguageExtension EP_NAME = new LanguageExtension<>("SequenceDiagram.typeFinder"); 8 | 9 | Class findMethod(); 10 | 11 | Class findClass(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/GenerateFinishedListener.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | public interface GenerateFinishedListener { 4 | void onFinish(String name); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/GeneratorFactory.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.lang.LanguageExtension; 5 | import com.intellij.psi.PsiElement; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | import vanstudio.sequence.openapi.model.CallStack; 9 | 10 | public abstract class GeneratorFactory { 11 | 12 | private static final LanguageExtension EP_NAME = new LanguageExtension<>("SequenceDiagram.generator"); 13 | 14 | @NotNull 15 | public static IGenerator createGenerator(Language language, SequenceParams params) { 16 | GeneratorFactory factory = EP_NAME.forLanguage(language); 17 | if (factory == null) { 18 | return new UnsupportedGenerator(); 19 | } 20 | factory.loadParams(params); 21 | return factory.getGenerator(params); 22 | } 23 | 24 | @NotNull 25 | public static IGenerator createGenerator(Language language, SequenceParams params, int offset) { 26 | GeneratorFactory factory = EP_NAME.forLanguage(language); 27 | if (factory == null) { 28 | return new UnsupportedGenerator(); 29 | } 30 | factory.loadParams(params); 31 | return factory.getGenerator(params, offset); 32 | } 33 | 34 | @NotNull 35 | public abstract IGenerator getGenerator(@NotNull SequenceParams params); 36 | 37 | @NotNull 38 | public abstract IGenerator getGenerator(@NotNull SequenceParams params, int offset); 39 | 40 | public abstract void loadParams(@NotNull SequenceParams params); 41 | 42 | 43 | static class UnsupportedGenerator implements IGenerator{ 44 | 45 | @Override 46 | public CallStack generate(PsiElement psiElement, @Nullable CallStack parent) { 47 | return CallStack.EMPTY; 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/IGenerator.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import org.jetbrains.annotations.Nullable; 5 | import vanstudio.sequence.openapi.model.CallStack; 6 | 7 | import java.util.List; 8 | 9 | public interface IGenerator { 10 | /** 11 | * Generate CallStack based on PsiElement. 12 | * @param psiElement instanceof PsiMethod, KtFunction 13 | * @param parent current CallStack or null if called from UI 14 | * @return CallStack includes method call of PsiMethod/KtFunction and calls in its body. 15 | */ 16 | CallStack generate(PsiElement psiElement, @Nullable CallStack parent); 17 | 18 | class ParamPair { 19 | public final List argNames; 20 | public final List argTypes; 21 | 22 | public ParamPair(List argNames, List argTypes) { 23 | this.argNames = argNames; 24 | this.argTypes = argTypes; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/SequenceNavigable.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * © fanhuagang@gmail.com 7 | * Created by van on 2020/4/12. 8 | */ 9 | public interface SequenceNavigable { 10 | 11 | void openClassInEditor(String className); 12 | 13 | void openMethodInEditor(String className, String methodName, List argTypes); 14 | 15 | boolean isInsideAMethod(); 16 | 17 | void openMethodCallInEditor(String fromClass, String fromMethod, List fromArgTypes, 18 | String toClass, String toMethod, List toArgType, int offset); 19 | 20 | List findImplementations(String className); 21 | 22 | List findImplementations(String className, String methodName, List argTypes); 23 | 24 | void openLambdaExprInEditor(String fromClass, String fromMethod, List fromArgTypes, List argTypes, String returnType, int integer); 25 | 26 | void openMethodCallInsideLambdaExprInEditor(String fromClass, String enclosedMethodName, List enclosedMethodArgTypes, 27 | List argTypes, String returnType, 28 | String toClass, String toMethod, List toArgTypes, int offset); 29 | 30 | String[] findSuperClass(String className); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/SequenceNavigableFactory.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.lang.LanguageExtensionPoint; 5 | import com.intellij.openapi.components.ComponentManager; 6 | import com.intellij.openapi.extensions.ExtensionPointName; 7 | import com.intellij.openapi.project.Project; 8 | //import vanstudio.sequence.generator.EmptySequenceNavigable; 9 | import org.jetbrains.annotations.NotNull; 10 | import vanstudio.sequence.generator.JavaSequenceNavigable; 11 | 12 | import java.util.List; 13 | 14 | public class SequenceNavigableFactory { 15 | private static final ExtensionPointName> EP_NAME = ExtensionPointName.create("SequenceDiagram.sequenceNavigable"); 16 | 17 | public static final SequenceNavigableFactory INSTANCE = new SequenceNavigableFactory(); 18 | 19 | private SequenceNavigableFactory() { 20 | } 21 | 22 | public SequenceNavigable forLanguage(ComponentManager componentManager, Language language) { 23 | 24 | @NotNull List> extensionList = EP_NAME.getExtensionsIfPointIsRegistered(componentManager); 25 | 26 | return extensionList.stream() 27 | .filter(it -> language.isKindOf(it.getKey())) 28 | .map(it -> it.createInstance(componentManager)) 29 | .findFirst() 30 | .orElse(new JavaSequenceNavigable((Project) componentManager)); // JAVA and UAST 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/SequenceParams.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi; 2 | 3 | import vanstudio.sequence.openapi.filters.CompositeElementFilter; 4 | import vanstudio.sequence.openapi.filters.ImplementationWhiteList; 5 | 6 | public class SequenceParams { 7 | public static final String PACKAGE_INDICATOR = ".*"; 8 | public static final String RECURSIVE_PACKAGE_INDICATOR = ".**"; 9 | 10 | private int _maxDepth = 3; 11 | private boolean _allowRecursion = false; 12 | @Deprecated(since = "2.2.0", forRemoval = true) 13 | private boolean smartInterface = false; 14 | private final CompositeElementFilter _methodFilter = new CompositeElementFilter(); 15 | private final ImplementationWhiteList _implFilter = new ImplementationWhiteList(); 16 | 17 | public SequenceParams() { 18 | } 19 | 20 | public int getMaxDepth() { 21 | return _maxDepth; 22 | } 23 | 24 | public void setMaxDepth(int maxDepth) { 25 | this._maxDepth = maxDepth; 26 | } 27 | 28 | public boolean isNotAllowRecursion() { 29 | return !_allowRecursion; 30 | } 31 | 32 | public void setAllowRecursion(boolean allowRecursion) { 33 | this._allowRecursion = allowRecursion; 34 | } 35 | 36 | @Deprecated(since = "2.2.0", forRemoval = true) 37 | public boolean isSmartInterface() { 38 | return smartInterface; 39 | } 40 | 41 | @Deprecated(since = "2.2.0", forRemoval = true) 42 | public void setSmartInterface(boolean smartInterface) { 43 | this.smartInterface = smartInterface; 44 | } 45 | 46 | public CompositeElementFilter getMethodFilter() { 47 | return _methodFilter; 48 | } 49 | 50 | public ImplementationWhiteList getImplementationWhiteList() { 51 | return _implFilter; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/filters/CompositeElementFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class CompositeElementFilter implements MethodFilter { 9 | private final List _filters = new ArrayList<>(); 10 | 11 | public void addFilter(MethodFilter filter) { 12 | //remove old if exist 13 | _filters.remove(filter); 14 | _filters.add(filter); 15 | } 16 | 17 | public void removeFilter(MethodFilter filter) { 18 | _filters.remove(filter); 19 | } 20 | 21 | /** 22 | * 23 | * @param psiElement PsiMethod or KtFunction 24 | * @return false if any filter not allow. true when all filter allowed. 25 | */ 26 | @Override 27 | public boolean allow(PsiElement psiElement) { 28 | for (MethodFilter psiElementFilter : _filters) { 29 | if (!psiElementFilter.allow(psiElement)) { 30 | return false; 31 | } 32 | } 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/filters/ImplementationWhiteList.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * The interface should be included. 9 | * 10 | * © fanhuagang@gmail.com 11 | * Created by van on 17/10/2016. 12 | */ 13 | public class ImplementationWhiteList implements MethodFilter { 14 | private final HashMap filters = new HashMap<>(); 15 | 16 | public void clear() { 17 | filters.clear(); 18 | } 19 | 20 | public void put(String key, MethodFilter filter) { 21 | filters.put(key, filter); 22 | } 23 | 24 | public void putIfAbsent(String key, MethodFilter filter) { 25 | if (!contain(key)) { 26 | filters.put(key, filter); 27 | } 28 | } 29 | 30 | public MethodFilter get(String key) { 31 | return filters.get(key); 32 | } 33 | 34 | public boolean contain(String key) { 35 | return filters.containsKey(key); 36 | } 37 | 38 | @Override 39 | public boolean allow(PsiElement psiMethod) { 40 | for (MethodFilter filter : filters.values()) { 41 | if(filter.allow(psiMethod)) { 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/filters/MethodFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | public interface MethodFilter { 6 | boolean allow(PsiElement psiElement); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/filters/PackageFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import vanstudio.sequence.util.MyPsiUtil; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * The package should be excluded. 10 | */ 11 | public class PackageFilter implements MethodFilter { 12 | private final String _packageName; 13 | private final boolean _recursive; 14 | 15 | public PackageFilter(String packageName) { 16 | this(packageName, false); 17 | } 18 | 19 | public PackageFilter(String packageName, boolean recursive) { 20 | _packageName = packageName; 21 | _recursive = recursive; 22 | } 23 | 24 | public boolean allow(PsiElement psiElement) { 25 | String packageName = MyPsiUtil.getPackageName(psiElement); 26 | if(packageName == null) 27 | return true; 28 | if (_recursive) { 29 | return !packageName.startsWith(_packageName); 30 | } 31 | else { 32 | return !packageName.equals(_packageName); 33 | } 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) return true; 39 | if (o == null || getClass() != o.getClass()) return false; 40 | PackageFilter that = (PackageFilter) o; 41 | return _recursive == that._recursive && _packageName.equals(that._packageName); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(_packageName, _recursive); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/filters/ProjectOnlyFilter.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.filters; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import vanstudio.sequence.util.MyPsiUtil; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Only project class/interface should be included. 10 | */ 11 | public class ProjectOnlyFilter implements MethodFilter { 12 | private final boolean _projectClassOnly; 13 | 14 | public ProjectOnlyFilter(boolean projectClassOnly) { 15 | _projectClassOnly = projectClassOnly; 16 | } 17 | 18 | public boolean allow(PsiElement psiElement) { 19 | if(_projectClassOnly && isInProject(psiElement)) 20 | return false; 21 | return true; 22 | } 23 | 24 | private boolean isInProject(PsiElement psiElement) { 25 | return MyPsiUtil.isInJarFileSystem(psiElement) || MyPsiUtil.isInClassFile(psiElement); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) return true; 31 | if (o == null || getClass() != o.getClass()) return false; 32 | ProjectOnlyFilter that = (ProjectOnlyFilter) o; 33 | return _projectClassOnly == that._projectClassOnly; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(_projectClassOnly); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/model/CallStack.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.model; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class CallStack { 9 | private final MethodDescription _method; 10 | private final CallStack _parent; 11 | private final List _calls = new ArrayList<>(); 12 | 13 | public CallStack(@NotNull MethodDescription method) { 14 | this(method, null); 15 | } 16 | 17 | public CallStack(@NotNull MethodDescription method, CallStack parent) { 18 | _method = method; 19 | _parent = parent; 20 | } 21 | 22 | public CallStack methodCall(@NotNull MethodDescription method) { 23 | CallStack callStack = new CallStack(method, this); 24 | _calls.add(callStack); 25 | return callStack; 26 | } 27 | 28 | public boolean isRecursive(MethodDescription method) { 29 | CallStack current = this; 30 | while(current != null) { 31 | if(current._method.equals(method)) 32 | return true; 33 | current = current._parent; 34 | } 35 | return false; 36 | } 37 | 38 | public MethodDescription getMethod() { 39 | return _method; 40 | } 41 | 42 | public List getCalls() { 43 | return _calls; 44 | } 45 | 46 | public int level() { 47 | return _parent == null ? 1 : _parent.level() + 1; 48 | } 49 | 50 | private void generateFormatStr(StringBuffer buffer, int deep) { 51 | for (int i = 0; i< deep; i ++) { 52 | buffer.append(" "); 53 | } 54 | buffer.append(_method.toJson()).append('\n'); 55 | for (CallStack callStack : _calls) { 56 | callStack.generateFormatStr(buffer, deep + 1); 57 | } 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | StringBuffer buffer = new StringBuffer(); 63 | int deep = 0; 64 | generateFormatStr(buffer, deep); 65 | return buffer.toString(); 66 | } 67 | 68 | public static final CallStack EMPTY = new CallStack(MethodDescription.DUMMY_METHOD); 69 | 70 | public boolean isEmpty() { 71 | return EMPTY.equals(this); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/model/ClassDescription.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.model; 2 | 3 | import vanstudio.sequence.openapi.Constants; 4 | 5 | import java.util.*; 6 | 7 | public class ClassDescription { 8 | private final String _className; 9 | private final List _attributes; 10 | 11 | public ClassDescription(String className, List attributes) { 12 | _className = className != null ? className : Constants.ANONYMOUS_CLASS_NAME; 13 | _attributes = attributes; 14 | } 15 | 16 | public String getClassShortName() { 17 | return _className.substring(_className.lastIndexOf('.') + 1); 18 | } 19 | 20 | public String getClassName() { 21 | return _className; 22 | } 23 | 24 | public List getAttributes() { return Collections.unmodifiableList(_attributes); } 25 | 26 | public String toString() { 27 | StringBuilder buffer = new StringBuilder(); 28 | for (String attribute : _attributes) { 29 | buffer.append('|').append(attribute); 30 | } 31 | buffer.append("|@").append(_className); 32 | return buffer.toString(); 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | ClassDescription that = (ClassDescription) o; 40 | return Objects.equals(_className, that._className); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(_className); 46 | } 47 | 48 | public static ClassDescription ANONYMOUS_CLASS = new ClassDescription(Constants.ANONYMOUS_CLASS_NAME,new ArrayList<>()); 49 | 50 | public static ClassDescription TOP_LEVEL_FUN = new ClassDescription(Constants.TOP_LEVEL_FUN, new ArrayList<>()); 51 | 52 | public static ClassDescription getFileNameAsClass(String filename) { 53 | return new ClassDescription(filename, new ArrayList<>()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/model/GenericType.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.model; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class GenericType { 7 | private static final Pattern PATTERN = Pattern.compile("<.*>"); 8 | private final String original; 9 | private boolean generic = false; 10 | private String name; 11 | private String fullName; 12 | private GenericType genericType; 13 | 14 | private GenericType(String original) { 15 | this.original = original; 16 | } 17 | 18 | public String getName() { 19 | StringBuilder sb = new StringBuilder(name); 20 | if (generic) { 21 | sb.append("<").append(genericType.getName()).append(">"); 22 | } 23 | return sb.toString(); 24 | } 25 | 26 | public String getFullName() { 27 | StringBuilder sb = new StringBuilder(fullName); 28 | if (generic) { 29 | sb.append("<").append(genericType.getFullName()).append(">"); 30 | } 31 | return sb.toString(); 32 | } 33 | 34 | public static GenericType create(String original) { 35 | GenericType type = new GenericType(original); 36 | type.build(); 37 | return type; 38 | } 39 | 40 | private void build() { 41 | Matcher matcher = PATTERN.matcher(original); 42 | generic = matcher.find(); 43 | if (generic) { 44 | fullName = original.substring(0, matcher.start()); 45 | genericType = create(original.substring(matcher.start() +1, matcher.end() -1)); 46 | } else { 47 | fullName = original; 48 | } 49 | name = shortName(fullName); 50 | } 51 | 52 | /** Return the last part of a qualified name from its string representation 53 | * @param name the string representation of the qualified name 54 | * @return the last part of the qualified name 55 | */ 56 | private static String shortName(String name) { 57 | return name.substring(name.lastIndexOf('.') + 1); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/model/LambdaExprDescription.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.model; 2 | 3 | import vanstudio.sequence.openapi.Constants; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | /** 10 | * © fanhuagang@gmail.com 11 | * Created by van on 2020/3/22. 12 | */ 13 | public class LambdaExprDescription extends MethodDescription { 14 | private final String _enclosedMethodName; 15 | private final List _enclosedMethodArgTypes; 16 | 17 | public LambdaExprDescription(MethodDescription methodDescription, String returnType, List argNames, List argTypes, int offset) { 18 | super(methodDescription.getClassDescription(), new ArrayList<>(), Constants.Lambda_Invoke,returnType, argNames, argTypes, offset); 19 | this._enclosedMethodName = methodDescription.getMethodName(); 20 | this._enclosedMethodArgTypes = methodDescription.getArgTypes(); 21 | } 22 | 23 | public String getEnclosedMethodName() { 24 | return _enclosedMethodName; 25 | } 26 | 27 | public List getEnclosedMethodArgTypes() { 28 | return _enclosedMethodArgTypes; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) return true; 34 | if (o == null || getClass() != o.getClass()) return false; 35 | if (!super.equals(o)) return false; 36 | LambdaExprDescription that = (LambdaExprDescription) o; 37 | return Objects.equals(_enclosedMethodName, that._enclosedMethodName) && Objects.equals(_enclosedMethodArgTypes, that._enclosedMethodArgTypes); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(super.hashCode(), _enclosedMethodName, _enclosedMethodArgTypes); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/openapi/model/MethodDescription.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.openapi.model; 2 | 3 | import com.google.gson.GsonBuilder; 4 | import vanstudio.sequence.openapi.Constants; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.*; 8 | 9 | public class MethodDescription { 10 | private final ClassDescription _classDescription; 11 | 12 | private final String _methodName; 13 | private final List _attributes; 14 | private final List _argNames; 15 | private final List _argTypes; 16 | private final String _returnType; 17 | private final int offset; 18 | 19 | protected MethodDescription(ClassDescription classDescription, List attributes, 20 | String methodName, String returnType, List argNames, List argTypes, int offset) { 21 | _attributes = attributes; 22 | _returnType = returnType; 23 | _argNames = argNames; 24 | _argTypes = argTypes; 25 | _classDescription = classDescription; 26 | _methodName = methodName; 27 | this.offset = offset; 28 | } 29 | 30 | public String toString() { 31 | StringBuilder buffer = new StringBuilder(); 32 | for (String attribute : _attributes) { 33 | buffer.append('|').append(attribute); 34 | } 35 | buffer.append("|@").append(_methodName).append('['); 36 | for (int i = 0; i < _argNames.size(); i++) { 37 | buffer.append(_argNames.get(i)).append('='); 38 | buffer.append(_argTypes.get(i)); 39 | if (i != _argNames.size() - 1) 40 | buffer.append(','); 41 | } 42 | buffer.append("]:").append(_returnType); 43 | return buffer.toString(); 44 | } 45 | 46 | public String toJson() { 47 | return new GsonBuilder().create().toJson(this); 48 | } 49 | 50 | public ClassDescription getClassDescription() { 51 | return _classDescription; 52 | } 53 | 54 | public String getMethodName() { 55 | return _methodName; 56 | } 57 | 58 | public String getTitleName() { 59 | return getClassDescription().getClassShortName() + '.' + 60 | getMethodName() ; 61 | } 62 | 63 | public String getFullName() { 64 | String name = Constants.CONSTRUCTOR_METHOD_NAME.equals(_methodName) ? "<>" : _methodName; 65 | StringBuilder sb = new StringBuilder(); 66 | sb.append(name).append("("); 67 | for (int i = 0; i < _argNames.size(); i++) { 68 | if (i > 0) sb.append(", "); 69 | String argName = _argNames.get(i); 70 | String argType = _argTypes.get(i); 71 | argType = shortTypeName(argType); 72 | sb.append(argName).append(": ").append(argType); 73 | } 74 | sb.append(")").append(": ").append(shortTypeName(_returnType)); 75 | return sb.toString(); 76 | } 77 | 78 | @NotNull 79 | private String shortTypeName(String argType) { 80 | GenericType genericType = GenericType.create(argType); 81 | return genericType.getName(); 82 | } 83 | 84 | public List getAttributes() { 85 | return Collections.unmodifiableList(_attributes); 86 | } 87 | 88 | public List getArgNames() { 89 | return Collections.unmodifiableList(_argNames); 90 | } 91 | 92 | public List getArgTypes() { 93 | return Collections.unmodifiableList(_argTypes); 94 | } 95 | 96 | public String getReturnType() { 97 | return _returnType; 98 | } 99 | 100 | @Override 101 | public boolean equals(Object o) { 102 | if (this == o) return true; 103 | if (o == null || getClass() != o.getClass()) return false; 104 | MethodDescription that = (MethodDescription) o; 105 | return Objects.equals(_classDescription, that._classDescription) 106 | && Objects.equals(_methodName, that._methodName) 107 | && Objects.equals(_argTypes, that._argTypes); 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return Objects.hash(_classDescription, _methodName, _argTypes); 113 | } 114 | 115 | 116 | public static MethodDescription createMethodDescription(ClassDescription classDescription, 117 | List attributes, String methodName, 118 | String returnType, 119 | List argNames, List argTypes, int offset) { 120 | return new MethodDescription(classDescription, attributes, methodName, returnType, argNames, argTypes, offset); 121 | } 122 | 123 | public static MethodDescription createConstructorDescription(ClassDescription classDescription, 124 | List attributes, List argNames, 125 | List argTypes, int offset) { 126 | return new MethodDescription(classDescription, attributes, Constants.CONSTRUCTOR_METHOD_NAME, 127 | classDescription.getClassName(), argNames, argTypes, offset); 128 | } 129 | 130 | public int getOffset() { 131 | return offset; 132 | } 133 | 134 | // static MethodDescription createLambdaDescription(ClassDescription classDescription, 135 | // List argNames, List argTypes, String returnType) { 136 | // return new MethodDescription(classDescription, 137 | // new ArrayList<>(), Constants.Lambda_Invoke, returnType, argNames, argTypes); 138 | // } 139 | 140 | public static final MethodDescription DUMMY_METHOD = new MethodDescription(ClassDescription.ANONYMOUS_CLASS, new ArrayList<>(), "dummy", "",new ArrayList<>(), new ArrayList<>(), 0); 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/ui/ButtonTabComponent.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.ui; 2 | 3 | /** 4 | * © fanhuagang@gmail.com 5 | * Created by van on 20/10/2016. 6 | */ 7 | 8 | import javax.swing.*; 9 | import javax.swing.plaf.basic.BasicButtonUI; 10 | import java.awt.*; 11 | import java.awt.event.*; 12 | import java.awt.event.ActionListener; 13 | import java.awt.event.MouseListener; 14 | 15 | /** 16 | * Component to be used as tabComponent; 17 | * Contains a JLabel to show the text and 18 | * a JButton to close the tab it belongs to 19 | */ 20 | public class ButtonTabComponent extends JPanel { 21 | private final JTabbedPane pane; 22 | private final JButton button; 23 | 24 | public ButtonTabComponent(final JTabbedPane pane) { 25 | //unset default FlowLayout' gaps 26 | super(new FlowLayout(FlowLayout.LEFT, 0, 0)); 27 | if (pane == null) { 28 | throw new NullPointerException("TabbedPane is null"); 29 | } 30 | this.pane = pane; 31 | setOpaque(false); 32 | 33 | JLabel icon = new JLabel() { 34 | @Override 35 | public Icon getIcon() { 36 | int i = pane.indexOfTabComponent(ButtonTabComponent.this); 37 | if (i != -1) { 38 | return pane.getIconAt(i); 39 | } 40 | return null; 41 | } 42 | }; 43 | 44 | add(icon); 45 | 46 | icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); 47 | 48 | //make JLabel read titles from JTabbedPane 49 | JLabel label = new JLabel() { 50 | public String getText() { 51 | int i = pane.indexOfTabComponent(ButtonTabComponent.this); 52 | if (i != -1) { 53 | return pane.getTitleAt(i); 54 | } 55 | return null; 56 | } 57 | }; 58 | 59 | add(label); 60 | //add more space between the label and the button 61 | label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); 62 | //tab button 63 | button = new TabButton(); 64 | add(button); 65 | //add more space to the top of the component 66 | setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); 67 | } 68 | 69 | public void addTabButtonListener(ActionListener l) { 70 | button.addActionListener(l); 71 | } 72 | 73 | private class TabButton extends JButton /*implements ActionListener*/ { 74 | public TabButton() { 75 | int size = 17; 76 | setPreferredSize(new Dimension(size, size)); 77 | setToolTipText("close this tab"); 78 | //Make the button looks the same for all Laf's 79 | setUI(new BasicButtonUI()); 80 | //Make it transparent 81 | setContentAreaFilled(false); 82 | //No need to be focusable 83 | setFocusable(false); 84 | setBorder(BorderFactory.createEtchedBorder()); 85 | setBorderPainted(false); 86 | //Making nice rollover effect 87 | //we use the same listener for all buttons 88 | addMouseListener(buttonMouseListener); 89 | setRolloverEnabled(true); 90 | //Close the proper tab by clicking the button 91 | // addActionListener(this); 92 | } 93 | 94 | // public void actionPerformed(ActionEvent e) { 95 | // int i = pane.indexOfTabComponent(ButtonTabComponent.this); 96 | // if (i != -1) { 97 | // pane.remove(i); 98 | // } 99 | // } 100 | 101 | //we don't want to update UI for this button 102 | public void updateUI() { 103 | } 104 | 105 | //paint the cross 106 | protected void paintComponent(Graphics g) { 107 | super.paintComponent(g); 108 | Graphics2D g2 = (Graphics2D) g.create(); 109 | //shift the image for pressed buttons 110 | if (getModel().isPressed()) { 111 | g2.translate(1, 1); 112 | } 113 | g2.setStroke(new BasicStroke(2)); 114 | g2.setColor(Color.BLACK); 115 | if (getModel().isRollover()) { 116 | g2.setColor(Color.MAGENTA); 117 | } 118 | int delta = 6; 119 | g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1); 120 | g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1); 121 | g2.dispose(); 122 | } 123 | } 124 | 125 | private final static MouseListener buttonMouseListener = new MouseAdapter() { 126 | public void mouseEntered(MouseEvent e) { 127 | Component component = e.getComponent(); 128 | if (component instanceof AbstractButton) { 129 | AbstractButton button = (AbstractButton) component; 130 | button.setBorderPainted(false); 131 | } 132 | } 133 | 134 | public void mouseExited(MouseEvent e) { 135 | Component component = e.getComponent(); 136 | if (component instanceof AbstractButton) { 137 | AbstractButton button = (AbstractButton) component; 138 | button.setBorderPainted(false); 139 | } 140 | } 141 | }; 142 | } -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/ui/OptionsUI.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import com.intellij.util.ui.JBUI; 6 | import vanstudio.sequence.config.SequenceParamsState; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | public class OptionsUI { 12 | 13 | public static class DialogPanel extends JPanel { 14 | private final JSpinner jSpinner; 15 | private final JCheckBox jCheckBoxPFO; 16 | private final JCheckBox jCheckBoxNGS; 17 | private final JCheckBox jCheckBoxNPM; 18 | private final JCheckBox jCheckBoxNC; 19 | // private final JCheckBox jCheckBoxSI; 20 | 21 | public DialogPanel() { 22 | super(new GridBagLayout()); 23 | final SequenceParamsState state = SequenceParamsState.getInstance(); 24 | setBorder(BorderFactory.createTitledBorder("Options")); 25 | GridBagConstraints gc = new GridBagConstraints(); 26 | gc.gridx = 0; 27 | gc.gridy = 0; 28 | gc.insets = JBUI.insets(5); 29 | gc.anchor = GridBagConstraints.WEST; 30 | JLabel jLabel = new JLabel("Call depth:"); 31 | add(jLabel, gc); 32 | 33 | gc.gridx = 1; 34 | gc.anchor = GridBagConstraints.CENTER; 35 | jSpinner = new JSpinner(new SpinnerNumberModel(state.callDepth, 1, 10, 1)); 36 | jLabel.setLabelFor(jSpinner); 37 | add(jSpinner, gc); 38 | 39 | gc.gridx = 0; 40 | gc.gridy = 1; 41 | gc.anchor = GridBagConstraints.WEST; 42 | gc.gridwidth = 2; 43 | gc.insets = JBUI.emptyInsets(); 44 | jCheckBoxPFO = new JCheckBox("Display only project classes", state.projectClassesOnly); 45 | add(jCheckBoxPFO, gc); 46 | 47 | gc.gridx = 0; 48 | gc.gridy = 2; 49 | gc.anchor = GridBagConstraints.WEST; 50 | gc.gridwidth = 2; 51 | gc.insets = JBUI.emptyInsets(); 52 | jCheckBoxNGS = new JCheckBox("Skip getters/setters", state.noGetterSetters); 53 | add(jCheckBoxNGS, gc); 54 | 55 | // gc.gridx = 0; 56 | // gc.gridy = 3; 57 | // gc.anchor = GridBagConstraints.WEST; 58 | // gc.gridwidth = 2; 59 | // gc.insets = JBUI.emptyInsets(); 60 | // jCheckBoxSI = new JCheckBox("Smart interface (experimental)", state.smartInterface); 61 | // add(jCheckBoxSI, gc); 62 | 63 | gc.gridx = 2; 64 | gc.gridy = 1; 65 | gc.anchor = GridBagConstraints.WEST; 66 | gc.gridwidth = 2; 67 | gc.insets = JBUI.emptyInsets(); 68 | jCheckBoxNPM = new JCheckBox("Skip private methods", state.noPrivateMethods); 69 | add(jCheckBoxNPM, gc); 70 | 71 | gc.gridx = 2; 72 | gc.gridy = 2; 73 | gc.anchor = GridBagConstraints.WEST; 74 | gc.gridwidth = 2; 75 | gc.insets = JBUI.emptyInsets(); 76 | jCheckBoxNC = new JCheckBox("Skip constructors", state.noConstructors); 77 | add(jCheckBoxNC, gc); 78 | } 79 | } 80 | 81 | public static class OptionsDialogWrapper extends DialogWrapper { 82 | private final DialogPanel dialogPanel = new DialogPanel(); 83 | 84 | public OptionsDialogWrapper(Project project) { 85 | super(project, false); 86 | setResizable(false); 87 | setTitle("Sequence Diagram Options"); 88 | init(); 89 | } 90 | 91 | protected JComponent createCenterPanel() { 92 | return dialogPanel; 93 | } 94 | 95 | public JComponent getPreferredFocusedComponent() { 96 | return dialogPanel.jSpinner; 97 | } 98 | 99 | public int getCallStackDepth() { 100 | return (Integer) dialogPanel.jSpinner.getValue(); 101 | } 102 | 103 | public boolean isProjectClassesOnly() { 104 | return dialogPanel.jCheckBoxPFO.isSelected(); 105 | } 106 | 107 | public boolean isNoGetterSetters() { 108 | return dialogPanel.jCheckBoxNGS.isSelected(); 109 | } 110 | 111 | public boolean isNoPrivateMethods() { 112 | return dialogPanel.jCheckBoxNPM.isSelected(); 113 | } 114 | 115 | public boolean isNoConstructors() { 116 | return dialogPanel.jCheckBoxNC.isSelected(); 117 | } 118 | 119 | // public boolean isSmartInterface() { 120 | // return dialogPanel.jCheckBoxSI.isSelected(); 121 | // } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/util/MyNotifier.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.util; 2 | 3 | import com.intellij.notification.NotificationGroup; 4 | import com.intellij.notification.NotificationType; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.project.Project; 7 | import icons.SequencePluginIcons; 8 | import vanstudio.sequence.SequenceService; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class MyNotifier { 12 | 13 | /** 14 | * Notification user with content. 15 | * @param project the project 16 | * @param content the content max in three line, may have html tag as will 17 | */ 18 | public static void notifyError(@Nullable Project project, String content) { 19 | NotificationGroup registeredGroup = NotificationGroup.findRegisteredGroup(SequenceService.PLUGIN_NAME); 20 | if (registeredGroup == null) return; 21 | 22 | registeredGroup 23 | .createNotification(content, NotificationType.INFORMATION) 24 | .setTitle(SequenceService.PLUGIN_NAME) 25 | .setIcon(SequencePluginIcons.SEQUENCE_ICON_13) 26 | .notify(project); 27 | } 28 | 29 | /** 30 | * Notify user with content and action. 31 | * @param project the project 32 | * @param content the content max in three line, may have html tag as will 33 | * @param action the action to be show in link. 34 | */ 35 | public static void notifyWithAction(@Nullable Project project, String content, AnAction action) { 36 | NotificationGroup registeredGroup = NotificationGroup.findRegisteredGroup(SequenceService.PLUGIN_NAME); 37 | if (registeredGroup == null) return; 38 | 39 | registeredGroup 40 | .createNotification(content, NotificationType.INFORMATION) 41 | .setTitle(SequenceService.PLUGIN_NAME) 42 | .setIcon(SequencePluginIcons.SEQUENCE_ICON_13) 43 | .addAction(action) 44 | .notify(project); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/vanstudio/sequence/util/MyUastUtil.kt: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.util 2 | 3 | import org.jetbrains.uast.* 4 | import vanstudio.sequence.diagram.Info 5 | import vanstudio.sequence.openapi.IGenerator.ParamPair 6 | import vanstudio.sequence.openapi.model.ClassDescription 7 | import vanstudio.sequence.openapi.model.LambdaExprDescription 8 | import vanstudio.sequence.openapi.model.MethodDescription 9 | 10 | fun createMethod(node: ULambdaExpression, offset: Int): MethodDescription { 11 | val paramPair: ParamPair = extractParameters(node.valueParameters) 12 | val returnType = node.getExpressionType()?.canonicalText 13 | 14 | val uMethod = node.getParentOfType(UMethod::class.java, true) 15 | val enclosedMethod = createMethod(uMethod!!, offset) 16 | 17 | return LambdaExprDescription(enclosedMethod, returnType, paramPair.argNames, paramPair.argTypes, offset) 18 | } 19 | 20 | fun createMethod(node: UMethod, offset: Int): MethodDescription { 21 | val paramPair: ParamPair = extractParameters(node.uastParameters) 22 | val containingUClass = node.getContainingUClass() 23 | val attributes = createAttributes(node) 24 | 25 | if (node.isConstructor) { 26 | return MethodDescription.createConstructorDescription( 27 | createClassDescription(containingUClass), 28 | attributes, paramPair.argNames, paramPair.argTypes, offset 29 | ) 30 | } 31 | 32 | val returnType = node.returnType 33 | return MethodDescription.createMethodDescription( 34 | createClassDescription(containingUClass), 35 | attributes, node.name, returnType?.canonicalText, 36 | paramPair.argNames, paramPair.argTypes, offset 37 | ) 38 | } 39 | 40 | fun createClassDescription(containingUClass: UClass?): ClassDescription? { 41 | return ClassDescription( 42 | containingUClass?.qualifiedName, 43 | createAttributes(containingUClass) 44 | ) 45 | } 46 | 47 | fun createAttributes(node: UClass?): List { 48 | val attributes = ArrayList() 49 | for (attribute in Info.RECOGNIZED_METHOD_ATTRIBUTES) { 50 | if (node?.hasModifierProperty(attribute) == true) { 51 | attributes.add(attribute) 52 | } 53 | } 54 | if (isExternal(node)) { 55 | attributes.add(Info.EXTERNAL_ATTRIBUTE); 56 | } 57 | if (isInterface(node)) { 58 | attributes.add(Info.INTERFACE_ATTRIBUTE) 59 | } 60 | return attributes 61 | } 62 | 63 | 64 | fun createAttributes(node: UMethod): List { 65 | val attributes = ArrayList() 66 | for (attribute in Info.RECOGNIZED_METHOD_ATTRIBUTES) { 67 | if (node.hasModifierProperty(attribute)) { 68 | attributes.add(attribute) 69 | } 70 | } 71 | val containingUClass = node.getContainingUClass() 72 | if (isExternal(containingUClass)) { 73 | attributes.add(Info.EXTERNAL_ATTRIBUTE); 74 | } 75 | if (isInterface(containingUClass)) { 76 | attributes.add(Info.INTERFACE_ATTRIBUTE) 77 | } 78 | return attributes 79 | } 80 | 81 | fun extractParameters(uastParameters: List): ParamPair { 82 | val argNames = ArrayList() 83 | val argTypes = ArrayList() 84 | for (uastParameter in uastParameters) { 85 | argNames.add(uastParameter.name) 86 | argTypes.add(uastParameter.type.canonicalText) 87 | } 88 | return ParamPair(argNames, argTypes) 89 | } 90 | 91 | fun isExternal(uClass: UClass?): Boolean { 92 | val virtualFile = uClass?.containingFile?.virtualFile 93 | val protocol = virtualFile?.fileSystem?.protocol 94 | 95 | return virtualFile?.name?.endsWith(".class") == true 96 | || protocol?.equals("jar", ignoreCase = true) == true 97 | || protocol?.equals("zip", ignoreCase = true) == true 98 | } 99 | 100 | fun isInterface(uClass: UClass?): Boolean { 101 | return uClass?.isInterface ?: false 102 | } 103 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/SequenceDiagram-kotlin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | SequenceDiagram 3 | SequenceDiagram 4 | SequenceDiagram for IntelliJ IDEA 6 | http://vanco.github.io/SequencePlugin. 7 | 8 |

with this plugin, you can
9 | + generate Simple Sequence Diagram.
10 | + Navigate the code by click the diagram shape.
11 | + Delete Class from diagram.
12 | + Export the diagram as image.
13 | + Export the diagram as PlantUML file.
14 | + Exclude classes from diagram by Settings > Tools > Sequence Diagram
15 | + Smart Interface(experimental)
16 | + Lambda Expression(experimental)

17 | 18 |

SequencePluginReload rename to SequenceDiagram. 19 | Simple Sequence Diagram Generator. fixed version of SequencePlugin , work from 13.x. 20 | The original version SequencePlugin come from ksibilev@yahoo.com.

21 | 22 | ]]> 23 | 1.2.4 24 | VanStudio 25 | 26 | 27 | com.intellij.modules.platform 28 | 29 | com.intellij.modules.java 30 | 31 | com.intellij.java 32 | 33 | org.jetbrains.kotlin 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 89 | 90 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/icons/folder.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/image.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/play.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/puml.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/save.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/sequence.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/sequence_13.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/icons/settings.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/main/resources/vanstudio/sequence/diagram/app/Sequence.properties: -------------------------------------------------------------------------------- 1 | Sequence.frame.title = SEQUENCE 2 | 3 | Sequence.menu.file.label = File 4 | Sequence.menu.edit.label = Edit 5 | Sequence.menu.help.label = Help 6 | 7 | NewAction.name = New 8 | NewAction.icon = icons/app/New16.gif 9 | NewAction.shortDesc = Create a new sequence diagram 10 | NewAction.confirmTitle = Modified diagram 11 | NewAction.confirmMessage = You've made changes to the diagram.\nDo you want to save them before creating a new diagram? 12 | 13 | OpenAction.name = Open 14 | OpenAction.shortDesc = Open an existing new sequence diagram 15 | OpenAction.icon = icons/app/Open16.gif 16 | OpenAction.dialogTitle = Open diagram 17 | 18 | SaveAction.name = Save 19 | SaveAction.shortDesc = Save the current sequence diagram 20 | SaveAction.icon = icons/app/Save16.gif 21 | SaveAction.dialogTitle = Save diagram 22 | 23 | SaveAsAction.name = SaveAs 24 | SaveAsAction.shortDesc = Save the current sequence diagram under a new name 25 | SaveAsAction.icon = icons/app/SaveAs16.gif 26 | SaveAsAction.dialogTitle = Save diagram as 27 | 28 | ExportAction.name = Export 29 | ExportAction.shortDesc = Export current sequence diagram as a png image 30 | ExportAction.dialogTitle = Export diagram as 31 | ExportAction.icon = icons/app/Export16.gif 32 | 33 | ExitAction.name = Exit 34 | ExitAction.shortDesc = Exit the program 35 | ExitAction.confirmMessage = You've made changes to the diagram.\nDo you want to save them before exiting? 36 | ExitAction.confirmTitle = Modified diagram 37 | 38 | CutAction.name = Cut 39 | CutAction.shortDesc = Cut current text selection to clipboard 40 | CutAction.icon = icons/app/Cut16.gif 41 | 42 | CopyAction.name = Copy 43 | CopyAction.shortDesc = Copy current text selection to clipboard 44 | CopyAction.icon = icons/app/Copy16.gif 45 | 46 | PasteAction.name = Paste 47 | PasteAction.shortDesc = Paste current clipboard contents to editor 48 | PasteAction.icon = icons/app/Paste16.gif 49 | 50 | ExampleAction.name = Example 51 | ExampleAction.shortDesc = Load an example model 52 | ExampleAction.icon = icons/app/Open16.gif 53 | ExampleAction.confirmMessage = You've made changes to the diagram.\nDo you want to save them before loading the example? 54 | ExampleAction.confirmTitle = Modified diagram 55 | ExampleAction.exampleModelText = \ 56 | (Comments newComment \n \ 57 | (CommentFactory newComment \n \ 58 | (Comment new \n \ 59 | (IDGen nextID) \n \ 60 | (Transaction add)))\n \ 61 | (Comments addToCache)) 62 | 63 | AboutAction.name = About 64 | AboutAction.shortDesc = Some information about this program 65 | AboutAction.aboutMessage = \ 66 | SEQUENCE - A very simple sequence diagram editor \n\ 67 | Copyright (C) 2002 Alex Moffat \n\ 68 | Version 20020729 \n\ 69 | \n\ 70 | This library is free software; you can redistribute it and/or \n\ 71 | modify it under the terms of the GNU Lesser General Public \n\ 72 | License as published by the Free Software Foundation; either \n\ 73 | version 2.1 of the License, or (at your option) any later version. \n\ 74 | \n\ 75 | This library is distributed in the hope that it will be useful, \n\ 76 | but WITHOUT ANY WARRANTY; without even the implied warranty of \n\ 77 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU \n\ 78 | Lesser General Public License for more details. \n\ 79 | \n\ 80 | You should have received a copy of the GNU Lesser General Public \n\ 81 | License along with this library; if not, write to the Free Software \n\ 82 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \n\ 83 | \n\ 84 | Included in the program are the graphics from the Java Look and Feel Graphics \n\ 85 | Repository and code from the Apache Foundations Log4j project. The licenses \n\ 86 | for these can be found in the LICENSES file that came with this program. 87 | AboutAction.aboutTitle = About this program 88 | AboutAction.icon = icons/app/About16.gif 89 | 90 | -------------------------------------------------------------------------------- /src/main/resources/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Welcome to SequenceDiagram

6 | 7 |

8 | Getting Started 9 |

10 |
11 |   1. Open "JAVA", "kotlin", "Groovy", "Scala" file
12 |   2. Generate SequenceDiagram with shortcut `Alt S` for windows, `Option S` for macOS
13 | 
14 | 15 |

Online Document

16 |

17 | https://vanco.github.io/SequencePlugin 18 |

19 | 20 | 21 | -------------------------------------------------------------------------------- /src/test/java/vanstudio/sequence/generator/MethodDescriptionTest.java: -------------------------------------------------------------------------------- 1 | package vanstudio.sequence.generator; 2 | 3 | import com.google.gson.Gson; 4 | import vanstudio.sequence.openapi.model.ClassDescription; 5 | import vanstudio.sequence.openapi.model.MethodDescription; 6 | import org.junit.Test; 7 | 8 | import java.awt.image.DataBufferInt; 9 | import java.util.ArrayList; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class MethodDescriptionTest { 14 | 15 | @Test 16 | public void toJson() { 17 | ArrayList attr = new ArrayList<>(); 18 | attr.add("public"); 19 | 20 | ArrayList argName = new ArrayList<>(); 21 | argName.add("classDescription"); 22 | argName.add("attributes"); 23 | argName.add("methodName"); 24 | argName.add("returnType"); 25 | argName.add("argNames"); 26 | argName.add("argTypes"); 27 | argName.add("offset"); 28 | 29 | ArrayList argType = new ArrayList<>(); 30 | argType.add("org.intellij.sequencer.model.ClassDescription"); 31 | argType.add("java.util.List"); 32 | argType.add("java.lang.String"); 33 | argType.add("java.lang.String"); 34 | argType.add("java.util.List"); 35 | argType.add("java.util.List"); 36 | argType.add("int"); 37 | 38 | MethodDescription m = MethodDescription.createMethodDescription(ClassDescription.ANONYMOUS_CLASS, 39 | attr, 40 | "createMethodDescription", 41 | "org.intellij.sequencer.model.MethodDescription", 42 | argName, 43 | argType, 44 | 0 45 | ); 46 | 47 | String json = m.toJson(); 48 | Gson gson = new Gson(); 49 | MethodDescription method = gson.fromJson(json, MethodDescription.class); 50 | 51 | assertEquals(m, method); 52 | } 53 | 54 | @Test(expected = NegativeArraySizeException.class) 55 | public void maxSize() { 56 | int size = -1233295232; 57 | DataBufferInt bufferInt = new DataBufferInt(size); 58 | } 59 | } 60 | --------------------------------------------------------------------------------