• 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         FileOutputStream fw = null;
318         if (!file.exists()) {
319             boolean isNewFile = file.createNewFile();
320             if (!isNewFile) {
321                 LOG.info("writeTmpFile createNewFile error");
322             }
323         }
324         try {
325             fw = new FileOutputStream(file);
326             fw.write(bs, 0, bs.length);
327         } catch (IOException e) {
328             // 处理可能发生的IOException
329             LOG.error("Error reading from process streams", e);
330         } finally {
331             try {
332                 fw.close();
333             } catch (IOException e) {
334                 LOG.error("Error closing stdInput", e);
335             }
336         }
337     }
338 
339     /**
340      * 获取生成成功结果文件。
341      *
342      * @param process 进程ID
343      */
genResultLog(Process process)344     private void genResultLog(Process process) {
345         BufferedReader stdInput = null;
346         BufferedReader stdError = null;
347         try {
348             stdInput = new BufferedReader(new InputStreamReader(process.getInputStream(),
349                 StandardCharsets.UTF_8));
350             stdError = new BufferedReader(new InputStreamReader(process.getErrorStream(),
351                 StandardCharsets.UTF_8));
352             String sErr;
353             String sOut;
354             sErr = getErrorResult(stdError);
355             if (TextUtils.isEmpty(sErr)) {
356                 sOut = genInputLog(stdInput);
357                 if (!generateIsSuccess(sOut)) {
358                     sErrorMessage = sOut;
359                 }
360             } else {
361                 generateSuccess = false;
362                 sErrorMessage = sErr;
363             }
364         } catch (IOException e) {
365             // 处理可能发生的IOException
366             LOG.error("Error in genResultLog", e);
367         } finally {
368             // 确保BufferedReader对象被关闭
369             try {
370                 stdInput.close();
371             } catch (IOException e) {
372                 LOG.error("Error closing stdInput", e); // 记录关闭stdInput时的错误
373             }
374             try {
375                 stdError.close();
376             } catch (IOException e) {
377                 LOG.error("Error closing stdError", e); // 记录关闭stdError时的错误
378             }
379         }
380     }
381 
382     /**
383      * 获取生成失败结果文件。
384      *
385      * @param stdError error buff
386      * @return ErrorResult
387      */
getErrorResult(BufferedReader stdError)388     private String getErrorResult(BufferedReader stdError) {
389         StringBuilder sErr = new StringBuilder();
390         while (true) {
391             String sTmp;
392             try {
393                 if ((sTmp = stdError.readLine()) == null) {
394                     break;
395                 }
396                 sErr.append(sTmp).append(FileUtil.getNewline());
397             } catch (IOException ioException) {
398                 LOG.error(" genResultLog stdInput error" + ioException);
399             }
400         }
401         return sErr.toString();
402     }
403 
generateIsSuccess(String sOut)404     private boolean generateIsSuccess(String sOut) {
405         generateSuccess = sOut.contains("success") || TextUtils.isEmpty(sOut);
406         return generateSuccess;
407     }
408 
409     /**
410      * 获取生成文本内容。
411      *
412      * @param stdInput input buff
413      * @return 返回当前输入框内容
414      */
genInputLog(BufferedReader stdInput)415     private String genInputLog(BufferedReader stdInput) {
416         StringBuilder sOut = new StringBuilder();
417         while (true) {
418             String sTmp;
419             try {
420                 if ((sTmp = stdInput.readLine()) == null) {
421                     break;
422                 }
423                 sOut.append(sTmp).append(FileUtil.getNewline());
424             } catch (IOException ioException) {
425                 LOG.error(" genResultLog stdInput error" + ioException);
426             }
427         }
428         return sOut.toString();
429     }
430 
431     static class StreamConsumer extends Thread {
432         InputStream is;
433 
StreamConsumer(InputStream is)434         StreamConsumer(InputStream is) {
435             super.setName("StreamConsumer");
436             this.is = is;
437         }
438 
439         @Override
run()440         public void run() {
441             InputStreamReader inputStreamReader = null;
442             BufferedReader bufferedReader = null;
443             try {
444                 inputStreamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
445                 bufferedReader = new BufferedReader(inputStreamReader);
446                 String readLine;
447                 while ((readLine = bufferedReader.readLine()) != null) {
448                     LOG.error("StreamConsumer" + readLine);
449                 }
450             } catch (IOException ioException) {
451                 LOG.error("StreamConsumer io error" + ioException);
452             } finally {
453                 // 确保BufferedReader和InputStreamReader被关闭
454                 try {
455                     bufferedReader.close();
456                 } catch (IOException e) {
457                     LOG.error("Error closing BufferedReader", e);
458                 }
459                 try {
460                     inputStreamReader.close();
461                 } catch (IOException e) {
462                     LOG.error("Error closing inputStreamReader", e);
463                 }
464             }
465         }
466     }
467 
468     /**
469      * 获取指定输出目录下的文件列表
470      *
471      * @param outPath 输出目录
472      * @return 文件信息列表
473      */
getFileInfoList(File outPath)474     public List<FileInfo> getFileInfoList(File outPath) {
475         List<FileInfo> fileInfoList = new ArrayList<>();
476         File[] files = outPath.listFiles();
477         for (File file : files) {
478             fileInfoList.add(new FileInfo(file));
479         }
480         return fileInfoList;
481     }
482 
483     /**
484      * 执行主程序入口
485      *
486      * @return 执行状态
487      */
runFunH2ts()488     public boolean runFunH2ts() {
489         GenNotification.notifyMessage(this.project, "", "Generating Ts", NotificationType.INFORMATION);
490         copyFileToLocalPath("header_parser");
491         String command;
492         command = genCommandH2ts();
493 
494         File outPath = new File(textFieldSelectOutPath.getText());
495         List<FileInfo> oldFileList = getFileInfoList(outPath);
496         try {
497             if (!TextUtils.isEmpty(command) && callExtProcess(command)) {
498                 List<FileInfo> newFileList = getFileInfoList(outPath);
499                 newFileList.removeAll(oldFileList); // 对比命令执行前后的文件列表差异,得到新生成的文件列表
500                 GenNotification.notifyGenResult(project, newFileList, "Generate Ts Successfully",
501                         NotificationType.INFORMATION);
502                 return true;
503             }
504         } catch (IOException | InterruptedException ex) {
505             GenNotification.notifyMessage(project, textFieldSelectOutPath.getText(), "Command exec error",
506                     NotificationType.ERROR);
507             LOG.error(ex);
508         }
509         return false;
510     }
511 
512     /**
513      * 生成命令行指令
514      *
515      * @return 返回命令行执行内容
516      */
genCommandH2ts()517     private String genCommandH2ts() {
518         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
519         String tmpDirFile = System.getProperty("java.io.tmpdir");
520         if (sysName.contains("WIN")) {
521             copyFileToLocalPath("napi_generator-win");
522             tmpDirFile += "napi_generator-win.exe";
523         } else if (sysName.contains("LINUX")) {
524             copyFileToLocalPath("napi_generator-linux");
525             tmpDirFile += "napi_generator-linux";
526         } else {
527             copyFileToLocalPath("napi_generator-macos");
528             tmpDirFile += "napi_generator-macos";
529         }
530 
531         File file = new File(tmpDirFile);
532         String command = file.toString();
533         String inArgs = genInArgs(textFieldSelectH.getText());
534         command += inArgs + " -o " + textFieldSelectOutPath.getText() + " -t " + true;
535         return command;
536     }
537 
getContentPanel()538     JPanel getContentPanel() {
539         return contentPane;
540     }
541 
542     /**
543      * 获取生成的.d.ts文件名
544      *
545      * @return 生成的.d.ts文件名
546      */
getTsFileName()547     public String getTsFileName() {
548         hGenFileName = textFieldSelectH.getText();
549         tsGenFileName = hGenFileName.substring(0, hGenFileName.lastIndexOf(".")) + ".d.ts";
550         return tsGenFileName;
551     }
552 
553     /**
554      * 获取生成的.d.ts文件输出路径
555      *
556      * @return .d.ts文件输出路径
557      */
getTsOutPath()558     public String getTsOutPath() {
559         tsOutPath = textFieldSelectOutPath.getText();
560         return tsOutPath;
561     }
562 
563     /**
564      * 获取工程
565      *
566      * @return 当前工程
567      */
getProject()568     public Project getProject() {
569         return this.project;
570     }
571 
572 }
573