├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png └── 7.png ├── jsonView.zip ├── lib ├── jxbrowser-6.20.jar ├── license.jar └── sun.misc.BASE64Decoder.jar ├── mac └── META-INF │ └── MANIFEST.MF ├── objectbox-models └── default.json ├── settings.gradle ├── splash.png └── src └── main ├── java └── com │ └── longforus │ └── apidebugger │ └── ui │ ├── DefaultParamsDialog.form │ ├── DefaultParamsDialog.java │ ├── DeleteBtnComboBoxRenderer.java │ ├── JSONEditPanel.java │ ├── JsonEditPanel.java │ ├── JsonJTreeNode.java │ ├── JsonTreeCellRenderer.java │ ├── MainPanel.form │ ├── MainPanel.java │ └── ParamsTableModel.java └── kotlin └── com └── longforus └── apidebugger ├── ExtFun.kt ├── HttpManage.kt ├── JsonTreeActionHandler.kt ├── Main.kt ├── MyValueHandler.kt ├── OB.kt ├── UIActionHandler.kt ├── UILifecycleHandler.kt ├── bean ├── ApiBean.kt ├── ListDbConverter.kt ├── MapDbConverter.kt ├── ProjectBean.kt └── TableBean.kt └── encrypt ├── DefaultEncryptHandler.kt └── IEncryptHandler.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/ 38 | # Keystore files 39 | # Uncomment the following line if you do not want to check your keystore files in. 40 | #*.jks 41 | 42 | # External native build folder generated in Android Studio 2.2 and later 43 | .externalNativeBuild 44 | 45 | # Google Services (e.g. APIs or Firebase) 46 | google-services.json 47 | 48 | # Freeline 49 | freeline.py 50 | freeline/ 51 | freeline_project_description.json 52 | 53 | # fastlane 54 | fastlane/report.xml 55 | fastlane/Preview.html 56 | fastlane/screenshots 57 | fastlane/test_output 58 | fastlane/readme.md 59 | src/main/kotlin/com/longforus/apidebugger/encrypt/kzEncryptHandler.kt 60 | src/main/kotlin/com/longforus/apidebugger/encrypt/EncryptUtil.java 61 | src/main/kotlin/com/longforus/apidebugger/encrypt/DetectTool.java 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Void Young 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API debugger 2 | 3 | A like Postman API debugger that supports custom encryption. 4 | 5 | 一个类似Postman的支持自定义加密传输的后台API接口调试工具. 6 | 7 | ![1](./img/1.png) 8 | 9 | ## 特征 10 | 11 | - 支持可扩展的自定义的参数加密方式. 12 | - 使用数据库按项目分开保存BaseUrl和接口Api列表,一次配置,持续使用. 13 | - 支持多个BaseUrl点击切换.请求参数动态增删. 14 | - 支持默认请求参数配置,该项目下的每一个接口请求都会默认添加默认请求参数. 15 | - 使用[Json Editor Online](http://jsoneditoronline.org/)展示请求结果json,美观,易用. 16 | - 支持简单的接口压力测试 17 | 18 | ## 安装 19 | 20 | 点击[下载最新release包](https://github.com/longforus/api-debugger/releases).在安装了JDK1.8或者JRE1.8的电脑上,双击jar包直接运行. 21 | 22 | - JDK下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 23 | - 如果在Windows系统下无法正常使用的话.需要安装 [Microsoft Visual C++ 2015 Redistributable (x64)](http://www.microsoft.com/en-us/download/details.aspx?id=53587) 24 | 25 | ## 使用 26 | 27 | 1. 创建项目:菜单栏 Project > new> 输入项目名称>OK 28 | 29 | ![2](/img/2.png) 30 | 31 | 看到当前打开的项目就OK了. 32 | 33 | 2. 输入BaseUrl,点击保存生效. 34 | 35 | 3. 输入接口Url,最后的请求Url = baseUrl+接口Url. 36 | 37 | 4. 选择请求方法,现在只做了POST和GET方法. 38 | 39 | 5. 选择加密方式.default是没有加密,直接发送. 40 | 41 | 6. Add Row 添加一个请求参数. 42 | 43 | 7. 填写请求参数的key和value,这里需要注意的是需要表格失去焦点变为蓝色之后,这个值才会被保存生效. 44 | 45 | ![3](/img/3.png) 46 | 47 | 8. 点击小飞机发送请求.请求的相关信息在右上部分的Request Information中显示.请求结果在下面的Json Editor中显示. 48 | 49 | 9. 请求成功返回后这个接口的url,方法,加密方式和请求参数会自动保存到数据库中.添加其他请求只需直接输入接口url和参数,方法等配置进行测试,前一个接口的信息不会被覆盖. 50 | 51 | ## 自定义加密扩展 52 | 53 | 该App界面使用Java实现,逻辑使用Kotlin实现.内部数据库为[ObjectBox](https://objectbox.io/),网络请求使用Okhttp3.需要达到自定义加密的目的的话.需要自行扩展. 54 | 55 | 1. **[重点]**fork仓库clone到本地后,使用IDEA打开.下载 jxbrowser-对应平台-6.20.jar (链接:https://pan.baidu.com/s/1B3ErPhbrocIaGhu3zg8RMA 密码:1wn9 ) 拷贝到lib中(太大了不好传). 56 | 57 | 2. 在build生成out文件夹后,解压jsonView.zip到`\out\production\classes\com\longforus\apidebugger\ui`目录下(这样生成jar包的时候才会把这些文件包含到jar包中,gradle应该有别的更优雅的方法,目前尚未实现). 58 | 59 | 3. 实现`com.longforus.apidebugger.encrypt.IEncryptHandler`抽象类.可参考默认实现类`com.longforus.apidebugger.encrypt.DefaultEncryptHandler` 60 | 61 | ```kotlin 62 | /** 63 | * Created by XQ Yang on 8/30/2018 5:11 PM. 64 | * Description : 加密处理 65 | */ 66 | abstract class IEncryptHandler { 67 | //这个加密类型的code,同一工程不允许出现相同的 68 | abstract val typeCode:Int 69 | //显示在界面上的名字 70 | abstract val title: String 71 | //实现get方法的参数加密 72 | abstract fun onGetMethodEncrypt(params: Map?, builder: Request.Builder, url: String) 73 | //实现post方法的参数加密 74 | abstract fun onPostMethodEncrypt(params: Map?, builder: Request.Builder, url: String): RequestBody 75 | override fun toString(): String { 76 | return title 77 | } 78 | } 79 | ``` 80 | 81 | 4. 新建一个实现类的实例添加到`com.longforus.apidebugger.MyValueHandler#getEncryptImplList`中.第0个为默认显示item.现在就可以在加密方式中选择你自己的加密方式了. 82 | 83 | ```kotlin 84 | 85 | object MyValueHandler { 86 | val encryptImplList = listOf(YourEncryptHandler(), DefaultEncryptHandler()) 87 | } 88 | ``` 89 | 90 | 5. 打包可运行的jar包: 91 | 92 | ![4](/img/4.png) 93 | 94 | ![5](/img/5.png) 95 | 96 | ![6](/img/6.png) 97 | 98 | ![7](/img/7.png) 99 | 100 | 6. 添加splash闪屏图片: 101 | 拷贝splash.png到`\out\production\classes`目录下,在上一步生成的清单文件中添加最后一行. 102 | ``` 103 | Manifest-Version: 1.0 104 | Main-Class: com.longforus.apidebugger.MainKt 105 | SplashScreen-Image: splash.png 106 | ``` -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.objectboxVersion = '2.1.0' 3 | repositories { 4 | jcenter() 5 | maven { url "https://plugins.gradle.org/m2/" } 6 | } 7 | dependencies { 8 | classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion" 9 | } 10 | } 11 | 12 | plugins { 13 | id 'java' 14 | id 'org.jetbrains.kotlin.jvm' version '1.2.30' 15 | } 16 | 17 | apply plugin: 'kotlin-kapt' 18 | apply plugin: 'application' 19 | apply plugin: 'io.objectbox' 20 | 21 | group 'com.longforus' 22 | version '1.0' 23 | 24 | 25 | repositories { 26 | jcenter() 27 | mavenCentral() 28 | } 29 | 30 | compileKotlin { 31 | kotlinOptions.jvmTarget = "1.8" 32 | } 33 | compileTestKotlin { 34 | kotlinOptions.jvmTarget = "1.8" 35 | } 36 | 37 | 38 | sourceCompatibility = 1.8 39 | 40 | 41 | mainClassName = "com.longforus.apidebugger.MainKt" 42 | dependencies { 43 | implementation fileTree(include: ['*.jar'], dir: 'lib') 44 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" 45 | // Optional: include all native libraries for distribution 46 | implementation "io.objectbox:objectbox-kotlin:$objectboxVersion" 47 | implementation "io.objectbox:objectbox-linux:$objectboxVersion" 48 | implementation "io.objectbox:objectbox-macos:$objectboxVersion" 49 | implementation "io.objectbox:objectbox-windows:$objectboxVersion" 50 | implementation "io.objectbox:objectbox-java:$objectboxVersion" 51 | kapt "io.objectbox:objectbox-processor:$objectboxVersion" 52 | implementation 'com.google.code.gson:gson:2.8.5' 53 | implementation 'org.apache.commons:commons-lang3:3.8' 54 | implementation 'com.squareup.okhttp3:okhttp:3.11.0' 55 | implementation 'com.jgoodies:jgoodies-forms:1.9.0' 56 | } 57 | 58 | 59 | 60 | 61 | 62 | //task copyRes(type: Copy, dependsOn: compileJava) { 63 | // compileJava.getOutputs().files.forEach{f-> 64 | // println(f.getAbsolutePath()) 65 | // } 66 | //} 67 | 68 | // enable debug output for plugin 69 | objectbox { 70 | debug false 71 | } 72 | // enable debug output for annotation processor 73 | tasks.withType(JavaCompile) { 74 | options.compilerArgs += [ "-Aobjectbox.debug=false" ] 75 | options.encoding = "UTF-8" 76 | } 77 | -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/4.png -------------------------------------------------------------------------------- /img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/5.png -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/6.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/img/7.png -------------------------------------------------------------------------------- /jsonView.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/jsonView.zip -------------------------------------------------------------------------------- /lib/jxbrowser-6.20.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/lib/jxbrowser-6.20.jar -------------------------------------------------------------------------------- /lib/license.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/lib/license.jar -------------------------------------------------------------------------------- /lib/sun.misc.BASE64Decoder.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/lib/sun.misc.BASE64Decoder.jar -------------------------------------------------------------------------------- /mac/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.longforus.apidebugger.MainKt 3 | SplashScreen-Image: splash.png 4 | -------------------------------------------------------------------------------- /objectbox-models/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", 3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", 4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", 5 | "entities": [ 6 | { 7 | "id": "1:7108676259396977645", 8 | "lastPropertyId": "7:2295757526968835612", 9 | "name": "ApiBean", 10 | "properties": [ 11 | { 12 | "id": "1:1639868835150814962", 13 | "name": "id" 14 | }, 15 | { 16 | "id": "2:2080580518748061893", 17 | "name": "method" 18 | }, 19 | { 20 | "id": "3:2834421018815822379", 21 | "name": "url" 22 | }, 23 | { 24 | "id": "4:4825371467230632842", 25 | "name": "paramsMap" 26 | }, 27 | { 28 | "id": "5:2063548344561858784", 29 | "name": "encryptType" 30 | }, 31 | { 32 | "id": "6:5429222793148795982", 33 | "indexId": "1:7522200330075218487", 34 | "name": "projectId" 35 | }, 36 | { 37 | "id": "7:2295757526968835612", 38 | "name": "createDate" 39 | } 40 | ], 41 | "relations": [] 42 | }, 43 | { 44 | "id": "2:6221161776531880629", 45 | "lastPropertyId": "3:5891365109221921321", 46 | "name": "ProjectBean", 47 | "properties": [ 48 | { 49 | "id": "1:3849416496976680262", 50 | "name": "id" 51 | }, 52 | { 53 | "id": "2:2968560084276041096", 54 | "indexId": "2:3527440894535392875", 55 | "name": "name" 56 | }, 57 | { 58 | "id": "3:5891365109221921321", 59 | "name": "baseUrlList" 60 | } 61 | ], 62 | "relations": [] 63 | }, 64 | { 65 | "id": "3:6642322354700112124", 66 | "lastPropertyId": "5:42282130757559747", 67 | "name": "TableBean", 68 | "properties": [ 69 | { 70 | "id": "1:7768808324752372527", 71 | "name": "id" 72 | }, 73 | { 74 | "id": "2:3447499673520710493", 75 | "name": "selected" 76 | }, 77 | { 78 | "id": "3:6991724854441645252", 79 | "name": "key" 80 | }, 81 | { 82 | "id": "4:3343448092562084023", 83 | "name": "value" 84 | }, 85 | { 86 | "id": "5:42282130757559747", 87 | "indexId": "3:1193464731902280295", 88 | "name": "projectId" 89 | } 90 | ], 91 | "relations": [] 92 | } 93 | ], 94 | "lastEntityId": "3:6642322354700112124", 95 | "lastIndexId": "3:1193464731902280295", 96 | "lastRelationId": "0:0", 97 | "lastSequenceId": "0:0", 98 | "modelVersion": 4, 99 | "modelVersionParserMinimum": 4, 100 | "retiredEntityUids": [], 101 | "retiredIndexUids": [], 102 | "retiredPropertyUids": [], 103 | "retiredRelationUids": [], 104 | "version": 1 105 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'apidebugger' 2 | 3 | -------------------------------------------------------------------------------- /splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/longforus/api-debugger/9bf3ebb379a0da2170e9389a8de562ae4ed9816a/splash.png -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/DefaultParamsDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/DefaultParamsDialog.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.longforus.apidebugger.MyValueHandler; 4 | import com.longforus.apidebugger.OB; 5 | import com.longforus.apidebugger.bean.TableBean; 6 | import com.longforus.apidebugger.bean.TableBean_; 7 | import java.awt.Dimension; 8 | import java.awt.Insets; 9 | import java.awt.event.ActionEvent; 10 | import java.awt.event.ActionListener; 11 | import java.awt.event.KeyEvent; 12 | import java.awt.event.KeyListener; 13 | import java.awt.event.WindowAdapter; 14 | import java.awt.event.WindowEvent; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import javax.swing.JButton; 18 | import javax.swing.JComponent; 19 | import javax.swing.JDialog; 20 | import javax.swing.JPanel; 21 | import javax.swing.JScrollPane; 22 | import javax.swing.JTable; 23 | import javax.swing.KeyStroke; 24 | 25 | public class DefaultParamsDialog extends JDialog { 26 | private JPanel contentPane; 27 | private JButton buttonOK; 28 | private JButton buttonCancel; 29 | private JTable mTable; 30 | private JButton mAddRowButton; 31 | private JButton mDeleteRowButton; 32 | private JButton mClearButton; 33 | private ParamsTableModel mModel; 34 | 35 | public DefaultParamsDialog() { 36 | setContentPane(contentPane); 37 | setModal(true); 38 | getRootPane().setDefaultButton(buttonOK); 39 | setTitle("Set \"" + MyValueHandler.INSTANCE.getCurProject().getName() + "\" project default request params"); 40 | buttonOK.addActionListener(new ActionListener() { 41 | public void actionPerformed(ActionEvent e) { 42 | onOK(); 43 | } 44 | }); 45 | 46 | buttonCancel.addActionListener(new ActionListener() { 47 | public void actionPerformed(ActionEvent e) { 48 | onCancel(); 49 | } 50 | }); 51 | 52 | // call onCancel() when cross is clicked 53 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 54 | addWindowListener(new WindowAdapter() { 55 | public void windowClosing(WindowEvent e) { 56 | onCancel(); 57 | } 58 | }); 59 | 60 | // call onCancel() on ESCAPE 61 | contentPane.registerKeyboardAction(new ActionListener() { 62 | public void actionPerformed(ActionEvent e) { 63 | onCancel(); 64 | } 65 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 66 | 67 | mModel = MainPanel.resetParamsTbModel(mTable); 68 | mTable.addKeyListener(new KeyListener() { 69 | @Override 70 | public void keyTyped(KeyEvent e) { 71 | 72 | } 73 | 74 | @Override 75 | public void keyPressed(KeyEvent e) { 76 | 77 | } 78 | 79 | @Override 80 | public void keyReleased(KeyEvent e) { 81 | if (e.getKeyCode() == KeyEvent.VK_DELETE) { 82 | mModel.removeRow(mTable.getSelectedRow()); 83 | } 84 | } 85 | }); 86 | mAddRowButton.addActionListener(e -> { 87 | mModel.addEmptyRow(); 88 | mTable.requestFocus(); 89 | int index = mModel.getRowCount() - 1; 90 | mTable.setRowSelectionInterval(index, index);//最后一行获得焦点 91 | mTable.editCellAt(index, 1); 92 | }); 93 | mDeleteRowButton.addActionListener(e -> mModel.removeRow(mTable.getSelectedRow())); 94 | mClearButton.addActionListener(e -> mModel.setData(new ArrayList<>())); 95 | mModel.setData(MyValueHandler.INSTANCE.getCurProject().getDefaultParams()); 96 | } 97 | 98 | private void onOK() { 99 | // save 100 | //ArrayList result = new ArrayList<>(mTable.getRowCount()); 101 | //for (int i = 0; i < mTable.getRowCount(); i++) { 102 | // Boolean select = (Boolean) mTable.getValueAt(i, 0); 103 | // String key = (String) mTable.getValueAt(i, 1); 104 | // String value = (String) mTable.getValueAt(i, 2); 105 | // TableBean bean = new TableBean(select, key, value); 106 | // bean.setId(bean.hashCode()); 107 | // result.add(bean); 108 | //} 109 | List collect = mModel.getData(); 110 | collect.forEach(tableBean -> tableBean.setId(tableBean.hashCode())); 111 | MyValueHandler.INSTANCE.getCurProject().setDefaultParams(collect); 112 | OB.paramsBox.remove(OB.paramsBox.query().equal(TableBean_.projectId, MyValueHandler.INSTANCE.getCurProject().getId()).build().find()); 113 | if (collect.size() > 0) { 114 | OB.paramsBox.put(collect); 115 | } 116 | dispose(); 117 | } 118 | 119 | private void onCancel() { 120 | // add your code here if necessary 121 | dispose(); 122 | } 123 | 124 | 125 | { 126 | // GUI initializer generated by IntelliJ IDEA GUI Designer 127 | // >>> IMPORTANT!! <<< 128 | // DO NOT EDIT OR ADD ANY CODE HERE! 129 | $$$setupUI$$$(); 130 | } 131 | 132 | /** 133 | * Method generated by IntelliJ IDEA GUI Designer 134 | * >>> IMPORTANT!! <<< 135 | * DO NOT edit this method OR call it in your code! 136 | * 137 | * @noinspection ALL 138 | */ 139 | private void $$$setupUI$$$() { 140 | contentPane = new JPanel(); 141 | contentPane.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); 142 | contentPane.setPreferredSize(new Dimension(501, 356)); 143 | final JPanel panel1 = new JPanel(); 144 | panel1.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 145 | contentPane.add(panel1, new com.intellij.uiDesigner.core.GridConstraints(1, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 146 | com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, 147 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); 148 | final com.intellij.uiDesigner.core.Spacer spacer1 = new com.intellij.uiDesigner.core.Spacer(); 149 | panel1.add(spacer1, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 150 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 151 | final JPanel panel2 = new JPanel(); 152 | panel2.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); 153 | panel1.add(panel2, new com.intellij.uiDesigner.core.GridConstraints(0, 1, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 154 | com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, 155 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 156 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 157 | buttonOK = new JButton(); 158 | buttonOK.setText("OK"); 159 | panel2.add(buttonOK, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 160 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 161 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 162 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 163 | buttonCancel = new JButton(); 164 | buttonCancel.setText("Cancel"); 165 | panel2.add(buttonCancel, new com.intellij.uiDesigner.core.GridConstraints(0, 1, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 166 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 167 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 168 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 169 | final JPanel panel3 = new JPanel(); 170 | panel3.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 3, new Insets(0, 0, 0, 0), -1, -1)); 171 | contentPane.add(panel3, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 172 | com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, 173 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 174 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 175 | mAddRowButton = new JButton(); 176 | mAddRowButton.setText("Add Row"); 177 | panel3.add(mAddRowButton, new com.intellij.uiDesigner.core.GridConstraints(1, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 178 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 179 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 180 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 181 | mDeleteRowButton = new JButton(); 182 | mDeleteRowButton.setText("Delete Row"); 183 | panel3.add(mDeleteRowButton, new com.intellij.uiDesigner.core.GridConstraints(1, 1, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 184 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 185 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 186 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 187 | mClearButton = new JButton(); 188 | mClearButton.setText("Clear"); 189 | panel3.add(mClearButton, new com.intellij.uiDesigner.core.GridConstraints(1, 2, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 190 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 191 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 192 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 193 | final JScrollPane scrollPane1 = new JScrollPane(); 194 | panel3.add(scrollPane1, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 3, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 195 | com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, 196 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, 197 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 198 | mTable = new JTable(); 199 | mTable.setRowHeight(25); 200 | scrollPane1.setViewportView(mTable); 201 | } 202 | 203 | /** @noinspection ALL */ 204 | public JComponent $$$getRootComponent$$$() { 205 | return contentPane; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/DeleteBtnComboBoxRenderer.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Component; 5 | import java.util.function.Consumer; 6 | import javax.swing.DefaultListCellRenderer; 7 | import javax.swing.JButton; 8 | import javax.swing.JLabel; 9 | import javax.swing.JList; 10 | import javax.swing.JPanel; 11 | import javax.swing.ListCellRenderer; 12 | 13 | 14 | @Deprecated 15 | public class DeleteBtnComboBoxRenderer implements ListCellRenderer { 16 | 17 | private DefaultListCellRenderer defaultCellRenderer = new DefaultListCellRenderer(); 18 | 19 | private Consumer onDelete; 20 | 21 | public DeleteBtnComboBoxRenderer(Consumer onDelete) { 22 | super(); 23 | this.onDelete = onDelete; 24 | } 25 | 26 | public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { 27 | JLabel renderer = (JLabel) defaultCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 28 | JPanel panel = new JPanel(new BorderLayout()); 29 | panel.add(renderer,BorderLayout.WEST); 30 | JButton x = new JButton("X"); 31 | x.setFocusable(true); 32 | x.setEnabled(true); 33 | x.requestFocus(); 34 | //点击事件无法被响应 35 | x.addActionListener(e -> { 36 | if (onDelete!=null) { 37 | onDelete.accept(value); 38 | } 39 | }); 40 | panel.add(x,BorderLayout.EAST); 41 | panel.setFocusable(true); 42 | panel.setEnabled(true); 43 | return panel; 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/JSONEditPanel.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonNull; 5 | import com.google.gson.JsonParser; 6 | import com.longforus.apidebugger.MyValueHandler; 7 | import java.awt.BorderLayout; 8 | import java.awt.event.MouseEvent; 9 | import java.util.ArrayList; 10 | import java.util.Enumeration; 11 | import java.util.List; 12 | import javax.swing.JOptionPane; 13 | import javax.swing.JPanel; 14 | import javax.swing.JScrollPane; 15 | import javax.swing.JTree; 16 | import javax.swing.event.MouseInputAdapter; 17 | import javax.swing.event.TreeSelectionListener; 18 | import javax.swing.tree.DefaultTreeModel; 19 | import javax.swing.tree.TreePath; 20 | import javax.swing.tree.TreeSelectionModel; 21 | 22 | /** 23 | * Implements the embeddable swing widget for editing JSON. 24 | * 25 | * This class is not thread safe. 26 | * 27 | * @author Stephen Owens 28 | * 29 | * 30 | *

