• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 package com.sk.ts.dialog;
16 
17 import com.intellij.notification.NotificationType;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.ui.ValidationInfo;
21 
22 import com.sk.ts.action.SelectHAction;
23 import com.sk.ts.action.SelectOutPathAction;
24 import com.sk.ts.utils.FileInfo;
25 import com.sk.ts.utils.FileUtil;
26 import com.sk.ts.utils.GenNotification;
27 import org.apache.http.util.TextUtils;
28 import org.jetbrains.annotations.Nullable;
29 import javax.swing.JDialog;
30 import javax.swing.JPanel;
31 import javax.swing.JTextField;
32 import javax.swing.JButton;
33 import javax.swing.JComponent;
34 import javax.swing.KeyStroke;
35 import java.awt.event.KeyEvent;
36 import java.awt.event.WindowAdapter;
37 import java.awt.event.WindowEvent;
38 import java.awt.event.WindowListener;
39 import java.io.BufferedReader;
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * 配置对话框
50  *
51  * @author: xudong
52  * @see: generator dialog
53  * @version: v1.0.0
54  * @since 2022-02-21
55  */
56 public class GenerateDialogPane extends JDialog {
57     private static final Logger LOG = Logger.getInstance(GenerateDialogPane.class);
58 
59     private final Project project;
60     private List<String> tsFileList = new ArrayList<>();
61     private JPanel contentPane;
62     private JTextField textFieldSelectH;
63     private JTextField textFieldSelectOutPath;
64 
65     private JButton buttonSelectH;
66     private JButton buttonSelectOutPath;
67 
68     private boolean generateSuccess = true;
69     private String sErrorMessage = "";
70     private String interFileOrDir;
71     private String genOutDir;
72     private String scriptOutDir;
73     private String hGenFileName;
74     private String tsGenFileName;
75     private String tsOutPath;
76 
77 
78     /**
79      * 构造函数
80      *
81      * @param project       projectid
82      * @param interFilePath 接口文件路径
83      * @param genDir        生成框架文件路径
84      * @param scriptDir     脚本目录
85      */
GenerateDialogPane(Project project, String interFilePath, String genDir, String scriptDir)86     public GenerateDialogPane(Project project, String interFilePath, String genDir, String scriptDir) {
87         setContentPane(contentPane);
88         setModal(true);
89         this.project = project;
90         this.interFileOrDir = interFilePath;
91         this.genOutDir = genDir;
92         this.scriptOutDir = scriptDir;
93         if (FileUtil.patternFileNameH(scriptDir)) {
94             textFieldSelectH.setText(interFileOrDir);
95             textFieldSelectOutPath.setText(genOutDir);
96         }
97         contentPane.registerKeyboardAction(actionEvent -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
98                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
99 
100         buttonSelectH.addActionListener(new SelectHAction(buttonSelectH, textFieldSelectH,
101                 textFieldSelectOutPath, project));
102         buttonSelectOutPath.addActionListener(new SelectOutPathAction(buttonSelectOutPath, textFieldSelectOutPath));
103 
104     }
105 
106     @Override
addWindowListener(WindowListener windowListener)107     public synchronized void addWindowListener(WindowListener windowListener) {
108         super.addWindowListener(windowListener);
109         new WindowAdapter() {
110             /**
111              * close dialog
112              *
113              * @param windowEvent WindowEvent
114              */
115             @Override
116             public void windowClosing(WindowEvent windowEvent) {
117                 onCancel();
118             }
119         };
120     }
121 
122     /**
123      * 检查输入的.h文件转换后的同名.d.ts文件是否在输出目录中已存在
124      *
125      * @param fileObj .h文件对象
126      * @param outPutDir 输出目录
127      * @return 如果ts文件已存在,返回文件名;否则返回空字符串
128      */
getExistFileName(File fileObj, String outPutDir)129     private String getExistFileName(File fileObj, String outPutDir) {
130         if (fileObj.isDirectory()) {
131             // 遇到文件夹直接跳过不检查,只检查普通.h文件是否存在对应的.ts
132             return "";
133         }
134         String hFileName = fileObj.getName();
135         String tsFileName = hFileName.substring(0, hFileName.lastIndexOf(".")) + ".d.ts";
136         File tsFile = new File(outPutDir + "/" + tsFileName);
137         return tsFile.exists() ? tsFile.toString() : "";
138     }
139 
140     /**
141      * 检查待生成的ts文件在输出目录中是否已存在
142      *
143      * @param hFilePath 待转换的.h文件/目录路径
144      * @param outPutDir 输出目录路径
145      * @return 如果ts文件已存在,返回文件名;否则返回空字符串
146      */
checkTsFileExist(String hFilePath, String outPutDir)147     private String checkTsFileExist(String hFilePath, String outPutDir) {
148         File hFileObj = new File(hFilePath);
149         if (!hFileObj.isDirectory()) {
150             return getExistFileName(hFileObj, outPutDir);
151         } else {
152             File[] fileList = hFileObj.listFiles();
153             for (File fileObj : fileList) {
154                 String existFileName = getExistFileName(fileObj, outPutDir);
155                 if (!existFileName.equals("")) {
156                     return existFileName;
157                 }
158             }
159         }
160         return "";
161     }
162 
163     /**
164      * 验证文本选择框是否空。是否替换已存在的内容
165      *
166      * @return ValidationInfo 返回不符要求的信息。
167      */
168     @Nullable
validationInfo()169     public ValidationInfo validationInfo() {
170         ValidationInfo validationInfo = null;
171 
172         String hFile = textFieldSelectH.getText();
173         String outPutDir = textFieldSelectOutPath.getText();
174         boolean isEmptyFile = TextUtils.isEmpty(hFile) || TextUtils.isEmpty(outPutDir);
175         if (isEmptyFile) {
176             String warnMsg = "文件路径、输出路径不能为空";
177             warningMessage(warnMsg);
178             validationInfo = new ValidationInfo(warnMsg);
179             return validationInfo;
180         }
181 
182         String existFileName = checkTsFileExist(hFile, outPutDir);
183         if (!existFileName.equals("")) {
184             ConfirmDialog confirmDialog = new ConfirmDialog(
185                     String.format("是否替换已存在的文件:%s ?", existFileName));
186             if (!confirmDialog.showAndGet()) {
187                 validationInfo = new ValidationInfo(String.format("不替换现有文件:%s", existFileName));
188                 return validationInfo;
189             }
190         }
191 
192         return validationInfo;
193     }
194 
onCancel()195     private void onCancel() {
196         dispose();
197     }
198 
warningMessage(String title)199     private void warningMessage(String title) {
200         String notifyContent = "请选择接口文件或文件夹,生成框架路径,编译脚本路径";
201         GenNotification.notifyMessage(this.project, notifyContent, title, NotificationType.WARNING);
202     }
203 
204     /**
205      * 拷贝文件到本地临时目录
206      *
207      * @param fileName 文件名
208      */
copyFileToLocalPath(String fileName)209     private void copyFileToLocalPath(String fileName) {
210         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
211         String tmpDirFile = System.getProperty("java.io.tmpdir");
212         String execFn;
213         if (sysName.contains("WIN")) {
214             execFn = "cmds/win/" + fileName + ".exe";
215             tmpDirFile += fileName + ".exe";
216         } else if (sysName.contains("LINUX")) {
217             execFn = "cmds/linux/" + fileName;
218             tmpDirFile += fileName;
219         } else {
220             execFn = "cmds/mac/" + fileName;
221             tmpDirFile += fileName;
222         }
223         try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(execFn)) {
224             if (inputStream == null) {
225                 throw new IOException("exec File InputStream is Null");
226             }
227             byte[] bs = inputStream.readAllBytes();
228             writeTmpFile(tmpDirFile, bs);
229             if (sysName.contains("LINUX") || sysName.contains("MAC OS")) {
230                 executable(tmpDirFile);
231             }
232         } catch (IOException | InterruptedException e) {
233             GenNotification.notifyMessage(this.project, e.getMessage(), "Can not Find File:" + execFn,
234                     NotificationType.ERROR);
235             LOG.error(e);
236         }
237     }
238 
239     /**
240      * 生成 -f -d 输入参数。
241      *
242      * @param fileOrDir 选中的文件或文件夹路径
243      * @return 生成后的 -f -d的值
244      */
genInArgs(String fileOrDir)245     private String genInArgs(String fileOrDir) {
246         tsFileList.clear();
247         String[] interArr = fileOrDir.split(",");
248         StringBuilder tsParam = new StringBuilder(" -f ");
249         StringBuilder dirParam = new StringBuilder(" -d ");
250         String inputCommand = "";
251         if (interArr.length > 0) {
252             for (String interStr : interArr) {
253                 File interFile = new File(interStr);
254                 if (interFile.isDirectory()) {
255                     dirParam.append(interStr).append(" ");
256                     for (File tsFile : interFile.listFiles()) {
257                         tsFileList.add(tsFile.getPath());
258                     }
259                 } else {
260                     tsParam.append(interStr).append(",");
261                     tsFileList.add(interStr);
262                 }
263             }
264             if (!TextUtils.isBlank(tsParam.toString().replaceAll("-f", ""))) {
265                 inputCommand += tsParam.substring(0, tsParam.length() - 1);
266             }
267             if (!TextUtils.isBlank(dirParam.toString().replace("-d", ""))) {
268                 inputCommand += dirParam.substring(0, dirParam.length() - 1);
269             }
270         }
271         return inputCommand;
272     }
273 
callExtProcess(String command)274     private boolean callExtProcess(String command) throws IOException, InterruptedException {
275 
276         if (TextUtils.isEmpty(command)) {
277             GenNotification.notifyMessage(this.project, "执行命令文件为空", "空命令行提示", NotificationType.ERROR);
278             return false;
279         }
280         Process process = Runtime.getRuntime().exec(command);
281         genResultLog(process);
282         StreamConsumer errConsumer = new StreamConsumer(process.getErrorStream());
283         StreamConsumer outputConsumer = new StreamConsumer(process.getInputStream());
284         errConsumer.start();
285         outputConsumer.start();
286 
287         if (!generateSuccess) {
288             GenNotification.notifyMessage(project, sErrorMessage, "提示", NotificationType.ERROR);
289             return false;
290         }
291 
292         errConsumer.join();
293         outputConsumer.join();
294         return true;
295     }
296 
297     /**
298      * 赋值可执行文件权限。
299      *
300      * @param execFn 可执行命令
301      * @throws IOException          打开文件异常
302      * @throws InterruptedException 中断异常
303      */
executable(String execFn)304     private void executable(String execFn) throws IOException, InterruptedException {
305         callExtProcess("chmod a+x " + execFn);
306     }
307 
308     /**
309      * 拷贝可执行文件到临时文件夹
310      *
311      * @param path 目标文件路径
312      * @param bs   字节内容
313      * @throws IOException exception
314      */
writeTmpFile(String path, byte[] bs)315     private void writeTmpFile(String path, byte[] bs) throws IOException {
316         File file = new File(path);
317         if (!file.exists()) {
318             boolean isNewFile = file.createNewFile();
319             if (!isNewFile) {
320                 LOG.info("writeTmpFile createNewFile error");
321             }
322         }
323         FileOutputStream fw = new FileOutputStream(file);
324         fw.write(bs, 0, bs.length);
325         fw.close();
326     }
327 
328     /**
329      * 获取生成成功结果文件。
330      *
331      * @param process 进程ID
332      */
genResultLog(Process process)333     private void genResultLog(Process process) {
334         BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
335         BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
336         String sErr;
337         String sOut;
338         sErr = getErrorResult(stdError);
339         if (TextUtils.isEmpty(sErr)) {
340             sOut = genInputLog(stdInput);
341             if (!generateIsSuccess(sOut)) {
342                 sErrorMessage = sOut;
343             }
344             return;
345         }
346         generateSuccess = false;
347         sErrorMessage = sErr;
348     }
349 
350     /**
351      * 获取生成失败结果文件。
352      *
353      * @param stdError error buff
354      * @return ErrorResult
355      */
getErrorResult(BufferedReader stdError)356     private String getErrorResult(BufferedReader stdError) {
357         StringBuilder sErr = new StringBuilder();
358         while (true) {
359             String sTmp;
360             try {
361                 if ((sTmp = stdError.readLine()) == null) {
362                     break;
363                 }
364                 sErr.append(sTmp).append(FileUtil.getNewline());
365             } catch (IOException ioException) {
366                 LOG.error(" genResultLog stdInput error" + ioException);
367             }
368         }
369         return sErr.toString();
370     }
371 
generateIsSuccess(String sOut)372     private boolean generateIsSuccess(String sOut) {
373         generateSuccess = sOut.contains("success") || TextUtils.isEmpty(sOut);
374         return generateSuccess;
375     }
376 
377     /**
378      * 获取生成文本内容。
379      *
380      * @param stdInput input buff
381      * @return 返回当前输入框内容
382      */
genInputLog(BufferedReader stdInput)383     private String genInputLog(BufferedReader stdInput) {
384         StringBuilder sOut = new StringBuilder();
385         while (true) {
386             String sTmp;
387             try {
388                 if ((sTmp = stdInput.readLine()) == null) {
389                     break;
390                 }
391                 sOut.append(sTmp).append(FileUtil.getNewline());
392             } catch (IOException ioException) {
393                 LOG.error(" genResultLog stdInput error" + ioException);
394             }
395         }
396         return sOut.toString();
397     }
398 
399     static class StreamConsumer extends Thread {
400         InputStream is;
401 
StreamConsumer(InputStream is)402         StreamConsumer(InputStream is) {
403             super.setName("StreamConsumer");
404             this.is = is;
405         }
406 
407         @Override
run()408         public void run() {
409             try {
410                 InputStreamReader inputStreamReader = new InputStreamReader(is);
411                 BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
412                 String readLine;
413                 while ((readLine = bufferedReader.readLine()) != null) {
414                     LOG.error("StreamConsumer" + readLine);
415                 }
416             } catch (IOException ioException) {
417                 LOG.error("StreamConsumer io error" + ioException);
418             }
419         }
420     }
421 
422     /**
423      * 获取指定输出目录下的文件列表
424      *
425      * @param outPath 输出目录
426      * @return 文件信息列表
427      */
getFileInfoList(File outPath)428     public List<FileInfo> getFileInfoList(File outPath) {
429         List<FileInfo> fileInfoList = new ArrayList<>();
430         File[] files = outPath.listFiles();
431         for (File file : files) {
432             fileInfoList.add(new FileInfo(file));
433         }
434         return fileInfoList;
435     }
436 
437     /**
438      * 执行主程序入口
439      *
440      * @return 执行状态
441      */
runFunH2ts()442     public boolean runFunH2ts() {
443         GenNotification.notifyMessage(this.project, "", "Generating Ts", NotificationType.INFORMATION);
444         copyFileToLocalPath("header_parser");
445         String command;
446         command = genCommandH2ts();
447 
448         File outPath = new File(textFieldSelectOutPath.getText());
449         List<FileInfo> oldFileList = getFileInfoList(outPath);
450         try {
451             if (!TextUtils.isEmpty(command) && callExtProcess(command)) {
452                 List<FileInfo> newFileList = getFileInfoList(outPath);
453                 newFileList.removeAll(oldFileList); // 对比命令执行前后的文件列表差异,得到新生成的文件列表
454                 GenNotification.notifyGenResult(project, newFileList, "Generate Ts Successfully",
455                         NotificationType.INFORMATION);
456                 return true;
457             }
458         } catch (IOException | InterruptedException ex) {
459             GenNotification.notifyMessage(project, textFieldSelectOutPath.getText(), "Command exec error",
460                     NotificationType.ERROR);
461             LOG.error(ex);
462         }
463         return false;
464     }
465 
466     /**
467      * 生成命令行指令
468      *
469      * @return 返回命令行执行内容
470      */
genCommandH2ts()471     private String genCommandH2ts() {
472         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
473         String tmpDirFile = System.getProperty("java.io.tmpdir");
474         if (sysName.contains("WIN")) {
475             copyFileToLocalPath("napi_generator-win");
476             tmpDirFile += "napi_generator-win.exe";
477         } else if (sysName.contains("LINUX")) {
478             copyFileToLocalPath("napi_generator-linux");
479             tmpDirFile += "napi_generator-linux";
480         } else {
481             copyFileToLocalPath("napi_generator-macos");
482             tmpDirFile += "napi_generator-macos";
483         }
484 
485         File file = new File(tmpDirFile);
486         String command = file.toString();
487         String inArgs = genInArgs(textFieldSelectH.getText());
488         command += inArgs + " -o " + textFieldSelectOutPath.getText() + " -t " + true;
489         return command;
490     }
491 
getContentPanel()492     JPanel getContentPanel() {
493         return contentPane;
494     }
495 
496     /**
497      * 获取生成的.d.ts文件名
498      *
499      * @return 生成的.d.ts文件名
500      */
getTsFileName()501     public String getTsFileName() {
502         hGenFileName = textFieldSelectH.getText();
503         tsGenFileName = hGenFileName.substring(0, hGenFileName.lastIndexOf(".")) + ".d.ts";
504         return tsGenFileName;
505     }
506 
507     /**
508      * 获取生成的.d.ts文件输出路径
509      *
510      * @return .d.ts文件输出路径
511      */
getTsOutPath()512     public String getTsOutPath() {
513         tsOutPath = textFieldSelectOutPath.getText();
514         return tsOutPath;
515     }
516 
517     /**
518      * 获取工程
519      *
520      * @return 当前工程
521      */
getProject()522     public Project getProject() {
523         return this.project;
524     }
525 
526 }
527