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