31 | * Copyright 2011 Stephen P. Owens : steve@doitnext.com 32 | *

33 | *

34 | * Licensed under the Apache License, Version 2.0 (the "License"); 35 | * you may not use this file except in compliance with the License. 36 | * You may obtain a copy of the License at 37 | *

38 | *

39 | * http://www.apache.org/licenses/LICENSE-2.0 40 | *

41 | *

42 | * Unless required by applicable law or agreed to in writing, software 43 | * distributed under the License is distributed on an "AS IS" BASIS, 44 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | * See the License for the specific language governing permissions and 46 | * limitations under the License. 47 | *

48 | */ 49 | public class JSONEditPanel extends JPanel { 50 | /** 51 | * Using default serial version id. 52 | */ 53 | private static final long serialVersionUID = 1L; 54 | 55 | // Default visibility to let classes in this module access this element directly. 56 | JTree jTree; 57 | 58 | public enum UpdateType { REPLACE, INSERT, APPEND, AS_CHILD }; 59 | public enum AllowedOps { REPLACE, INSERT, APPEND, AS_CHILD, DELETE, RENAME, GET_JSON }; 60 | 61 | /** 62 | * Default constructor for the JSONEditPanel object. 63 | * 64 | * Creates an empty tree. 65 | */ 66 | public JSONEditPanel() { 67 | setLayout(new BorderLayout()); 68 | JSONJTreeNode root = new JSONJTreeNode(null, -1, new JsonNull()); 69 | jTree = new JTree(root); 70 | jTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 71 | add(new JScrollPane(jTree), BorderLayout.CENTER); 72 | } 73 | 74 | /** 75 | * Allow the owner of the component to listen for tree selection events. 76 | * 77 | * @param tsl A handler for tree selection events 78 | */ 79 | public void addTreeSelectionListener(TreeSelectionListener tsl) { 80 | jTree.addTreeSelectionListener(tsl); 81 | jTree.addMouseListener(new MouseInputAdapter() { 82 | @Override 83 | public void mouseClicked(MouseEvent e) { 84 | super.mouseClicked(e); 85 | if (e.getButton() == MouseEvent.BUTTON3) { 86 | JSONJTreeNode component = (JSONJTreeNode) jTree.getLastSelectedPathComponent(); 87 | MyValueHandler.INSTANCE.setSysClipboardText(component.toSSearchStr()); 88 | } 89 | } 90 | }); 91 | } 92 | 93 | /** 94 | * Replaces the current tree structure in the contained JTree component 95 | * with a new structure built from the JSON string provided. 96 | * 97 | * @param json - the JSON to update the tree control with 98 | * @param updateType - if a node is selected when this method is called 99 | * then the updateType indicates where the new json goes: 100 | * REPLACE - replace current selected node with new JSON 101 | * INSERT - place node before selected node 102 | * APPEND - place node after selected node 103 | * AS_CHILD - append to end of child nodes or insert new child node if no 104 | * children present. Selected node must be of type ARRAY or OBJECT 105 | */ 106 | @SuppressWarnings("unchecked") 107 | public void setJson(String json, UpdateType updateType) { 108 | TreePath selection = jTree.getSelectionPath(); 109 | if(selection == null) { 110 | if(updateType == UpdateType.REPLACE) { 111 | JsonElement root = new JsonParser().parse(json); 112 | JSONJTreeNode rootNode = new JSONJTreeNode(null, -1, root); 113 | jTree.setModel(new DefaultTreeModel(rootNode)); 114 | } else { 115 | JOptionPane.showMessageDialog(this, 116 | "Only replace JSON and get JSON are supported when no node is selected.", 117 | "Notice", JOptionPane.INFORMATION_MESSAGE); 118 | } 119 | } else { 120 | JSONJTreeNode selectedNode = (JSONJTreeNode) selection.getLastPathComponent(); 121 | JSONJTreeNode parent = (JSONJTreeNode)selectedNode.getParent(); 122 | switch(updateType) { 123 | case REPLACE: { 124 | if(parent == null) { 125 | JsonElement root = new JsonParser().parse(json); 126 | JSONJTreeNode rootNode = new JSONJTreeNode(null, -1, root); 127 | jTree.setModel(new DefaultTreeModel(rootNode)); 128 | return; 129 | } 130 | JsonElement root = new JsonParser().parse(json); 131 | JSONJTreeNode replacementNode = new JSONJTreeNode(selectedNode.fieldName, selectedNode.index, root); 132 | int index = selectedNode.getParent().getIndex(selectedNode); 133 | selectedNode.removeFromParent(); 134 | parent.insert(replacementNode, index); 135 | ((DefaultTreeModel)jTree.getModel()).reload(parent); 136 | } 137 | break; 138 | case INSERT: 139 | case APPEND: { 140 | if(parent == null) { 141 | JOptionPane.showMessageDialog(this, 142 | "You cannot append to the root element.", "Notice", JOptionPane.INFORMATION_MESSAGE); 143 | return; 144 | } 145 | JsonElement root = new JsonParser().parse(json); 146 | JSONJTreeNode replacementNode = new JSONJTreeNode(selectedNode.fieldName, selectedNode.index, root); 147 | int index = selectedNode.getParent().getIndex(selectedNode); 148 | if(updateType.equals(UpdateType.APPEND)) 149 | index++; 150 | parent.insert(replacementNode, index); 151 | ((DefaultTreeModel)jTree.getModel()).reload(parent); 152 | } 153 | break; 154 | case AS_CHILD: { 155 | JsonElement root = new JsonParser().parse(json); 156 | String fieldName = null; 157 | int arrayIndex = -1; 158 | if(selectedNode.dataType.equals(JSONJTreeNode.DataType.ARRAY)) { 159 | Enumeration en = selectedNode.children(); 160 | int count = 0; 161 | while(en.hasMoreElements()) { 162 | en.nextElement(); 163 | count++; 164 | } 165 | arrayIndex = count; 166 | } 167 | else if(selectedNode.dataType.equals(JSONJTreeNode.DataType.OBJECT)) 168 | fieldName = "new-field"; 169 | else { 170 | JOptionPane.showMessageDialog(this, 171 | "Vaue type entities can not have children.", "Notice", JOptionPane.INFORMATION_MESSAGE); 172 | return; 173 | } 174 | JSONJTreeNode newNode = new JSONJTreeNode(fieldName, arrayIndex, root); 175 | selectedNode.add(newNode); 176 | ((DefaultTreeModel)jTree.getModel()).reload(selectedNode); 177 | } 178 | break; 179 | } 180 | } 181 | } 182 | 183 | /** 184 | * Renames the selected node if it is a renameable node. 185 | */ 186 | public void renameNode() { 187 | TreePath selection = jTree.getSelectionPath(); 188 | if(selection == null){ 189 | JOptionPane.showMessageDialog(this, 190 | "No node is selected for rename.", "Notice", JOptionPane.INFORMATION_MESSAGE); 191 | return; 192 | } 193 | JSONJTreeNode node = (JSONJTreeNode) selection.getLastPathComponent(); 194 | JSONJTreeNode parent = (JSONJTreeNode) node.getParent(); 195 | if(parent == null) { 196 | JOptionPane.showMessageDialog(this, 197 | "It is not possible to assign a name to the root node.", "Notice", JOptionPane.INFORMATION_MESSAGE); 198 | return; 199 | } 200 | if(!parent.dataType.equals(JSONJTreeNode.DataType.OBJECT)){ 201 | JOptionPane.showMessageDialog(this, 202 | "Only object fields may be renamed.", "Notice", JOptionPane.INFORMATION_MESSAGE); 203 | return; 204 | } 205 | 206 | String newName = JOptionPane.showInputDialog ( "Enter new name for " + node.fieldName ); 207 | node.fieldName = newName; 208 | ((DefaultTreeModel)jTree.getModel()).nodeChanged(node); 209 | } 210 | 211 | /** 212 | * Deletes selected node or sets entire model to null if root node or no node is selected. 213 | */ 214 | public void deleteNode() { 215 | TreePath selection = jTree.getSelectionPath(); 216 | if(selection == null) { 217 | // Replace root with emptyness 218 | jTree.setModel(new DefaultTreeModel(new JSONJTreeNode(null, -1, new JsonNull()))); 219 | } else { 220 | JSONJTreeNode node = (JSONJTreeNode) selection.getLastPathComponent(); 221 | JSONJTreeNode parent = (JSONJTreeNode) node.getParent(); 222 | if(parent == null) { 223 | // Replace root with emptyness 224 | jTree.setModel(new DefaultTreeModel(new JSONJTreeNode(null, -1, new JsonNull()))); 225 | } else { 226 | node.removeFromParent(); 227 | ((DefaultTreeModel)jTree.getModel()).reload(parent); 228 | } 229 | } 230 | } 231 | 232 | /** 233 | * Returns the current JSON from the JTree 234 | * 235 | * @return the JSON as it is represented by the current state of the Tree model 236 | */ 237 | public String getJson() { 238 | TreePath selection = jTree.getSelectionPath(); 239 | JSONJTreeNode node = null; 240 | if(selection == null) { 241 | ((DefaultTreeModel)jTree.getModel()).reload(); 242 | node = (JSONJTreeNode) jTree.getModel().getRoot(); 243 | } else { 244 | ((DefaultTreeModel)jTree.getModel()).reload(node); 245 | node = (JSONJTreeNode) selection.getLastPathComponent(); 246 | } 247 | if(node != null) 248 | return node.asJsonElement().toString(); 249 | else 250 | return null; 251 | } 252 | 253 | /** 254 | * Determine what can currently be asked of the component in terms of tree modifiation operations. 255 | * 256 | * @return a list of AllowedOps enumerations to indicate the commands that are currently allowed upon the Component 257 | */ 258 | public List getAllowedOperations() { 259 | List result = new ArrayList(); 260 | result.add(AllowedOps.REPLACE); 261 | result.add(AllowedOps.GET_JSON); 262 | 263 | TreePath selection = jTree.getSelectionPath(); 264 | if(selection == null) 265 | return result; 266 | 267 | JSONJTreeNode selectedNode = (JSONJTreeNode) selection.getLastPathComponent(); 268 | JSONJTreeNode parentNode = null; 269 | 270 | if(selectedNode != null) { 271 | result.add(AllowedOps.DELETE); 272 | parentNode = (JSONJTreeNode) selectedNode.getParent(); 273 | } 274 | if(parentNode != null) { 275 | result.add(AllowedOps.APPEND); 276 | result.add(AllowedOps.INSERT); 277 | } 278 | if(selectedNode.dataType.equals(JSONJTreeNode.DataType.ARRAY) || 279 | selectedNode.dataType.equals(JSONJTreeNode.DataType.OBJECT) ) 280 | result.add(AllowedOps.AS_CHILD); 281 | if((parentNode != null) && (parentNode.dataType.equals(JSONJTreeNode.DataType.OBJECT))) 282 | result.add(AllowedOps.RENAME); 283 | return result; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/JsonEditPanel.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonNull; 5 | import com.google.gson.JsonParser; 6 | import com.longforus.apidebugger.MyValueHandler; 7 | import java.awt.BorderLayout; 8 | import java.awt.event.MouseEvent; 9 | import java.util.ArrayList; 10 | import java.util.Enumeration; 11 | import java.util.List; 12 | import javax.swing.JMenuItem; 13 | import javax.swing.JOptionPane; 14 | import javax.swing.JPanel; 15 | import javax.swing.JPopupMenu; 16 | import javax.swing.JScrollPane; 17 | import javax.swing.JTree; 18 | import javax.swing.event.MouseInputAdapter; 19 | import javax.swing.event.TreeSelectionListener; 20 | import javax.swing.tree.DefaultTreeModel; 21 | import javax.swing.tree.TreePath; 22 | import javax.swing.tree.TreeSelectionModel; 23 | 24 | /** 25 | * Implements the embeddable swing widget for editing JSON. 26 | * 27 | * This class is not thread safe. 28 | * 29 | * @author Stephen Owens 30 | * 31 | * 32 | *

33 | * Copyright 2011 Stephen P. Owens : steve@doitnext.com 34 | *

35 | *

36 | * Licensed under the Apache License, Version 2.0 (the "License"); 37 | * you may not use this file except in compliance with the License. 38 | * You may obtain a copy of the License at 39 | *

40 | *

41 | * http://www.apache.org/licenses/LICENSE-2.0 42 | *

43 | *

44 | * Unless required by applicable law or agreed to in writing, software 45 | * distributed under the License is distributed on an "AS IS" BASIS, 46 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | * See the License for the specific language governing permissions and 48 | * limitations under the License. 49 | *

50 | */ 51 | @Deprecated 52 | public class JsonEditPanel extends JPanel { 53 | /** 54 | * Using default serial version id. 55 | */ 56 | private static final long serialVersionUID = 1L; 57 | 58 | // Default visibility to let classes in this module access this element directly. 59 | JTree jTree; 60 | private final JPopupMenu mPopupMenu; 61 | 62 | public enum UpdateType { 63 | REPLACE, 64 | INSERT, 65 | APPEND, 66 | AS_CHILD 67 | } 68 | 69 | ; 70 | 71 | public enum AllowedOps { 72 | REPLACE, 73 | INSERT, 74 | APPEND, 75 | AS_CHILD, 76 | DELETE, 77 | RENAME, 78 | GET_JSON 79 | } 80 | 81 | ; 82 | 83 | /** 84 | * Default constructor for the JsonEditPanel object. 85 | * 86 | * Creates an empty tree. 87 | */ 88 | public JsonEditPanel() { 89 | setLayout(new BorderLayout()); 90 | JsonJTreeNode root = new JsonJTreeNode(null, -1, new JsonNull()); 91 | jTree = new JTree(root); 92 | jTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 93 | mPopupMenu = new JPopupMenu(); 94 | JMenuItem copy = new JMenuItem("Copy"); 95 | copy.addActionListener(e -> { 96 | JsonJTreeNode component = (JsonJTreeNode) jTree.getLastSelectedPathComponent(); 97 | String text; 98 | switch (component.dataType) { 99 | case OBJECT: 100 | case ARRAY: 101 | text = component.asJsonElement().toString(); 102 | break; 103 | default: 104 | text = component.toSSearchStr(); 105 | } 106 | MyValueHandler.INSTANCE.setSysClipboardText(text); 107 | }); 108 | mPopupMenu.add(copy); 109 | jTree.addMouseListener(new MouseInputAdapter() { 110 | @Override 111 | public void mouseClicked(MouseEvent e) { 112 | super.mouseClicked(e); 113 | if (e.getButton() == MouseEvent.BUTTON3) { 114 | mPopupMenu.show(jTree, e.getX(), e.getY()); 115 | } 116 | } 117 | }); 118 | add(new JScrollPane(jTree), BorderLayout.CENTER); 119 | } 120 | 121 | /** 122 | * Allow the owner of the component to listen for tree selection events. 123 | * 124 | * @param tsl A handler for tree selection events 125 | */ 126 | public void addTreeSelectionListener(TreeSelectionListener tsl) { 127 | jTree.addTreeSelectionListener(tsl); 128 | } 129 | 130 | /** 131 | * Replaces the current tree structure in the contained JTree component 132 | * with a new structure built from the JSON string provided. 133 | * 134 | * @param json - the JSON to update the tree control with 135 | * @param updateType - if a node is selected when this method is called 136 | * then the updateType indicates where the new json goes: 137 | * REPLACE - replace current selected node with new JSON 138 | * INSERT - place node before selected node 139 | * APPEND - place node after selected node 140 | * AS_CHILD - append to end of child nodes or insert new child node if no 141 | * children present. Selected node must be of type ARRAY or OBJECT 142 | */ 143 | @SuppressWarnings("unchecked") 144 | public void setJson(String json, UpdateType updateType) { 145 | TreePath selection = jTree.getSelectionPath(); 146 | if (selection == null) { 147 | if (updateType == UpdateType.REPLACE) { 148 | JsonElement root = new JsonParser().parse(json); 149 | JsonJTreeNode rootNode = new JsonJTreeNode(null, -1, root); 150 | jTree.setModel(new DefaultTreeModel(rootNode)); 151 | } else { 152 | JOptionPane.showMessageDialog(this, "Only replace JSON and get JSON are supported when no node is selected.", "Notice", JOptionPane.INFORMATION_MESSAGE); 153 | } 154 | } else { 155 | JsonJTreeNode selectedNode = (JsonJTreeNode) selection.getLastPathComponent(); 156 | JsonJTreeNode parent = (JsonJTreeNode) selectedNode.getParent(); 157 | switch (updateType) { 158 | case REPLACE: { 159 | if (parent == null) { 160 | JsonElement root = new JsonParser().parse(json); 161 | JsonJTreeNode rootNode = new JsonJTreeNode(null, -1, root); 162 | jTree.setModel(new DefaultTreeModel(rootNode)); 163 | return; 164 | } 165 | JsonElement root = new JsonParser().parse(json); 166 | JsonJTreeNode replacementNode = new JsonJTreeNode(selectedNode.fieldName, selectedNode.index, root); 167 | int index = selectedNode.getParent().getIndex(selectedNode); 168 | selectedNode.removeFromParent(); 169 | parent.insert(replacementNode, index); 170 | ((DefaultTreeModel) jTree.getModel()).reload(parent); 171 | } 172 | break; 173 | case INSERT: 174 | case APPEND: { 175 | if (parent == null) { 176 | JOptionPane.showMessageDialog(this, "You cannot append to the root element.", "Notice", JOptionPane.INFORMATION_MESSAGE); 177 | return; 178 | } 179 | JsonElement root = new JsonParser().parse(json); 180 | JsonJTreeNode replacementNode = new JsonJTreeNode(selectedNode.fieldName, selectedNode.index, root); 181 | int index = selectedNode.getParent().getIndex(selectedNode); 182 | if (updateType.equals(UpdateType.APPEND)) { 183 | index++; 184 | } 185 | parent.insert(replacementNode, index); 186 | ((DefaultTreeModel) jTree.getModel()).reload(parent); 187 | } 188 | break; 189 | case AS_CHILD: { 190 | JsonElement root = new JsonParser().parse(json); 191 | String fieldName = null; 192 | int arrayIndex = -1; 193 | if (selectedNode.dataType.equals(JsonJTreeNode.DataType.ARRAY)) { 194 | Enumeration en = selectedNode.children(); 195 | int count = 0; 196 | while (en.hasMoreElements()) { 197 | en.nextElement(); 198 | count++; 199 | } 200 | arrayIndex = count; 201 | } else if (selectedNode.dataType.equals(JsonJTreeNode.DataType.OBJECT)) { 202 | fieldName = "new-field"; 203 | } else { 204 | JOptionPane.showMessageDialog(this, "Vaue type entities can not have children.", "Notice", JOptionPane.INFORMATION_MESSAGE); 205 | return; 206 | } 207 | JsonJTreeNode newNode = new JsonJTreeNode(fieldName, arrayIndex, root); 208 | selectedNode.add(newNode); 209 | ((DefaultTreeModel) jTree.getModel()).reload(selectedNode); 210 | } 211 | break; 212 | } 213 | } 214 | } 215 | 216 | /** 217 | * Renames the selected node if it is a renameable node. 218 | */ 219 | public void renameNode() { 220 | TreePath selection = jTree.getSelectionPath(); 221 | if (selection == null) { 222 | JOptionPane.showMessageDialog(this, "No node is selected for rename.", "Notice", JOptionPane.INFORMATION_MESSAGE); 223 | return; 224 | } 225 | JsonJTreeNode node = (JsonJTreeNode) selection.getLastPathComponent(); 226 | JsonJTreeNode parent = (JsonJTreeNode) node.getParent(); 227 | if (parent == null) { 228 | JOptionPane.showMessageDialog(this, "It is not possible to assign a name to the root node.", "Notice", JOptionPane.INFORMATION_MESSAGE); 229 | return; 230 | } 231 | if (!parent.dataType.equals(JsonJTreeNode.DataType.OBJECT)) { 232 | JOptionPane.showMessageDialog(this, "Only object fields may be renamed.", "Notice", JOptionPane.INFORMATION_MESSAGE); 233 | return; 234 | } 235 | 236 | String newName = JOptionPane.showInputDialog("Enter new name for " + node.fieldName); 237 | node.fieldName = newName; 238 | ((DefaultTreeModel) jTree.getModel()).nodeChanged(node); 239 | } 240 | 241 | /** 242 | * Deletes selected node or sets entire model to null if root node or no node is selected. 243 | */ 244 | public void deleteNode() { 245 | TreePath selection = jTree.getSelectionPath(); 246 | if (selection == null) { 247 | // Replace root with emptyness 248 | jTree.setModel(new DefaultTreeModel(new JsonJTreeNode(null, -1, new JsonNull()))); 249 | } else { 250 | JsonJTreeNode node = (JsonJTreeNode) selection.getLastPathComponent(); 251 | JsonJTreeNode parent = (JsonJTreeNode) node.getParent(); 252 | if (parent == null) { 253 | // Replace root with emptyness 254 | jTree.setModel(new DefaultTreeModel(new JsonJTreeNode(null, -1, new JsonNull()))); 255 | } else { 256 | node.removeFromParent(); 257 | ((DefaultTreeModel) jTree.getModel()).reload(parent); 258 | } 259 | } 260 | } 261 | 262 | /** 263 | * Returns the current JSON from the JTree 264 | * 265 | * @return the JSON as it is represented by the current state of the Tree model 266 | */ 267 | public String getJson() { 268 | TreePath selection = jTree.getSelectionPath(); 269 | JsonJTreeNode node = null; 270 | if (selection == null) { 271 | ((DefaultTreeModel) jTree.getModel()).reload(); 272 | node = (JsonJTreeNode) jTree.getModel().getRoot(); 273 | } else { 274 | ((DefaultTreeModel) jTree.getModel()).reload(node); 275 | node = (JsonJTreeNode) selection.getLastPathComponent(); 276 | } 277 | if (node != null) { 278 | return node.asJsonElement().toString(); 279 | } else { 280 | return null; 281 | } 282 | } 283 | 284 | /** 285 | * Determine what can currently be asked of the component in terms of tree modifiation operations. 286 | * 287 | * @return a list of AllowedOps enumerations to indicate the commands that are currently allowed upon the Component 288 | */ 289 | public List getAllowedOperations() { 290 | List result = new ArrayList(); 291 | result.add(AllowedOps.REPLACE); 292 | result.add(AllowedOps.GET_JSON); 293 | 294 | TreePath selection = jTree.getSelectionPath(); 295 | if (selection == null) { 296 | return result; 297 | } 298 | 299 | JsonJTreeNode selectedNode = (JsonJTreeNode) selection.getLastPathComponent(); 300 | JsonJTreeNode parentNode = null; 301 | 302 | if (selectedNode != null) { 303 | result.add(AllowedOps.DELETE); 304 | parentNode = (JsonJTreeNode) selectedNode.getParent(); 305 | } 306 | if (parentNode != null) { 307 | result.add(AllowedOps.APPEND); 308 | result.add(AllowedOps.INSERT); 309 | } 310 | if (selectedNode.dataType.equals(JsonJTreeNode.DataType.ARRAY) || selectedNode.dataType.equals(JsonJTreeNode.DataType.OBJECT)) { 311 | result.add(AllowedOps.AS_CHILD); 312 | } 313 | if ((parentNode != null) && (parentNode.dataType.equals(JsonJTreeNode.DataType.OBJECT))) { 314 | result.add(AllowedOps.RENAME); 315 | } 316 | return result; 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/JsonJTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | import com.google.gson.JsonSyntaxException; 8 | import java.util.Enumeration; 9 | import java.util.Iterator; 10 | import java.util.Map.Entry; 11 | import javax.swing.tree.DefaultMutableTreeNode; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | /** 15 | *

Provides the model for translating JsonElement into JTree data nodes. This class is not thread safe.

16 | * 17 | * @author Stephen Owens 18 | * 19 | *

Provides the model for translating JsonElement into JTree data nodes.

20 | * 21 | *

22 | * Copyright 2011 Stephen P. Owens : steve@doitnext.com 23 | *

24 | *

25 | * Licensed under the Apache License, Version 2.0 (the "License"); 26 | * you may not use this file except in compliance with the License. 27 | * You may obtain a copy of the License at 28 | *

29 | *

30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | *

32 | *

33 | * Unless required by applicable law or agreed to in writing, software 34 | * distributed under the License is distributed on an "AS IS" BASIS, 35 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | * See the License for the specific language governing permissions and 37 | * limitations under the License. 38 | *

39 | */ 40 | @Deprecated 41 | public class JsonJTreeNode extends DefaultMutableTreeNode { 42 | /** 43 | * Using default serial id. 44 | */ 45 | private static final long serialVersionUID = 1L; 46 | 47 | public enum DataType { 48 | ARRAY, 49 | OBJECT, 50 | VALUE 51 | } 52 | 53 | ; 54 | final DataType dataType; 55 | final int index; 56 | String fieldName; 57 | final String value; 58 | private int childCount = 0; 59 | 60 | public String getFieldName() { 61 | return fieldName; 62 | } 63 | 64 | /** 65 | * @param fieldName - name of field if applicable or null 66 | * @param index - index of element in the array or -1 if not part of an array 67 | * @param jsonElement - element to represent 68 | */ 69 | public JsonJTreeNode(String fieldName, int index, JsonElement jsonElement) { 70 | this.index = index; 71 | this.fieldName = fieldName; 72 | if (jsonElement.isJsonArray()) { 73 | this.dataType = DataType.ARRAY; 74 | this.value = jsonElement.toString(); 75 | populateChildren(jsonElement); 76 | } else if (jsonElement.isJsonObject()) { 77 | this.dataType = DataType.OBJECT; 78 | this.value = jsonElement.toString(); 79 | populateChildren(jsonElement); 80 | } else if (jsonElement.isJsonPrimitive()) { 81 | this.dataType = DataType.VALUE; 82 | this.value = jsonElement.toString(); 83 | } else if (jsonElement.isJsonNull()) { 84 | this.dataType = DataType.VALUE; 85 | this.value = jsonElement.toString(); 86 | } else { 87 | throw new IllegalArgumentException("jsonElement is an unknown element type."); 88 | } 89 | } 90 | 91 | private void populateChildren(JsonElement myJsonElement) { 92 | switch (dataType) { 93 | case ARRAY: 94 | int index = 0; 95 | JsonArray array = myJsonElement.getAsJsonArray(); 96 | childCount = array.size(); 97 | Iterator it = array.iterator(); 98 | while (it.hasNext()) { 99 | JsonElement element = it.next(); 100 | JsonJTreeNode childNode = new JsonJTreeNode(null, index, element); 101 | this.add(childNode); 102 | index++; 103 | } 104 | break; 105 | case OBJECT: 106 | JsonObject object = myJsonElement.getAsJsonObject(); 107 | childCount = object.size(); 108 | for (Entry entry : object.entrySet()) { 109 | JsonJTreeNode childNode = new JsonJTreeNode(entry.getKey(), -1, entry.getValue()); 110 | this.add(childNode); 111 | } 112 | break; 113 | default: 114 | throw new IllegalStateException("Internal coding error this should never happen."); 115 | } 116 | } 117 | 118 | public JsonElement asJsonElement() { 119 | StringBuilder sb = new StringBuilder(); 120 | buildJsonString(sb); 121 | String json = sb.toString().trim(); 122 | if (json.startsWith("{") || json.startsWith("[")) { 123 | return new JsonParser().parse(sb.toString()); 124 | } else { 125 | // Safety check the JSON, if it is of a named value object 126 | // We cheat a little if it is an orphan name value pair then 127 | // if we wrap it in {} chars it will parse if it isn't the parse 128 | // fails. 129 | String testValue = "{" + json + "}"; 130 | try { 131 | JsonElement wrapperElt = new JsonParser().parse(testValue); 132 | JsonObject obj = (JsonObject) wrapperElt; 133 | Iterator> it = obj.entrySet().iterator(); 134 | Entry entry = it.next(); 135 | return entry.getValue(); 136 | } catch (JsonSyntaxException jse) { 137 | JsonElement rawElement = new JsonParser().parse(json); 138 | return rawElement; 139 | } 140 | } 141 | } 142 | 143 | @SuppressWarnings("unchecked") 144 | private void buildJsonString(StringBuilder sb) { 145 | if (!StringUtils.isEmpty(this.fieldName)) { 146 | sb.append("\"" + this.fieldName + "\":"); 147 | } 148 | Enumeration children; 149 | switch (dataType) { 150 | case ARRAY: 151 | sb.append("["); 152 | children = this.children(); 153 | processNode(sb, children); 154 | sb.append("]"); 155 | break; 156 | case OBJECT: 157 | sb.append("{"); 158 | children = this.children(); 159 | processNode(sb, children); 160 | sb.append("}"); 161 | break; 162 | default: { 163 | // Use the JSON parser to parse the value for safety 164 | JsonElement elt = new JsonParser().parse(this.value); 165 | sb.append(elt.toString()); 166 | } 167 | } 168 | } 169 | 170 | private void processNode(StringBuilder sb, Enumeration children) { 171 | while (children.hasMoreElements()) { 172 | JsonJTreeNode child = (JsonJTreeNode) children.nextElement(); 173 | child.buildJsonString(sb); 174 | if (children.hasMoreElements()) { 175 | sb.append(","); 176 | } 177 | } 178 | } 179 | 180 | @Override 181 | public String toString() { 182 | switch (dataType) { 183 | case ARRAY: 184 | if (index >= 0) { 185 | return String.format("%d [%d]", index, childCount); 186 | }else if (fieldName != null) { 187 | return String.format("%s [%d]", fieldName, childCount); 188 | }else { 189 | return String.format("(%s)", dataType.name()); 190 | } 191 | case OBJECT: 192 | if (index >= 0) { 193 | return String.format("%d {%d}", index, childCount); 194 | }else if (fieldName != null) { 195 | return String.format("%s {%d}", fieldName, childCount); 196 | } else { 197 | return String.format("(%s)", dataType.name()); 198 | } 199 | default: 200 | if (index >= 0) { 201 | return String.format("[%d] %s", index, value); 202 | } else if (fieldName != null) { 203 | return String.format("%s: %s", fieldName, value); 204 | } else { 205 | return String.format("%s",value); 206 | } 207 | } 208 | } 209 | public String toSSearchStr() { 210 | switch (dataType) { 211 | case ARRAY: 212 | if (index >= 0) { 213 | return String.format("%d [%d]", index, childCount); 214 | }else if (fieldName != null) { 215 | return String.format("%s [%d]", fieldName, childCount); 216 | }else { 217 | return String.format("(%s)", dataType.name()); 218 | } 219 | case OBJECT: 220 | if (index >= 0) { 221 | return String.format("%d {%d}", index, childCount); 222 | }else if (fieldName != null) { 223 | return String.format("%s {%d}", fieldName, childCount); 224 | } else { 225 | return String.format("(%s)", dataType.name()); 226 | } 227 | default: 228 | if (index >= 0) { 229 | return String.format("[%d] %s", index, value); 230 | } else if (fieldName != null) { 231 | return String.format("\"%s\": %s", fieldName, value); 232 | } else { 233 | return String.format("%s",value); 234 | } 235 | } 236 | } 237 | /* 238 | @Override 239 | public String toString() { 240 | switch(dataType) { 241 | case ARRAY: 242 | case OBJECT: 243 | if(index >= 0) { 244 | return String.format("[%d] (%s)", index, dataType.name()); 245 | } else if(fieldName != null) { 246 | return String.format("%s (%s)", fieldName, dataType.name()); 247 | } else { 248 | return String.format("(%s)", dataType.name()); 249 | } 250 | default: 251 | if(index >= 0) { 252 | return String.format("[%d] %s", index, value); 253 | } else if(fieldName != null) { 254 | return String.format("%s: %s", fieldName, value); 255 | } else { 256 | return String.format("%s", value); 257 | } 258 | 259 | } 260 | } 261 | */ 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/JsonTreeCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import java.awt.Component; 4 | import javax.swing.JTree; 5 | import javax.swing.tree.DefaultTreeCellRenderer; 6 | 7 | /** 8 | * 自定义树描述类,将树的每个节点设置成不同的图标 9 | * 10 | * @author RuiLin.Xie - xKF24276 11 | */ 12 | @Deprecated 13 | public class JsonTreeCellRenderer extends DefaultTreeCellRenderer { 14 | /** 15 | * ID 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | 19 | /** 20 | * 重写父类DefaultTreeCellRenderer的方法 21 | */ 22 | @Override 23 | public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { 24 | 25 | //执行父类原型操作 26 | super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); 27 | 28 | setText(value.toString()); 29 | 30 | if (sel) { 31 | setForeground(getTextSelectionColor()); 32 | } else { 33 | setForeground(getTextNonSelectionColor()); 34 | } 35 | this.setIcon(null); 36 | return this; 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/MainPanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 |
272 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/MainPanel.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.jgoodies.forms.layout.CellConstraints; 4 | import com.jgoodies.forms.layout.FormLayout; 5 | import com.longforus.apidebugger.ExtFunKt; 6 | import com.longforus.apidebugger.MyValueHandler; 7 | import com.longforus.apidebugger.OB; 8 | import com.longforus.apidebugger.UIActionHandler; 9 | import com.longforus.apidebugger.UILifecycleHandler; 10 | import com.longforus.apidebugger.bean.ApiBean; 11 | import com.longforus.apidebugger.encrypt.IEncryptHandler; 12 | import com.teamdev.jxbrowser.chromium.Browser; 13 | import com.teamdev.jxbrowser.chromium.BrowserContext; 14 | import com.teamdev.jxbrowser.chromium.BrowserException; 15 | import com.teamdev.jxbrowser.chromium.JSValue; 16 | import com.teamdev.jxbrowser.chromium.ProtocolService; 17 | import com.teamdev.jxbrowser.chromium.URLResponse; 18 | import com.teamdev.jxbrowser.chromium.swing.BrowserView; 19 | import java.awt.Color; 20 | import java.awt.Dimension; 21 | import java.awt.FlowLayout; 22 | import java.awt.HeadlessException; 23 | import java.awt.Image; 24 | import java.awt.Insets; 25 | import java.awt.Toolkit; 26 | import java.awt.event.ItemEvent; 27 | import java.awt.event.KeyAdapter; 28 | import java.awt.event.KeyEvent; 29 | import java.awt.event.KeyListener; 30 | import java.awt.event.MouseEvent; 31 | import java.io.DataInputStream; 32 | import java.io.InputStream; 33 | import java.io.UnsupportedEncodingException; 34 | import java.net.MalformedURLException; 35 | import java.net.URL; 36 | import javax.swing.BorderFactory; 37 | import javax.swing.DefaultComboBoxModel; 38 | import javax.swing.ImageIcon; 39 | import javax.swing.JButton; 40 | import javax.swing.JComboBox; 41 | import javax.swing.JComponent; 42 | import javax.swing.JFrame; 43 | import javax.swing.JLabel; 44 | import javax.swing.JMenuItem; 45 | import javax.swing.JPanel; 46 | import javax.swing.JPopupMenu; 47 | import javax.swing.JProgressBar; 48 | import javax.swing.JScrollPane; 49 | import javax.swing.JTable; 50 | import javax.swing.JTextField; 51 | import javax.swing.JTextPane; 52 | import javax.swing.SizeRequirements; 53 | import javax.swing.event.MouseInputAdapter; 54 | import javax.swing.text.Element; 55 | import javax.swing.text.View; 56 | import javax.swing.text.ViewFactory; 57 | import javax.swing.text.html.HTMLEditorKit; 58 | import javax.swing.text.html.InlineView; 59 | import javax.swing.text.html.ParagraphView; 60 | 61 | /** 62 | * Created by XQ Yang on 8/30/2018 10:34 AM. 63 | * Description : 64 | */ 65 | 66 | public class MainPanel extends JFrame { 67 | private ParamsTableModel mParamsTableModel; 68 | private JComboBox mCbBaseUrl; 69 | private JButton mBtnSaveBaseUrl; 70 | private JComboBox mCbApiUrl; 71 | private JButton mBtnSend; 72 | private JComboBox mCbEncrypt; 73 | private JTable mTbParams; 74 | private JTextPane mTpInfo; 75 | private JLabel lbStatus; 76 | private JPanel baseP; 77 | private JButton btnDelUrl; 78 | private JButton btnDelApi; 79 | private JComboBox mCbMethod; 80 | private JButton btnAddRow; 81 | private JButton btnDelRow; 82 | private JButton btnClear; 83 | private BrowserView mBrowserView; 84 | private JButton mbtnDp; 85 | private JTextField tvTestCount; 86 | private JButton mBtnStartTest; 87 | private JProgressBar mPb; 88 | private Browser mBrowser; 89 | 90 | public JProgressBar getPb() { 91 | return mPb; 92 | } 93 | 94 | public JTextField getTvTestCount() { 95 | return tvTestCount; 96 | } 97 | 98 | public JComboBox getCbMethod() { 99 | return mCbMethod; 100 | } 101 | 102 | public JTable getTbParams() { 103 | return mTbParams; 104 | } 105 | 106 | public ParamsTableModel getParamsTableModel() { 107 | return mParamsTableModel; 108 | } 109 | 110 | public JComboBox getCbEncrypt() { 111 | return mCbEncrypt; 112 | } 113 | 114 | public JLabel getLbStatus() { 115 | return lbStatus; 116 | } 117 | 118 | public JTextPane getTpInfo() { 119 | return mTpInfo; 120 | } 121 | 122 | public JComboBox getCbBaseUrl() { 123 | return mCbBaseUrl; 124 | } 125 | 126 | public JComboBox getCbApiUrl() { 127 | return mCbApiUrl; 128 | } 129 | 130 | public int getSelectedEncryptID() { 131 | return MyValueHandler.INSTANCE.encryptIndex2Id(mCbEncrypt.getSelectedIndex()); 132 | } 133 | 134 | public int getSelectedMethodType() { 135 | return mCbMethod.getSelectedIndex(); 136 | } 137 | 138 | public MainPanel(String title) throws HeadlessException { 139 | super(title); 140 | $$$setupUI$$$(); 141 | 142 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 143 | String filename = getClass().getResource("") + "jsonView/send.png"; 144 | try { 145 | ImageIcon icon = new ImageIcon(new URL(filename)); 146 | setIconImage(icon.getImage()); 147 | icon.setImage(icon.getImage().getScaledInstance(68, 68, Image.SCALE_SMOOTH)); 148 | 149 | mBtnSend.setIcon(icon); 150 | } catch (MalformedURLException e) { 151 | e.printStackTrace(); 152 | } 153 | mCbMethod.setModel(new DefaultComboBoxModel(new String[] { "POST", "GET" })); 154 | //限制只能输入数字 155 | tvTestCount.addKeyListener(new KeyAdapter() { 156 | public void keyTyped(KeyEvent e) { 157 | int keyChar = e.getKeyChar(); 158 | if (keyChar < KeyEvent.VK_0 || keyChar > KeyEvent.VK_9) { 159 | e.consume(); //关键,屏蔽掉非法输入 160 | } 161 | } 162 | }); 163 | 164 | setContentPane(baseP); 165 | setJMenuBar(UILifecycleHandler.INSTANCE.getMenuBar()); 166 | initEvent(); 167 | //mCbApiUrl.setRenderer(new DeleteBtnComboBoxRenderer(o -> UIActionHandler.INSTANCE.onDelApiUrl((ApiBean) o))); 168 | //mCbBaseUrl.setRenderer(new DeleteBtnComboBoxRenderer(UIActionHandler.INSTANCE :: onDelBaseUrl)); 169 | initTextPanel(); 170 | initTable(); 171 | pack(); 172 | Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); 173 | int x = (int) screensize.getWidth() / 2 - baseP.getPreferredSize().width / 2; 174 | int y = 0; 175 | setLocation(x, y); 176 | setVisible(true); 177 | browserInit(); 178 | //Observable.create((ObservableOnSubscribe) emitter -> emitter.onNext("{}")).delay(10, TimeUnit.MILLISECONDS).subscribe( 179 | // s -> mBrowser.executeJavaScript("app.doc" + "._id || (document.location.href = document.location.pathname + \"#/new\", location.reload())")); 180 | 181 | } 182 | 183 | public void setJsonData(String jsonData) { 184 | JSValue app = mBrowser.executeJavaScriptAndReturnValue("app"); 185 | JSValue write = app.asObject().getProperty("setData"); 186 | write.asFunction().invoke(app.asObject(), jsonData); 187 | } 188 | 189 | private void browserInit() { 190 | BrowserContext browserContext = mBrowser.getContext(); 191 | ProtocolService protocolService = browserContext.getProtocolService(); 192 | protocolService.setProtocolHandler("jar", request -> { 193 | try { 194 | URLResponse response = new URLResponse(); 195 | URL path = new URL(request.getURL()); 196 | InputStream inputStream = path.openStream(); 197 | DataInputStream stream = new DataInputStream(inputStream); 198 | byte[] data = new byte[stream.available()]; 199 | stream.readFully(data); 200 | response.setData(data); 201 | String mimeType = getMimeType(path.toString()); 202 | response.getHeaders().setHeader("Content-Type", mimeType); 203 | return response; 204 | } catch (Exception ignored) { 205 | } 206 | return null; 207 | }); 208 | 209 | // Assume that we need to load a resource related to this class in the JAR file 210 | String url = getClass().getResource("") + "jsonView/index.html"; 211 | mBrowser.loadURL(url); 212 | } 213 | 214 | private static String getMimeType(String path) { 215 | if (path.endsWith(".html")) { 216 | return "text/html"; 217 | } 218 | if (path.endsWith(".css")) { 219 | return "text/css"; 220 | } 221 | if (path.endsWith(".js")) { 222 | return "text/javascript"; 223 | } 224 | if (path.endsWith(".png")) { 225 | return "image/png"; 226 | } 227 | if (path.endsWith(".svg")) { 228 | return "image/svg+xml"; 229 | } 230 | if (path.endsWith(".ico")) { 231 | return "image/x-icon"; 232 | } 233 | return "text/html"; 234 | } 235 | 236 | private void initEvent() { 237 | mBtnSaveBaseUrl.addActionListener(e -> UIActionHandler.INSTANCE.onSaveBaseUrl(mCbBaseUrl.getModel().getSelectedItem())); 238 | btnDelUrl.addActionListener(e -> UIActionHandler.INSTANCE.onDelBaseUrl(mCbBaseUrl.getModel().getSelectedItem())); 239 | btnDelApi.addActionListener(e -> UIActionHandler.INSTANCE.onDelApiUrl((ApiBean) mCbApiUrl.getModel().getSelectedItem())); 240 | mCbApiUrl.addItemListener(e -> { 241 | if (e.getStateChange() == ItemEvent.SELECTED) { 242 | if (e.getItem() instanceof ApiBean) { 243 | UIActionHandler.INSTANCE.onApiItemChanged(((ApiBean) e.getItem())); 244 | } 245 | } 246 | }); 247 | mBtnSend.addActionListener(e -> UIActionHandler.INSTANCE.onSend()); 248 | 249 | mCbMethod.addItemListener(e -> UIActionHandler.INSTANCE.onMethodChanged(mCbMethod.getSelectedIndex())); 250 | mCbEncrypt.addItemListener(e -> UIActionHandler.INSTANCE.onEncryptTypeChanged(((IEncryptHandler) e.getItem()).getTypeCode())); 251 | mbtnDp.addActionListener(e -> showDefaultParamsDialog()); 252 | mBtnStartTest.addActionListener(e -> UIActionHandler.INSTANCE.onStartTest()); 253 | } 254 | 255 | private void showDefaultParamsDialog() { 256 | if (MyValueHandler.INSTANCE.getCurProject() == null) { 257 | ExtFunKt.showErrorMsg("Please create the project first"); 258 | return; 259 | } 260 | DefaultParamsDialog paramsDialog = new DefaultParamsDialog(); 261 | paramsDialog.pack(); 262 | paramsDialog.setLocation((getX() + getWidth() / 2) - (paramsDialog.getWidth() / 2), getY() + getHeight() / 2 - paramsDialog.getHeight() / 2); 263 | paramsDialog.setVisible(true); 264 | } 265 | 266 | private void initTextPanel() { 267 | mTpInfo.addMouseListener(new MouseInputAdapter() { 268 | @Override 269 | public void mouseClicked(MouseEvent e) { 270 | super.mouseClicked(e); 271 | if (e.getButton() == MouseEvent.BUTTON3) { 272 | JPopupMenu menu = new JPopupMenu("Clear"); 273 | JMenuItem clear1 = menu.add(new JMenuItem("clear")); 274 | clear1.addActionListener(e1 -> mTpInfo.setText("")); 275 | menu.show(mTpInfo, e.getX(), e.getY()); 276 | } 277 | } 278 | }); 279 | //支持自动换行 280 | HTMLEditorKit editorKit = new HTMLEditorKit() { 281 | @Override 282 | public ViewFactory getViewFactory() { 283 | 284 | return new HTMLFactory() { 285 | public View create(Element e) { 286 | View v = super.create(e); 287 | if (v instanceof InlineView) { 288 | return new InlineView(e) { 289 | public int getBreakWeight(int axis, float pos, float len) { 290 | return GoodBreakWeight; 291 | } 292 | 293 | public View breakView(int axis, int p0, float pos, float len) { 294 | if (axis == View.X_AXIS) { 295 | checkPainter(); 296 | int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len); 297 | if (p0 == getStartOffset() && p1 == getEndOffset()) { 298 | return this; 299 | } 300 | return createFragment(p0, p1); 301 | } 302 | return this; 303 | } 304 | }; 305 | } else if (v instanceof ParagraphView) { 306 | return new ParagraphView(e) { 307 | protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) { 308 | if (r == null) { 309 | r = new SizeRequirements(); 310 | } 311 | float pref = layoutPool.getPreferredSpan(axis); 312 | float min = layoutPool.getMinimumSpan(axis); 313 | // Don't include insets, Box.getXXXSpan will include them. 314 | r.minimum = (int) min; 315 | r.preferred = Math.max(r.minimum, (int) pref); 316 | r.maximum = Integer.MAX_VALUE; 317 | r.alignment = 0.5f; 318 | return r; 319 | } 320 | }; 321 | } 322 | return v; 323 | } 324 | }; 325 | } 326 | }; 327 | mTpInfo.setEditorKit(editorKit); 328 | } 329 | 330 | public void resetParamsTbModel() { 331 | mParamsTableModel = MainPanel.resetParamsTbModel(mTbParams); 332 | } 333 | 334 | public static ParamsTableModel resetParamsTbModel(JTable table) { 335 | ParamsTableModel model = new ParamsTableModel(); 336 | table.setModel(model); 337 | table.getColumnModel().getColumn(0).setPreferredWidth(50); 338 | table.getColumnModel().getColumn(1).setPreferredWidth(120); 339 | table.getColumnModel().getColumn(2).setPreferredWidth(350); 340 | return model; 341 | } 342 | 343 | private void initTable() { 344 | mParamsTableModel = resetParamsTbModel(mTbParams); 345 | mTbParams.addKeyListener(new KeyListener() { 346 | @Override 347 | public void keyTyped(KeyEvent e) { 348 | 349 | } 350 | 351 | @Override 352 | public void keyPressed(KeyEvent e) { 353 | 354 | } 355 | 356 | @Override 357 | public void keyReleased(KeyEvent e) { 358 | if (e.getKeyCode() == KeyEvent.VK_DELETE) { 359 | mParamsTableModel.removeRow(mTbParams.getSelectedRow()); 360 | } 361 | } 362 | }); 363 | btnAddRow.addActionListener(e -> { 364 | mParamsTableModel.addEmptyRow(); 365 | mTbParams.requestFocus(); 366 | int index = mParamsTableModel.getRowCount() - 1; 367 | mTbParams.setRowSelectionInterval(index, index);//最后一行获得焦点 368 | mTbParams.editCellAt(index, 1); 369 | }); 370 | btnDelRow.addActionListener(e -> mParamsTableModel.removeRow(mTbParams.getSelectedRow())); 371 | btnClear.addActionListener(e -> UIActionHandler.INSTANCE.onClearParams()); 372 | } 373 | 374 | private void createUIComponents() { 375 | try { 376 | mBrowser = new Browser(); 377 | } catch (BrowserException e) { 378 | ExtFunKt.showErrorMsg("already been opened !!!"); 379 | } 380 | mBrowserView = new BrowserView(mBrowser); 381 | } 382 | 383 | @Override 384 | protected void finalize() throws Throwable { 385 | super.finalize(); 386 | OB.INSTANCE.onExit(); 387 | } 388 | 389 | public String getCurApiUrl() { 390 | if (mCbApiUrl.getEditor().getItem() != null) { 391 | return mCbApiUrl.getEditor().getItem().toString(); 392 | } 393 | return ""; 394 | } 395 | 396 | public int getCurMethod() { 397 | return mCbMethod.getSelectedIndex(); 398 | } 399 | 400 | public int getCurEncryptCode() { 401 | return mCbEncrypt.getSelectedIndex(); 402 | } 403 | 404 | public String getCurBaseUrl() { 405 | return (String) mCbBaseUrl.getSelectedItem(); 406 | } 407 | 408 | /** 409 | * Method generated by IntelliJ IDEA GUI Designer 410 | * >>> IMPORTANT!! <<< 411 | * DO NOT edit this method OR call it in your code! 412 | * 413 | * @noinspection ALL 414 | */ 415 | private void $$$setupUI$$$() { 416 | createUIComponents(); 417 | baseP = new JPanel(); 418 | baseP.setLayout(new FormLayout( 419 | "fill:d:noGrow,left:4dlu:noGrow,fill:300px:noGrow,left:4dlu:noGrow,fill:d:noGrow,left:4dlu:noGrow,fill:max(d;4px):noGrow,left:4dlu:noGrow,fill:d:noGrow," + 420 | "left:4dlu:noGrow,fill:d:noGrow,left:4dlu:noGrow,fill:600px:grow", 421 | "center:d:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:33px:noGrow,center:33px:noGrow,center:200px:noGrow,center:40px:noGrow,top:4dlu:noGrow,center:p:grow," + 422 | "center:max(d;4px):noGrow")); 423 | baseP.setName("Api debugger"); 424 | baseP.setPreferredSize(new Dimension(1341, 980)); 425 | baseP.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1), null)); 426 | final JLabel label1 = new JLabel(); 427 | label1.setText("Base Url:"); 428 | CellConstraints cc = new CellConstraints(); 429 | baseP.add(label1, cc.xy(1, 2)); 430 | mCbBaseUrl = new JComboBox(); 431 | mCbBaseUrl.setEditable(true); 432 | baseP.add(mCbBaseUrl, cc.xy(3, 2)); 433 | mBtnSaveBaseUrl = new JButton(); 434 | mBtnSaveBaseUrl.setText("Save"); 435 | baseP.add(mBtnSaveBaseUrl, cc.xy(5, 2)); 436 | final JScrollPane scrollPane1 = new JScrollPane(); 437 | baseP.add(scrollPane1, cc.xyw(1, 6, 11, CellConstraints.FILL, CellConstraints.FILL)); 438 | scrollPane1.setBorder(BorderFactory.createTitledBorder("Request Parameter")); 439 | mTbParams = new JTable(); 440 | mTbParams.setRowHeight(25); 441 | scrollPane1.setViewportView(mTbParams); 442 | lbStatus = new JLabel(); 443 | lbStatus.setText("Status:"); 444 | baseP.add(lbStatus, cc.xyw(1, 10, 11)); 445 | final JScrollPane scrollPane2 = new JScrollPane(); 446 | baseP.add(scrollPane2, cc.xywh(13, 1, 1, 7, CellConstraints.FILL, CellConstraints.FILL)); 447 | scrollPane2.setBorder(BorderFactory.createTitledBorder("Request Information")); 448 | mTpInfo = new JTextPane(); 449 | mTpInfo.setText(""); 450 | scrollPane2.setViewportView(mTpInfo); 451 | mCbEncrypt = new JComboBox(); 452 | baseP.add(mCbEncrypt, cc.xy(11, 2)); 453 | btnDelUrl = new JButton(); 454 | btnDelUrl.setText("Delete"); 455 | baseP.add(btnDelUrl, cc.xy(7, 2)); 456 | mCbMethod = new JComboBox(); 457 | baseP.add(mCbMethod, cc.xy(9, 2)); 458 | final JLabel label2 = new JLabel(); 459 | label2.setText("Api Url:"); 460 | baseP.add(label2, cc.xy(1, 4)); 461 | mCbApiUrl = new JComboBox(); 462 | mCbApiUrl.setEditable(true); 463 | baseP.add(mCbApiUrl, cc.xyw(3, 4, 5)); 464 | final JPanel panel1 = new JPanel(); 465 | panel1.setLayout(new FlowLayout(FlowLayout.RIGHT, 3, 0)); 466 | baseP.add(panel1, cc.xyw(1, 7, 11)); 467 | btnAddRow = new JButton(); 468 | btnAddRow.setText("Add Row"); 469 | panel1.add(btnAddRow); 470 | btnDelRow = new JButton(); 471 | btnDelRow.setText("Delete Row"); 472 | panel1.add(btnDelRow); 473 | btnClear = new JButton(); 474 | btnClear.setText("Clear"); 475 | panel1.add(btnClear); 476 | mBtnSend = new JButton(); 477 | mBtnSend.setBorderPainted(false); 478 | mBtnSend.setIconTextGap(1); 479 | mBtnSend.setInheritsPopupMenu(false); 480 | mBtnSend.setMargin(new Insets(5, 5, 5, 5)); 481 | mBtnSend.setMaximumSize(new Dimension(78, 78)); 482 | mBtnSend.setMinimumSize(new Dimension(78, 78)); 483 | mBtnSend.setOpaque(true); 484 | mBtnSend.setPreferredSize(new Dimension(78, 78)); 485 | mBtnSend.setRequestFocusEnabled(false); 486 | mBtnSend.setText(""); 487 | mBtnSend.setToolTipText("Send"); 488 | baseP.add(mBtnSend, cc.xywh(11, 4, 1, 2)); 489 | final JPanel panel2 = new JPanel(); 490 | panel2.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); 491 | panel2.setBackground(new Color(-15856893)); 492 | baseP.add(panel2, cc.xyw(1, 5, 3)); 493 | final JLabel label3 = new JLabel(); 494 | label3.setForeground(new Color(-1)); 495 | label3.setText("Pressure test"); 496 | panel2.add(label3, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, 497 | com.intellij.uiDesigner.core.GridConstraints.FILL_NONE, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, 498 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 499 | tvTestCount = new JTextField(); 500 | tvTestCount.setToolTipText("test count"); 501 | panel2.add(tvTestCount, new com.intellij.uiDesigner.core.GridConstraints(0, 1, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, 502 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, 503 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 504 | mBtnStartTest = new JButton(); 505 | mBtnStartTest.setText("Start"); 506 | panel2.add(mBtnStartTest, new com.intellij.uiDesigner.core.GridConstraints(0, 2, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, 507 | com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, 508 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, 509 | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 510 | baseP.add(mBrowserView, cc.xywh(1, 8, 13, 2)); 511 | btnDelApi = new JButton(); 512 | btnDelApi.setText("Delete"); 513 | baseP.add(btnDelApi, cc.xy(9, 4)); 514 | mbtnDp = new JButton(); 515 | mbtnDp.setText(" Default Parameter"); 516 | mbtnDp.setToolTipText(" Set Current Project Default Parameter"); 517 | baseP.add(mbtnDp, cc.xyw(7, 5, 3)); 518 | mPb = new JProgressBar(); 519 | mPb.setMaximumSize(new Dimension(50, 10)); 520 | mPb.setPreferredSize(new Dimension(50, 10)); 521 | baseP.add(mPb, cc.xy(13, 10, CellConstraints.FILL, CellConstraints.DEFAULT)); 522 | } 523 | 524 | /** @noinspection ALL */ 525 | public JComponent $$$getRootComponent$$$() { 526 | return baseP; 527 | } 528 | } 529 | -------------------------------------------------------------------------------- /src/main/java/com/longforus/apidebugger/ui/ParamsTableModel.java: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.ui; 2 | 3 | import com.longforus.apidebugger.bean.TableBean; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | import javax.swing.table.AbstractTableModel; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | public class ParamsTableModel extends AbstractTableModel { 11 | //单元格元素类型 12 | private Class[] cellType = { Boolean.class, String.class, String.class }; 13 | //表头 14 | private String title[] = { "select", "key", "value" }; 15 | //模拟数据 16 | //private Object data[][] = { 17 | // { "1", new ImageIcon("e://image/3.jpg"), new Boolean(true), 0, new JButton("start1") }, { 18 | // "2", new ImageIcon("e://image/1.jpg"), new Boolean(false), 60, new JButton("start2") }, { 19 | // "3", new ImageIcon("e://image/4.png"), new Boolean(false), 25, new JButton("start3") } }; 20 | 21 | private List data = new ArrayList<>(); 22 | 23 | public ParamsTableModel() { 24 | } 25 | 26 | public List getData() { 27 | return data.stream().filter(tableBean -> StringUtils.isNotEmpty(tableBean.getKey()) && StringUtils.isNotEmpty(tableBean.getValue())).collect(Collectors.toList()); 28 | } 29 | 30 | public void setData(List data) { 31 | this.data.clear(); 32 | this.data.addAll(data); 33 | fireTableDataChanged(); 34 | } 35 | 36 | public void clear() { 37 | int size = this.data.size(); 38 | this.data.clear(); 39 | fireTableRowsDeleted(0, size); 40 | } 41 | 42 | public void addEmptyRow() { 43 | data.add(new TableBean(true, "", "")); 44 | fireTableRowsInserted(data.size() - 1, data.size() - 1); 45 | } 46 | 47 | public void removeRow(int row) { 48 | if (row > -1 && row < data.size()) { 49 | data.remove(row); 50 | } 51 | fireTableRowsDeleted(row, row); 52 | } 53 | 54 | @Override 55 | public Class getColumnClass(int arg0) { 56 | return cellType[arg0]; 57 | } 58 | 59 | @Override 60 | public String getColumnName(int arg0) { 61 | return title[arg0]; 62 | } 63 | 64 | @Override 65 | public int getColumnCount() { 66 | return title.length; 67 | } 68 | 69 | @Override 70 | public int getRowCount() { 71 | return data.size(); 72 | } 73 | 74 | @Override 75 | public Object getValueAt(int rowIndex, int columnIndex) { 76 | if (rowIndex < data.size()) { 77 | switch (columnIndex) { 78 | case 0: 79 | return data.get(rowIndex).getSelected(); 80 | case 1: 81 | return data.get(rowIndex).getKey(); 82 | case 2: 83 | return data.get(rowIndex).getValue(); 84 | } 85 | } 86 | return null; 87 | } 88 | 89 | //重写isCellEditable方法 90 | 91 | @Override 92 | public boolean isCellEditable(int rowIndex, int columnIndex) { 93 | return true; 94 | } 95 | 96 | //重写setValueAt方法 97 | 98 | @Override 99 | public void setValueAt(Object aValue, int rowIndex, int columnIndex) { 100 | if (rowIndex < data.size()) { 101 | switch (columnIndex) { 102 | case 0: 103 | data.get(rowIndex).setSelected((Boolean) aValue); 104 | break; 105 | case 1: 106 | data.get(rowIndex).setKey((String) aValue); 107 | break; 108 | case 2: 109 | data.get(rowIndex).setValue((String) aValue); 110 | break; 111 | } 112 | } 113 | this.fireTableCellUpdated(rowIndex, columnIndex); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/ExtFun.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import java.awt.Color 4 | import javax.swing.JOptionPane 5 | import javax.swing.JTextPane 6 | import javax.swing.text.BadLocationException 7 | import javax.swing.text.MutableAttributeSet 8 | import javax.swing.text.SimpleAttributeSet 9 | import javax.swing.text.StyleConstants 10 | 11 | /** 12 | * Created by XQ Yang on 8/30/2018 3:05 PM. 13 | * Description : 14 | */ 15 | fun JTextPane.append(str:String,color: Color? = null,autoScroll:Boolean = true){ 16 | val doc = this.document 17 | if (doc != null) { 18 | try { 19 | var attr: MutableAttributeSet? = null 20 | if (color != null) { 21 | attr = SimpleAttributeSet() 22 | StyleConstants.setForeground(attr, color) 23 | StyleConstants.setBold(attr, true) 24 | } 25 | doc.insertString(doc.length, str, attr) 26 | if (autoScroll) { 27 | this.caretPosition = this.styledDocument.length 28 | } 29 | } catch (e: BadLocationException) { 30 | } 31 | 32 | } 33 | } 34 | 35 | fun showErrorMsg(msg:String){ 36 | JOptionPane.showMessageDialog(null, msg,"Error", JOptionPane.ERROR_MESSAGE) 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/HttpManage.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.google.gson.JsonObject 4 | import com.longforus.apidebugger.MyValueHandler.mGson 5 | import com.longforus.apidebugger.ui.MainPanel 6 | import okhttp3.* 7 | import java.awt.Color 8 | import java.io.IOException 9 | import java.util.concurrent.TimeUnit 10 | 11 | 12 | /** 13 | * Created by XQ Yang on 8/30/2018 2:29 PM. 14 | * Description : 15 | */ 16 | object HttpManage { 17 | 18 | lateinit var mainPanel: MainPanel 19 | val okHttpClient = OkHttpClient.Builder().writeTimeout(30, TimeUnit.SECONDS) 20 | .readTimeout(30, TimeUnit.SECONDS) 21 | .connectTimeout(30, TimeUnit.SECONDS).build() 22 | 23 | 24 | fun sendTestRequest(request: Request? = getRequest(), isLast: Boolean = false, allCount: Int = 0,startTime:Long = 0) { 25 | request?.let { 26 | if (!isLast) { 27 | okHttpClient.newCall(request).enqueue(object : Callback { 28 | override fun onFailure(call: Call, e: IOException) { 29 | } 30 | 31 | override fun onResponse(call: Call, response: Response) { 32 | synchronized(this@HttpManage) { 33 | com.longforus.apidebugger.mainPanel.pb.value += 1 34 | } 35 | } 36 | }) 37 | } else { 38 | doRequest(request,startTime,allCount) 39 | } 40 | } 41 | } 42 | 43 | fun sendRequest(request: Request? = getRequest()) { 44 | request?.let { 45 | doRequest(request) 46 | } 47 | } 48 | 49 | 50 | fun getRequest(): Request? { 51 | val url = getAbsoluteUrl(mainPanel.curApiUrl) 52 | if (url.isEmpty()) { 53 | return null 54 | } 55 | return buildRequest(url, UIActionHandler.getParamsMap(mainPanel.paramsTableModel, false), mainPanel.curMethod, mainPanel.curEncryptCode) 56 | } 57 | 58 | 59 | private fun doRequest(request: Request, startTime: Long = System.currentTimeMillis(), allCount: Int = 0) { 60 | okHttpClient.newCall(request).enqueue(object : Callback { 61 | override fun onFailure(call: Call, e: IOException) { 62 | mainPanel.lbStatus.text = "onFailure message : ${e.message}\n" 63 | mainPanel.tpInfo.append( "\nonFailure \n message : ${e.message}\n", Color.RED) 64 | mainPanel.tpInfo.append(" consuming: ${System.currentTimeMillis() - startTime}ms \n", Color.RED) 65 | } 66 | 67 | @Throws(IOException::class) 68 | override fun onResponse(call: Call, response: Response) { 69 | UIActionHandler.onSaveApi(mainPanel.cbApiUrl.editor.item) 70 | mainPanel.lbStatus.text = "onResponse code: ${response.code()} " 71 | mainPanel.tpInfo.append("\nconsuming: ${System.currentTimeMillis() - startTime}ms \n", Color.BLUE) 72 | if (response.isSuccessful) { 73 | val body = response.body() ?: return 74 | val bytes = body.bytes() 75 | val resStr = String(bytes) 76 | mainPanel.tpInfo.append("response size: ${bytes.size} byte \n", Color.BLUE) 77 | val json = mGson.fromJson(resStr, JsonObject::class.java) 78 | val jsonStr = mGson.toJson(json, JsonObject::class.java) 79 | MyValueHandler.curShowJsonStr = jsonStr 80 | mainPanel.setJsonData(jsonStr) 81 | } else { 82 | mainPanel.tpInfo.append("\non response but not success\n", Color.RED) 83 | mainPanel.tpInfo.append("code = ${response.code()} \n ", Color.RED) 84 | mainPanel.tpInfo.append("message = ${response.message()} \n", Color.RED) 85 | } 86 | if (allCount!=0) { 87 | mainPanel.tpInfo.append("\nAll test count: $allCount \n", Color.GREEN) 88 | mainPanel.tpInfo.append("\nSuccess test count: ${mainPanel.pb.value} \n", Color.GREEN) 89 | mainPanel.tpInfo.append("\nThe total time consuming: ${System.currentTimeMillis() - startTime}ms \n", Color.GREEN) 90 | } 91 | } 92 | }) 93 | } 94 | 95 | /** 96 | * 根据网络请求方式构建联网请求时所需的request 97 | * 98 | * @param httpMethodType 联网请求方式 99 | * @return 联网所需的request 100 | */ 101 | 102 | @Throws(Exception::class) 103 | private fun buildRequest(url: String, params: Map?, httpMethodType: Int, encryptType: Int): Request { 104 | val builder = Request.Builder() 105 | val iEncryptHandler = MyValueHandler.encryptImplList[encryptType] 106 | if (httpMethodType == 1) { 107 | mainPanel.tpInfo.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~这是一条分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", Color.ORANGE) 108 | mainPanel.tpInfo.append("OkHttp GET \n", Color.RED) 109 | iEncryptHandler.onGetMethodEncrypt(params, builder, url) 110 | builder.get() 111 | } else if (httpMethodType == 0) { 112 | mainPanel.tpInfo.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~这是一条分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", Color.ORANGE) 113 | mainPanel.tpInfo.append("OkHttp POST \n", Color.RED) 114 | builder.url(url) 115 | builder.post(iEncryptHandler.onPostMethodEncrypt(params, builder, url)) 116 | } 117 | return builder.build() 118 | } 119 | 120 | 121 | /** 122 | * 根据相对路径获取全路径 123 | * 124 | * @param relativeUrl 相对路径 125 | */ 126 | private fun getAbsoluteUrl(relativeUrl: String): String { 127 | if (mainPanel.curBaseUrl.isNullOrEmpty()) { 128 | showErrorMsg("BaseURL is Null") 129 | return "" 130 | } 131 | return mainPanel.curBaseUrl + relativeUrl 132 | } 133 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/JsonTreeActionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.longforus.apidebugger.ui.JsonJTreeNode 4 | import javax.swing.event.TreeSelectionEvent 5 | import javax.swing.event.TreeSelectionListener 6 | 7 | /** 8 | * Created by XQ Yang on 8/30/2018 4:00 PM. 9 | * Description : 10 | */ 11 | 12 | object JsonTreeActionHandler: TreeSelectionListener { 13 | override fun valueChanged(e: TreeSelectionEvent?) { 14 | println(e.toString()) 15 | e?.newLeadSelectionPath?.lastPathComponent?.let { 16 | val treeNode = it as JsonJTreeNode 17 | val start = MyValueHandler.curShowJsonStr.indexOf(treeNode.toSSearchStr()) 18 | // mainPanel.tpResponse.select(start,start+treeNode.toSSearchStr().length) 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/Main.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.longforus.apidebugger.ui.MainPanel 4 | import com.teamdev.jxbrowser.chromium.az 5 | import java.awt.EventQueue 6 | import java.lang.reflect.Field 7 | import java.lang.reflect.Modifier 8 | import java.math.BigInteger 9 | 10 | 11 | 12 | /** 13 | * Created by XQ Yang on 8/30/2018 10:07 AM. 14 | * Description : 15 | */ 16 | 17 | lateinit var mainPanel: MainPanel 18 | val appName = "Api debugger" 19 | 20 | 21 | fun main(args: Array) { 22 | jxInit() 23 | OB.init() 24 | EventQueue.invokeLater { 25 | mainPanel = MainPanel(appName) 26 | UILifecycleHandler.onResume(mainPanel) 27 | HttpManage.mainPanel = mainPanel 28 | } 29 | } 30 | 31 | fun jxInit() { 32 | try { 33 | val e = az::class.java.getDeclaredField("e") 34 | e.isAccessible = true 35 | val f = az::class.java.getDeclaredField("f") 36 | f.isAccessible = true 37 | val modifersField = Field::class.java.getDeclaredField("modifiers") 38 | modifersField.isAccessible = true 39 | modifersField.setInt(e, e.modifiers and Modifier.FINAL.inv()) 40 | modifersField.setInt(f, f.modifiers and Modifier.FINAL.inv()) 41 | e.set(null, BigInteger("1")) 42 | f.set(null, BigInteger("1")) 43 | modifersField.isAccessible = false 44 | } catch (e1: Exception) { 45 | e1.printStackTrace() 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/MyValueHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.google.gson.GsonBuilder 4 | import com.longforus.apidebugger.bean.ApiBean 5 | import com.longforus.apidebugger.bean.ProjectBean 6 | import com.longforus.apidebugger.encrypt.DefaultEncryptHandler 7 | import com.longforus.apidebugger.encrypt.IEncryptHandler 8 | import java.awt.Toolkit 9 | import java.awt.datatransfer.StringSelection 10 | 11 | 12 | /** 13 | * Created by XQ Yang on 8/30/2018 5:24 PM. 14 | * Description : 15 | */ 16 | 17 | object MyValueHandler { 18 | 19 | 20 | val encryptImplList = listOf( DefaultEncryptHandler()) 21 | // val encryptImplList = listOf( DefaultEncryptHandler()) 22 | val mGson =GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create() 23 | 24 | var curProject: ProjectBean? = null 25 | set(value) { 26 | if (value == field) { 27 | return 28 | } 29 | field = value 30 | value?.let { 31 | UILifecycleHandler.initProject(it, mainPanel) 32 | } 33 | } 34 | 35 | 36 | var curApi: ApiBean? = null 37 | set(value) { 38 | if (value == field) { 39 | return 40 | } 41 | field = value 42 | UILifecycleHandler.initApi(value) 43 | } 44 | 45 | var curBaseUrl = "" 46 | var curShowJsonStr = "" 47 | 48 | fun encryptId2Index(id: Int): Int { 49 | encryptImplList.forEachIndexed { index, iEncryptHandler -> 50 | if (iEncryptHandler.typeCode == id) { 51 | return index 52 | } 53 | } 54 | return 0 55 | } 56 | fun encryptIndex2Id(index: Int)=encryptImplList[index].typeCode 57 | 58 | 59 | 60 | 61 | /** 62 | * 将字符串复制到剪切板。 63 | */ 64 | fun setSysClipboardText(writeMe: String) { 65 | val clip = Toolkit.getDefaultToolkit().systemClipboard 66 | val tText = StringSelection(writeMe) 67 | clip.setContents(tText, null) 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/OB.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.longforus.apidebugger.bean.ApiBean 4 | import com.longforus.apidebugger.bean.MyObjectBox 5 | import com.longforus.apidebugger.bean.ProjectBean 6 | import com.longforus.apidebugger.bean.TableBean 7 | import io.objectbox.Box 8 | import io.objectbox.BoxStore 9 | import io.objectbox.kotlin.boxFor 10 | 11 | /** 12 | * Created by XQ Yang on 8/31/2018 9:44 AM. 13 | * Description : 14 | */ 15 | 16 | 17 | object OB{ 18 | 19 | lateinit var store:BoxStore 20 | lateinit var projectBox: Box 21 | lateinit var apiBox: Box 22 | lateinit var paramsBox: Box 23 | fun init(){ 24 | store = MyObjectBox.builder().name("api-debugger-db").build() 25 | projectBox= store.boxFor() 26 | apiBox= store.boxFor() 27 | paramsBox= store.boxFor() 28 | } 29 | 30 | fun onExit(){ 31 | store.close() 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/UIActionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.longforus.apidebugger.bean.ApiBean 4 | import com.longforus.apidebugger.ui.ParamsTableModel 5 | import javax.swing.DefaultComboBoxModel 6 | 7 | /** 8 | * Created by XQ Yang on 8/31/2018 11:21 AM. 9 | * Description : 10 | */ 11 | object UIActionHandler { 12 | 13 | fun onSaveBaseUrl(selectedItem: Any) { 14 | MyValueHandler.curProject?.let { 15 | if (!it.baseUrlList.contains(selectedItem as String)) { 16 | it.baseUrlList.add(0, selectedItem) 17 | MyValueHandler.curBaseUrl = selectedItem 18 | OB.projectBox.put(it) 19 | val model = mainPanel.cbBaseUrl.model as DefaultComboBoxModel 20 | model.insertElementAt(selectedItem, 0) 21 | } 22 | } 23 | } 24 | 25 | fun onSaveApi(selectedItem: Any) { 26 | MyValueHandler.curProject?.let { 27 | val apiBean: ApiBean 28 | if (selectedItem is String) { 29 | apiBean = ApiBean(selectedItem, it.id) 30 | if (!it.apis.contains(apiBean)) { 31 | apiBean.encryptType = mainPanel.selectedEncryptID 32 | apiBean.method = mainPanel.selectedMethodType 33 | apiBean.paramsMap = getParamsMap(mainPanel.paramsTableModel) 34 | it.apis.add(0, apiBean) 35 | val model = mainPanel.cbApiUrl.model as DefaultComboBoxModel 36 | model.insertElementAt(apiBean, 0) 37 | mainPanel.cbApiUrl.selectedIndex = 0 38 | MyValueHandler.curApi = apiBean 39 | } 40 | } else { 41 | apiBean = selectedItem as ApiBean 42 | apiBean.encryptType = mainPanel.selectedEncryptID 43 | apiBean.method = mainPanel.selectedMethodType 44 | apiBean.paramsMap = getParamsMap(mainPanel.paramsTableModel) 45 | } 46 | OB.apiBox.put(apiBean) 47 | } 48 | } 49 | 50 | fun getParamsMap(model: ParamsTableModel, isSave: Boolean = true): MutableMap { 51 | val map = HashMap() 52 | for (bean in model.data) { 53 | if (bean.selected || isSave) { 54 | map[bean.key] = bean.value 55 | } 56 | } 57 | return map 58 | } 59 | 60 | fun onNewApi() { 61 | val clone = MyValueHandler.curApi?.clone() as ApiBean? 62 | clone?.let { 63 | OB.apiBox.put(it) 64 | MyValueHandler.curApi = it 65 | mainPanel.cbApiUrl.insertItemAt(it, 0) 66 | mainPanel.cbApiUrl.selectedIndex = 0 67 | } 68 | } 69 | 70 | fun onSend() { 71 | if (MyValueHandler.curProject == null) { 72 | showErrorMsg("Please create the project first") 73 | } else { 74 | HttpManage.sendRequest() 75 | } 76 | } 77 | 78 | fun onDelBaseUrl(selectedItem: Any) { 79 | MyValueHandler.curProject?.baseUrlList?.remove(selectedItem) 80 | mainPanel.cbBaseUrl.removeItem(selectedItem) 81 | OB.projectBox.put(MyValueHandler.curProject) 82 | } 83 | 84 | fun onDelApiUrl(selectedItem: ApiBean) { 85 | MyValueHandler.curProject?.apis?.remove(selectedItem) 86 | mainPanel.cbApiUrl.removeItem(selectedItem) 87 | OB.apiBox.remove(selectedItem) 88 | MyValueHandler.curApi = MyValueHandler.curProject?.apis?.get(0) 89 | } 90 | 91 | fun onApiItemChanged(item: ApiBean) { 92 | MyValueHandler.curApi = item 93 | } 94 | 95 | fun onMethodChanged(index: Int) { 96 | if (MyValueHandler.curApi?.method != index) { 97 | MyValueHandler.curApi?.method = index 98 | if (MyValueHandler.curApi != null) { 99 | OB.apiBox.put(MyValueHandler.curApi) 100 | } 101 | } 102 | } 103 | 104 | fun onEncryptTypeChanged(typeCode: Int) { 105 | if (MyValueHandler.curApi?.encryptType != typeCode) { 106 | MyValueHandler.curApi?.encryptType = typeCode 107 | if (MyValueHandler.curApi != null) { 108 | OB.apiBox.put(MyValueHandler.curApi) 109 | } 110 | } 111 | } 112 | 113 | fun onClearParams() { 114 | MyValueHandler.curApi?.paramsMap?.clear() 115 | mainPanel.paramsTableModel.clear() 116 | if (MyValueHandler.curApi != null) { 117 | OB.apiBox.put(MyValueHandler.curApi) 118 | } 119 | } 120 | 121 | fun onStartTest() { 122 | val str = mainPanel.tvTestCount.text 123 | if (str.isNullOrEmpty()) { 124 | showErrorMsg("Test times cannot be empty") 125 | return 126 | } 127 | val count = str.toInt() 128 | if (count > 0) { 129 | mainPanel.pb.minimum = 0 130 | mainPanel.pb.value = 0 131 | mainPanel.pb.maximum = count 132 | mainPanel.lbStatus.text = "Stress testing..." 133 | val startTime = System.currentTimeMillis() 134 | val request = HttpManage.getRequest() 135 | request?.let { 136 | for (i in 0..count) { 137 | if (i == count) { 138 | HttpManage.sendTestRequest(it,true,count,startTime) 139 | } else { 140 | HttpManage.sendTestRequest(it,false) 141 | } 142 | } 143 | } 144 | 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/UILifecycleHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger 2 | 3 | import com.longforus.apidebugger.MyValueHandler.curApi 4 | import com.longforus.apidebugger.MyValueHandler.curProject 5 | import com.longforus.apidebugger.bean.ApiBean 6 | import com.longforus.apidebugger.bean.ProjectBean 7 | import com.longforus.apidebugger.bean.ProjectBean_ 8 | import com.longforus.apidebugger.ui.MainPanel 9 | import io.objectbox.kotlin.query 10 | import javax.swing.* 11 | 12 | /** 13 | * Created by XQ Yang on 8/31/2018 11:23 AM. 14 | * Description : 15 | */ 16 | object UILifecycleHandler { 17 | 18 | val cacheMenu = mutableMapOf() 19 | 20 | fun onResume(mainPanel: MainPanel) { 21 | mainPanel.cbEncrypt.model = DefaultComboBoxModel(MyValueHandler.encryptImplList.toTypedArray()) 22 | val allProject = OB.projectBox.query().build().find() 23 | if (allProject.isNotEmpty()) { 24 | curProject = allProject.last() 25 | } 26 | } 27 | 28 | fun initProject(it: ProjectBean, mainPanel: MainPanel) { 29 | mainPanel.title= " $appName Current Project : ${it.name}" 30 | mainPanel.cbBaseUrl.model = DefaultComboBoxModel(it.baseUrlList.toTypedArray()) 31 | if (it.baseUrlList.isNotEmpty()) { 32 | MyValueHandler.curBaseUrl = it.baseUrlList.last() 33 | } 34 | val apis = it.apis 35 | mainPanel.cbApiUrl.model = DefaultComboBoxModel(apis.toTypedArray()) 36 | curApi = if (apis.isNotEmpty()) { 37 | apis[0] 38 | } else { 39 | null 40 | } 41 | } 42 | 43 | fun initApi(api: ApiBean?){ 44 | if (api == null) { 45 | mainPanel.cbEncrypt.selectedIndex = 0 46 | mainPanel.cbMethod.selectedIndex = 0 47 | mainPanel.resetParamsTbModel() 48 | } else { 49 | val id2Index = MyValueHandler.encryptId2Index(api.encryptType) 50 | mainPanel.cbEncrypt.selectedIndex = id2Index 51 | mainPanel.cbMethod.selectedIndex = api.method 52 | if (api.paramsMap.isEmpty()) { 53 | mainPanel.resetParamsTbModel() 54 | } else { 55 | // api.paramsMap.entries.forEachIndexed { index, entry -> 56 | // mainPanel.tbParame.model.setValueAt(entry.key, index, 1) 57 | // mainPanel.tbParame.model.setValueAt(entry.value, index, 2) 58 | // } 59 | mainPanel.paramsTableModel.data = ApiBean.getTableValueList(api) 60 | } 61 | } 62 | } 63 | 64 | fun getMenuBar(): JMenuBar { 65 | val menuBar = JMenuBar() 66 | val pm = JMenu("Project") 67 | val item = JMenuItem("new") 68 | item.addActionListener { 69 | val projectName = JOptionPane.showInputDialog("Input Project Name") 70 | if (projectName.isNullOrEmpty()) { 71 | return@addActionListener 72 | } 73 | val count = OB.projectBox.query { 74 | equal(ProjectBean_.name, projectName) 75 | }.count() 76 | if (count > 0) { 77 | showErrorMsg("Project existing") 78 | } else { 79 | val project = ProjectBean() 80 | project.name = projectName 81 | val newPro = JMenuItem(projectName) 82 | cacheMenu[projectName] = newPro 83 | newPro.addActionListener {_-> 84 | curProject = project 85 | } 86 | pm.insert(newPro,2) 87 | OB.projectBox.put(project) 88 | curProject = project 89 | } 90 | } 91 | pm.add(item) 92 | pm.addSeparator() 93 | OB.projectBox.query().build().find().sortedByDescending { it.id }.forEach {pro-> 94 | val tempItem = JMenuItem(pro.name) 95 | cacheMenu[pro.name] = tempItem 96 | tempItem.addActionListener { 97 | curProject = pro 98 | } 99 | pm.add(tempItem) 100 | } 101 | pm.addSeparator() 102 | val deleteItem = JMenuItem("delete current open project") 103 | deleteItem.addActionListener { 104 | if (curProject != null) { 105 | OB.projectBox.remove(curProject) 106 | val jMenuItem = cacheMenu.remove(curProject?.name) 107 | pm.remove(jMenuItem) 108 | val mutableList = OB.projectBox.query().build().find() 109 | if (mutableList.isNotEmpty()) { 110 | val last = mutableList.last() 111 | if (last != null) { 112 | curProject = last 113 | } 114 | } 115 | } 116 | } 117 | pm.add(deleteItem) 118 | 119 | val am = JMenu("About") 120 | val item1 = JMenuItem("about") 121 | am.add(item1) 122 | item1.addActionListener { 123 | JOptionPane.showMessageDialog(null,"version 1.0 \nAuthor longforus \nQQ 89082243 ","About",JOptionPane.INFORMATION_MESSAGE) 124 | } 125 | menuBar.add(pm) 126 | menuBar.add(am) 127 | return menuBar 128 | } 129 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/bean/ApiBean.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.bean 2 | 3 | import io.objectbox.annotation.Convert 4 | import io.objectbox.annotation.Entity 5 | import io.objectbox.annotation.Id 6 | import io.objectbox.annotation.Index 7 | 8 | /** 9 | * Created by XQ Yang on 8/30/2018 5:41 PM. 10 | * Description : 11 | */ 12 | @Entity 13 | class ApiBean(@Id(assignable = true) 14 | var id: Long = 0, 15 | var method: Int = 0, 16 | var url: String = "", 17 | @Convert(converter = MapDbConverter::class, 18 | dbType = String::class) 19 | var paramsMap: MutableMap = HashMap(), 20 | var encryptType: Int = 0, @Index var projectId: Long = 0, val createDate: Long = System.currentTimeMillis()) : kotlin.Cloneable { 21 | 22 | constructor(url: String, projectId: Long) : this() { 23 | this.url = url 24 | this.projectId = projectId 25 | this.id = hashCode().toLong() 26 | } 27 | 28 | 29 | companion object { 30 | fun getTableValueList(bean: ApiBean): MutableList { 31 | if (bean.paramsMap.isEmpty()) { 32 | return mutableListOf() 33 | } 34 | val list = mutableListOf() 35 | bean.paramsMap.forEach { 36 | list.add(TableBean(selected= true,key = it.key,value = it.value)) 37 | } 38 | return list 39 | } 40 | } 41 | 42 | 43 | override fun toString(): String { 44 | return url 45 | } 46 | 47 | 48 | public override fun clone(): Any { 49 | val map = HashMap() 50 | paramsMap.forEach { t, u -> 51 | map[t] = u 52 | } 53 | val bean = ApiBean(method = this.method, url = this.url + "新", paramsMap = map, encryptType = this.encryptType, projectId = this.projectId) 54 | bean.id = bean.hashCode().toLong() 55 | return bean 56 | } 57 | 58 | override fun equals(other: Any?): Boolean { 59 | if (this === other) return true 60 | if (javaClass != other?.javaClass) return false 61 | 62 | other as ApiBean 63 | 64 | if (url != other.url) return false 65 | if (projectId != other.projectId) return false 66 | 67 | return true 68 | } 69 | 70 | override fun hashCode(): Int { 71 | var result = url.hashCode() 72 | result = 31 * result + projectId.hashCode() 73 | return result 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/bean/ListDbConverter.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.bean 2 | 3 | import com.google.gson.reflect.TypeToken 4 | import com.longforus.apidebugger.MyValueHandler 5 | import io.objectbox.converter.PropertyConverter 6 | 7 | /** 8 | * Created by XQ Yang on 8/30/2018 5:48 PM. 9 | * Description : 10 | */ 11 | class ListDbConverter : PropertyConverter, String> { 12 | 13 | override fun convertToEntityProperty(databaseValue: String?): List { 14 | if (databaseValue.isNullOrEmpty()) { 15 | return mutableListOf() 16 | } 17 | val type = object : TypeToken>() {}.type 18 | return MyValueHandler.mGson.fromJson(databaseValue, type) 19 | } 20 | 21 | override fun convertToDatabaseValue(entityProperty: List?): String { 22 | if (entityProperty == null) { 23 | return "" 24 | } 25 | return MyValueHandler.mGson.toJson(entityProperty) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/bean/MapDbConverter.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.bean 2 | 3 | import com.google.gson.reflect.TypeToken 4 | import com.longforus.apidebugger.MyValueHandler 5 | import io.objectbox.converter.PropertyConverter 6 | 7 | /** 8 | * Created by XQ Yang on 8/30/2018 5:48 PM. 9 | * Description : 10 | */ 11 | class MapDbConverter : PropertyConverter, String> { 12 | 13 | override fun convertToEntityProperty(databaseValue: String?): Map { 14 | val type = object : TypeToken>() {}.type 15 | return MyValueHandler.mGson.fromJson(databaseValue, type) 16 | } 17 | 18 | override fun convertToDatabaseValue(entityProperty: Map?): String { 19 | return MyValueHandler.mGson.toJson(entityProperty) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/bean/ProjectBean.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.bean 2 | 3 | import com.longforus.apidebugger.OB 4 | import io.objectbox.annotation.Convert 5 | import io.objectbox.annotation.Entity 6 | import io.objectbox.annotation.Id 7 | import io.objectbox.annotation.Index 8 | import io.objectbox.kotlin.query 9 | 10 | /** 11 | * Created by XQ Yang on 8/30/2018 5:40 PM. 12 | * Description : 13 | */ 14 | @Entity 15 | data class ProjectBean( 16 | @Id 17 | var id: Long = 0, 18 | @Index 19 | var name: String = "", 20 | @Convert(converter = ListDbConverter::class, 21 | dbType = String::class) 22 | var baseUrlList: MutableList = mutableListOf() 23 | ) { 24 | @Transient 25 | var apis: MutableList = mutableListOf() 26 | get() { 27 | if (field.isNotEmpty()) { 28 | return field 29 | } 30 | field.clear() 31 | field.addAll(OB.apiBox.query { 32 | equal(ApiBean_.projectId, id) 33 | }.find().sortedByDescending { apiBean -> apiBean.createDate }) 34 | return field 35 | } 36 | 37 | @Transient 38 | var defaultParams: MutableList = mutableListOf() 39 | //这里每次都会读取不够优雅 40 | get() { 41 | if (field.isNotEmpty()) { 42 | return field 43 | } 44 | field.clear() 45 | field.addAll(OB.paramsBox.query { 46 | equal(TableBean_.projectId, id) 47 | }.find()) 48 | return field 49 | } 50 | 51 | 52 | 53 | override fun toString(): String { 54 | return name 55 | } 56 | 57 | override fun equals(other: Any?): Boolean { 58 | if (this === other) return true 59 | if (javaClass != other?.javaClass) return false 60 | 61 | other as ProjectBean 62 | 63 | if (name != other.name) return false 64 | 65 | return true 66 | } 67 | 68 | override fun hashCode(): Int { 69 | return name.hashCode() 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/bean/TableBean.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.bean 2 | 3 | import com.longforus.apidebugger.MyValueHandler 4 | import io.objectbox.annotation.Entity 5 | import io.objectbox.annotation.Id 6 | import io.objectbox.annotation.Index 7 | 8 | /** 9 | * Created by XQ Yang on 9/4/2018 2:41 PM. 10 | * Description : 11 | */ 12 | @Entity 13 | data class TableBean(@Id(assignable = true) var id: Long = 0, var selected: Boolean, var key: String, var value: String, @Index var projectId: Long = 0) { 14 | 15 | constructor(s: Boolean, k: String, v: String) : this(selected = s, key = k, value = v) 16 | 17 | init { 18 | projectId = MyValueHandler.curProject?.id ?: 0 19 | } 20 | 21 | override fun equals(other: Any?): Boolean { 22 | if (this === other) return true 23 | if (javaClass != other?.javaClass) return false 24 | 25 | other as TableBean 26 | 27 | if (key != other.key) return false 28 | if (value != other.value) return false 29 | if (projectId != other.projectId) return false 30 | 31 | return true 32 | } 33 | 34 | override fun hashCode(): Int { 35 | var result = key.hashCode() 36 | result = 31 * result + value.hashCode() 37 | result = 31 * result + projectId.hashCode() 38 | return result 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/encrypt/DefaultEncryptHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.encrypt 2 | 3 | import com.longforus.apidebugger.MyValueHandler 4 | import com.longforus.apidebugger.append 5 | import com.longforus.apidebugger.mainPanel 6 | import okhttp3.FormBody 7 | import okhttp3.Request 8 | import okhttp3.RequestBody 9 | 10 | /** 11 | * Created by XQ Yang on 8/30/2018 5:13 PM. 12 | * Description : 默认加密 13 | */ 14 | class DefaultEncryptHandler : IEncryptHandler() { 15 | override val typeCode: Int = 0 16 | override val title: String = "default" 17 | 18 | 19 | override fun onPostMethodEncrypt(params: Map?, builder: Request.Builder, url: String): RequestBody { 20 | val sb = StringBuilder("?") 21 | val encodingBuilder = FormBody.Builder() 22 | if (params?.isNotEmpty() == true) { 23 | mainPanel.tpInfo.append("params :\n") 24 | params.forEach { 25 | encodingBuilder.add(it.key, it.value) 26 | sb.append(it.key).append("=").append(it.value).append("&") 27 | mainPanel.tpInfo.append(" ${it.key} = ${it.value} \n") 28 | } 29 | } 30 | if (MyValueHandler.curProject?.defaultParams?.isNotEmpty() ==true) { 31 | mainPanel.tpInfo.append("default params :\n") 32 | MyValueHandler.curProject?.defaultParams?.forEach { 33 | if (it.selected) { 34 | encodingBuilder.add(it.key, it.value) 35 | sb.append(it.key).append("=").append(it.value).append("&") 36 | mainPanel.tpInfo.append(" ${it.key} = ${it.value} \n") 37 | } 38 | } 39 | } 40 | val resultUrl = url + if (sb.endsWith("?") || sb.endsWith("&")) sb.subSequence(0, sb.length - 1) else sb.toString() 41 | mainPanel.tpInfo.append("Url :\n$resultUrl \n") 42 | return encodingBuilder.build() 43 | } 44 | 45 | override fun onGetMethodEncrypt(params: Map?, builder: Request.Builder, url: String) { 46 | val sb = StringBuilder("?") 47 | params?.let { 48 | for (entry in params) { 49 | sb.append(entry.key).append("=").append(entry.value).append("&") 50 | } 51 | } 52 | if (MyValueHandler.curProject?.defaultParams?.isNotEmpty() ==true) { 53 | MyValueHandler.curProject?.defaultParams?.forEach { 54 | if (it.selected) { 55 | sb.append(it.key).append("=").append(it.value).append("&") 56 | } 57 | } 58 | } 59 | val resultUrl = url + if (sb.endsWith("?") || sb.endsWith("&")) sb.subSequence(0, sb.length - 1) else sb.toString() 60 | builder.url(resultUrl) 61 | mainPanel.tpInfo.append("Url :\n $resultUrl \n") 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/longforus/apidebugger/encrypt/IEncryptHandler.kt: -------------------------------------------------------------------------------- 1 | package com.longforus.apidebugger.encrypt 2 | 3 | import okhttp3.Request 4 | import okhttp3.RequestBody 5 | 6 | /** 7 | * Created by XQ Yang on 8/30/2018 5:11 PM. 8 | * Description : 加密处理 9 | */ 10 | abstract class IEncryptHandler { 11 | //这个加密类型的code,同一工程不允许出现相同的 12 | abstract val typeCode:Int 13 | //显示在界面上的名字 14 | abstract val title: String 15 | //实现get方法的参数加密 16 | abstract fun onGetMethodEncrypt(params: Map?, builder: Request.Builder, url: String) 17 | //实现post方法的参数加密 18 | abstract fun onPostMethodEncrypt(params: Map?, builder: Request.Builder, url: String): RequestBody 19 | override fun toString(): String { 20 | return title 21 | } 22 | } --------------------------------------------------------------------------------