目標
輸入
- url地址, 可能包含一級域名和二級域名
- get/post參數, 鍵值對形式, 可以動態增減
- 接口返回的json結構
輸出
- 繼承特定類的請求類的java文件
- 根據url實現相應的方法
- 根據get/post參數創建內部類, 并且實現
toString
方法, 并且創建對應的構建函數 - 根據json結構創建實體類, 并實現
toString
方法
實現
涉及的行為
- 從界面獲取相關值
- 使用列表表示參數, 所以需要動態增加/刪除控件
- 獲取目錄路徑, 并創建java文件
- 往java文件寫入類定義, 變量定義和方法定義
- 解析json
實現界面
一個界面一般包含兩個文件, form file
和UI class
.
form file
類似Android里面的xml
布局文件, 用來描述界面的布局. 使用GUI Designer(類似Android Studio的布局預覽器)通過拖動控件來創建靜態布局.
控件分為component
和container
,關系和Android中的View
和ViewGroup
差不多, 其中container
會要求指定部分僅對container
生效的屬性.
一般
container
使用默認的GridLayoutManager (IntelliJ)
就可以滿足要求了.
大小
-
Horizontal/Vertical Size Policy
: 決定了當該component
所屬的container
大小發生變化時component
的尺寸變化規則. 該屬性只有GridLayoutManager
具有, 其中可以賦值下面三個值
a.canShrink
: 實際值可以比指定值(preferred size
)小.
b.canGrow
: 實際值可以比指定值大.
c.wantGrow
: 實際值會比指定值大. 優先級高于canGrow
, 類似match_parent
, 會填充container
-
Preferred Size
: 二維數組指定寬高, 對于GridLayoutManager
可以單獨指定寬或者高,-1
表示該值由動態計算得出.
位置
不同的LayoutManager
有不同的位置屬性.
對于GridLayoutManager
來說, 整個布局就是一個網格, 通過網格控制component
的位置, 跟Excel有點類似.
對于對齊, 是通過
HSpacer/VSpacer
來占據空間來對齊的.
使用GUI Designer進行布局的時候, 如果是GridLayoutManager
, 插入時會有提示插入的行號和列號, 注意網格的嵌套關系就能輕松把控件放在目標位置了, 另外通過拉伸控件可以合并網格.
UI class
布局實際使用的是Swing, 因此在UI class
中涉及控件的操作都可以查閱
Swing的官方教程.
關于動態增刪控件, 在Swing中, 在
JLIst
中的按鈕是不會響應點擊功能的, 不熟悉Swing浪費了很多時間...
動態增加控件
因此不能用JLIst
, JTable
過于復雜, 因此直接往JPanel
中添加控件然后重繪來實現動態增加控件的效果.
具體的代碼參考官方教程, 只記錄遇到的幾個坑:
注意: 下面的說法沒有深究, 有可能是錯誤的.
布局
使用GridLayoutManager
不能動態增加網格, 在這里要實現類似列表的效果, 對吼使用的是BoxLayout
.
對齊
使用BoxLayout
后, 增加控件會自動居中, 需要通過Box.createVerticalGlue()
來創建占位控件來把增加的控件頂至頂部實現頂部對齊的效果.
重繪
增加或者刪除控件之后, 需要調用revalidate()
和repaint()
方法來重繪控件才能正常顯示.
獲取輸入值
通過控件獲取輸入值非常簡單, 類似Android, 控件具有各種getXXXX
方法來獲取輸入值.
獲取目錄, 創建文件
PSI
插件開發中, 使用PSI Files
來表示具體操作的文件.
PSI(Program Structure Interface) file
是一個接口, 使用樹狀結構表示文件中的內容.
對于特定的文件一般有相應的子類, 例如PsiJavaFile
表示一個Java文件.
如何獲取PSI
- 在Action中通過
AnActionEvent#getData(LangDataKeys.PSI_FILE)
- 在VirtualFile中通過
PsiManager.getInstance(project).findFile()
- 在Document中通過
PsiDocumentManager.getInstance(project).getPsiFile()
- 通過
psiElement.getContainingFile()
從element實例獲取 - 在project中的任意位置通過文件名獲取
FilenameIndex.getFilesByName(project, name, scope)
這個插件的入口是Action, 所以可以通過Action就能獲取到當前用戶所在的Java文件的PSI
實例.
PSI
的操作可以參考PSI Cookbook.
獲取目錄
當已經有PsiFile
實例時, 可以通過獲取PsiFile#getParent()
獲取到目錄實例PsiDirectory
.
創建類文件
對于Java來說, 可以通過JavaDirectoryService#createClass(dir, fileName)
來創建類文件, 同時會返回一個PsiClass
實例表示該類.
修改類文件
對于Java, 修改類文件首先要獲取想要修改的對象對應的PsiElement
子類實例(下面稱PsiElement
為元素), 例如類定義對應PsiClass
實例, 方法定義對應PsiMethod
等等.
在Java文件中所有東西都是用元素表示的. 整個Java文件是一棵元素樹, 根節點是一個
PsiClass
.
通過PsiElement#add()
可以給這個元素添加其他元素.
刪除對象包含的元素則需要調用要刪除的元素的PsiElement#delete()
方法.
大部分
PsiElement
都可以通過PsiElementFactory
中的相關方法獲取.
修改修飾符
只要部分元素有修飾符, 這部分元素都會繼承
PsiModifierListOwner
.
通過PsiModifierListOwner#getModifierList()
可以獲取修飾符列表PsiModifierList
實例.
注意修飾符列表本身也是一個元素.
通過PsiElement#add()
方法就可以增加修飾符.
通過遍歷PsiModifierList
中包含的元素, 然后調用包含的元素的PsiElement#delete()
來刪除修飾符.
修改繼承關系
只有類元素有繼承關系.
與修飾符類似, 通過PsiClass#getExtendsList()
來獲取繼承關系列表, 然后通過add
操作來增加繼承關系.
刪除繼承關系需要PsiClass#getExtendsList().getReferenceElements()
來獲取元素, 然后刪除這些元素.
寫入變量
變量元素對應的實例為PsiField
, 可以通過PsiElementFactory#createField
來創建變量, 創建變量時需要通過PsiType
指定變量的類型, 同樣可以通過PsiElementFactory#create
寫入方法
方法元素對應的實例為PsiMethod
.
構造函數屬于方法元素, 通過PsiElementFactory#createConstructor()
來創建.
方法變量
只有方法元素有方法變量.
可以通過PsiMethod#getParameterList()
獲取方法變量列表, 然后修改.
方法變量也是一個元素, 實例是PsiParameter
, 同樣可以通過工廠方法創建, 創建時需要指定變量類型.
方法體
方法體也是一個元素, 實例是PsiCodeBlock
通過PsiMethod#getBody()
來獲取方法體元素, 然后通過增加statement和expression來寫方法體內容.
理所當然, statement和expression也是元素, 可以通過工廠方法創建.
解析json
解析json需要引入json庫, 右鍵項目有Open Module Settings選項, 打開面板有Dependencies面板, 可以添加依賴.
后續就是簡單的創建類和變量了. 不再累述.
總結
- 關鍵是取得
PsiFile
類實例, 一切文件操作都是以此為根據. - 理解
PsiElement
元素, 整個文件就是一棵元素樹. 類是元素, 方法是元素, 方法體是元素, 語句是元素, 關鍵字也是元素. -
PsiElementFactory
是個好東西, 能夠很方便地創建元素, 其中的createXXXFromText
更是個黑科技. - 對于Java來說,
JavaPsiFacade
也是個好東西. 能夠通過名稱獲取PsiClass
等 - 讀寫文件需要使用
WriteCommandAction.runWriteCommandAction()
來啟用工作線程. - 官方文檔很水, 論壇還湊合.
- 方法的使用可以看源碼的注釋. 例如
PsiElementFactory
的源碼.