• 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.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 import com.sk.action.BrowseAction;
22 import com.sk.action.GenAction;
23 import com.sk.action.ScriptAction;
24 import com.sk.action.SelectHAction;
25 import com.sk.action.SelectOutPathAction;
26 import com.sk.utils.FileUtil;
27 import com.sk.utils.GenNotification;
28 import org.apache.http.util.TextUtils;
29 import org.jetbrains.annotations.Nullable;
30 
31 import javax.swing.JButton;
32 import javax.swing.JComponent;
33 import javax.swing.JDialog;
34 import javax.swing.JPanel;
35 import javax.swing.JRadioButton;
36 import javax.swing.JTabbedPane;
37 import javax.swing.JTextField;
38 import javax.swing.KeyStroke;
39 import java.awt.event.KeyEvent;
40 import java.awt.event.WindowAdapter;
41 import java.awt.event.WindowEvent;
42 import java.awt.event.WindowListener;
43 import java.io.BufferedReader;
44 import java.io.File;
45 import java.io.FileOutputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51 
52 /**
53  * 配置对话框
54  *
55  * @author: xudong
56  * @see: generator dialog
57  * @version: v1.0.0
58  * @since 2022-02-21
59  */
60 public class GenerateDialogPane extends JDialog {
61     private static final Logger LOG = Logger.getInstance(GenerateDialogPane.class);
62     private static final String COMMAND_STATEMENT = "add_library(napitest SHARED x_napi_tool.cpp napitest.cpp "
63             + "napitest_middle.cpp)" + FileUtil.getNewline() + "target_link_libraries(napitest libace_napi.z.so)";
64     private static final String REGEX = "napitest";
65     private static final Pattern LF_PATTERN = Pattern.compile(REGEX, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
66 
67     private final Project project;
68     private JPanel contentPane;
69     private JTabbedPane tabbedPane;
70     private JTextField textFieldSelectH;
71     private JTextField textFieldSelectOutPath;
72     private JTextField textFieldInterPath;
73     private JTextField textFieldGenPath;
74     private JTextField textFieldScriptPath;
75     private JRadioButton radioButton;
76     private JButton buttonSelectInter;
77     private JButton buttonSelectGenPath;
78     private JButton buttonSelectScriptPath;
79     private JButton buttonSelectH;
80     private JButton buttonSelectOutPath;
81     private boolean generateSuccess = true;
82     private String sErrorMessage = "";
83     private String interFileOrDir;
84     private String genOutDir;
85     private String scriptOutDir;
86     private int selectedIndex;
87 
88     /**
89      * 构造函数
90      *
91      * @param project       projectid
92      * @param interFilePath 接口文件路径
93      * @param genDir        生成框架文件路径
94      * @param scriptDir     脚本目录
95      */
GenerateDialogPane(Project project, String interFilePath, String genDir, String scriptDir)96     public GenerateDialogPane(Project project, String interFilePath, String genDir, String scriptDir) {
97         setContentPane(contentPane);
98         setModal(true);
99         this.project = project;
100         this.interFileOrDir = interFilePath;
101         this.genOutDir = genDir;
102         this.scriptOutDir = scriptDir;
103         if (FileUtil.patternFileNameH(scriptDir)) {
104             textFieldSelectH.setText(interFileOrDir);
105             textFieldSelectOutPath.setText(genOutDir);
106             tabbedPane.setSelectedIndex(1);
107             selectedIndex = 1;
108         } else {
109             textFieldInterPath.setText(interFileOrDir);
110             textFieldGenPath.setText(genOutDir);
111             textFieldScriptPath.setText(genOutDir);
112         }
113         // call onCancel() on ESCAPE
114         contentPane.registerKeyboardAction(actionEvent -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
115                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
116 
117         BrowseAction browseAction = new BrowseAction(project, buttonSelectInter, textFieldInterPath,
118                 textFieldGenPath, textFieldScriptPath);
119         buttonSelectInter.addActionListener(browseAction);
120         buttonSelectGenPath.addActionListener(new GenAction(buttonSelectGenPath, textFieldGenPath));
121         buttonSelectScriptPath.addActionListener(new ScriptAction(buttonSelectScriptPath, textFieldScriptPath));
122         buttonSelectH.addActionListener(new SelectHAction(buttonSelectH, textFieldSelectH));
123         buttonSelectOutPath.addActionListener(new SelectOutPathAction(buttonSelectOutPath, textFieldSelectOutPath));
124         tabbedPane.addChangeListener(changeEvent -> selectedIndex = tabbedPane.getSelectedIndex());
125     }
126 
127     @Override
addWindowListener(WindowListener windowListener)128     public synchronized void addWindowListener(WindowListener windowListener) {
129         super.addWindowListener(windowListener);
130         new WindowAdapter() {
131             /**
132              * close dialog
133              *
134              * @param windowEvent WindowEvent
135              */
136             @Override
137             public void windowClosing(WindowEvent windowEvent) {
138                 onCancel();
139             }
140         };
141     }
142 
143     /**
144      * 验证文本选择框是否空。是否替换已存在的内容
145      *
146      * @return ValidationInfo 返回不符要求的信息。
147      */
148     @Nullable
validationInfo()149     public ValidationInfo validationInfo() {
150         ValidationInfo validationInfo = null;
151         if (selectedIndex == 0) {
152             String fileInter = textFieldInterPath.getText();
153             String scriptDir = textFieldScriptPath.getText();
154             String filegypDir = textFieldGenPath.getText();
155             boolean isEmptyFile =
156                     TextUtils.isEmpty(fileInter) || TextUtils.isEmpty(scriptDir) || TextUtils.isEmpty(filegypDir);
157             if (isEmptyFile) {
158                 String warnMsg = "接口文件、框架、编译脚本路径不能为空";
159                 warningMessage(warnMsg);
160                 validationInfo = new ValidationInfo(warnMsg);
161                 return validationInfo;
162             }
163             File file = new File(filegypDir + "/binding.gyp");
164             if (file.exists()) {
165                 ConfirmDialog confirmDialog = new ConfirmDialog("是否替换已存在的编译脚本?");
166                 if (!confirmDialog.showAndGet()) {
167                     validationInfo = new ValidationInfo(String.format("不替换现有编译脚本:%s", file));
168                     return validationInfo;
169                 }
170             }
171         } else {
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         return validationInfo;
183     }
184 
onCancel()185     private void onCancel() {
186         dispose();
187     }
188 
warningMessage(String title)189     private void warningMessage(String title) {
190         String notifyContent = "请选择接口文件或文件夹,生成框架路径,编译脚本路径";
191         GenNotification.notifyMessage(this.project, notifyContent, title, NotificationType.WARNING);
192     }
193 
194     /**
195      * 执行主程序入口
196      *
197      * @return 执行状态
198      */
runFun()199     public boolean runFun() {
200         GenNotification.notifyMessage(this.project, "", "正在生成", NotificationType.INFORMATION);
201         interFileOrDir = textFieldInterPath.getText();
202         genOutDir = textFieldGenPath.getText();
203         scriptOutDir = textFieldScriptPath.getText();
204         copyFileToLocalPath("header_parser");
205         String command;
206         command = genCommand();
207 
208         try {
209             if (!TextUtils.isEmpty(command) && callExtProcess(command)) {
210                 GenNotification.notifyMessage(project, textFieldGenPath.getText(), "提示",
211                         NotificationType.INFORMATION, true);
212                 return true;
213             }
214         } catch (IOException | InterruptedException ex) {
215             GenNotification.notifyMessage(project, textFieldGenPath.getText(), "Command exec error",
216                     NotificationType.ERROR);
217             LOG.error(ex);
218         }
219         return false;
220     }
221 
222     /**
223      * 生成命令行指令
224      *
225      * @return 返回命令行执行内容
226      */
genCommand()227     private String genCommand() {
228         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
229         String tmpDirFile = System.getProperty("java.io.tmpdir");
230         if (sysName.contains("WIN")) {
231             copyFileToLocalPath("napi_generator-win");
232             tmpDirFile += "napi_generator-win.exe";
233         } else if (sysName.contains("LINUX")) {
234             copyFileToLocalPath("napi_generator-linux");
235             tmpDirFile += "napi_generator-linux";
236         } else {
237             copyFileToLocalPath("napi_generator-macos");
238             tmpDirFile += "napi_generator-macos";
239         }
240         File file = new File(tmpDirFile);
241         String command = file.toString();
242         String inArgs = genInArgs();
243         command += inArgs + " -o " + genOutDir + " -i " + radioButton.isSelected();
244         return command;
245     }
246 
247     /**
248      * 拷贝文件到本地临时目录
249      *
250      * @param fileName 文件名
251      */
copyFileToLocalPath(String fileName)252     private void copyFileToLocalPath(String fileName) {
253         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
254         String tmpDirFile = System.getProperty("java.io.tmpdir");
255         String execFn;
256         if (sysName.contains("WIN")) {
257             execFn = "cmds/win/" + fileName + ".exe";
258             tmpDirFile += fileName + ".exe";
259         } else if (sysName.contains("LINUX")) {
260             execFn = "cmds/linux/" + fileName;
261             tmpDirFile += fileName;
262         } else {
263             execFn = "cmds/mac/" + fileName;
264             tmpDirFile += fileName;
265         }
266         try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(execFn)) {
267             if (inputStream == null) {
268                 throw new IOException("exec File InputStream is Null");
269             }
270             byte[] bs = inputStream.readAllBytes();
271             writeTmpFile(tmpDirFile, bs);
272             if (sysName.contains("LINUX") || sysName.contains("MAC OS")) {
273                 executable(tmpDirFile);
274             }
275         } catch (IOException | InterruptedException e) {
276             GenNotification.notifyMessage(this.project, e.getMessage(), "Can not Find File:" + execFn,
277                     NotificationType.ERROR);
278             LOG.error(e);
279         }
280     }
281 
282     /**
283      * 生成 -f -d 输入参数。
284      *
285      * @return 生成后的值-f -d的值
286      */
genInArgs()287     private String genInArgs() {
288         String[] interArr = interFileOrDir.split(",");
289         StringBuilder tsParam = new StringBuilder(" -f ");
290         StringBuilder dirParam = new StringBuilder(" -d ");
291         String inputCommand = "";
292         if (interArr.length > 0) {
293             for (String interStr : interArr) {
294                 File interFile = new File(interStr);
295                 if (interFile.isDirectory()) {
296                     dirParam.append(interStr).append(" ");
297                 } else {
298                     tsParam.append(interStr).append(",");
299                 }
300             }
301             if (!TextUtils.isEmpty(tsParam.toString().replaceAll("-f", ""))
302                     && !TextUtils.isBlank(tsParam.toString().replaceAll("-f", ""))) {
303                 inputCommand += tsParam.substring(0, tsParam.length() - 1);
304             }
305             if (!TextUtils.isEmpty(dirParam.toString().replace("-d", ""))
306                     && !TextUtils.isBlank(dirParam.toString().replace("-d", ""))) {
307                 inputCommand += dirParam.substring(0, dirParam.length() - 1);
308             }
309         }
310         return inputCommand;
311     }
312 
callExtProcess(String command)313     private boolean callExtProcess(String command) throws IOException, InterruptedException {
314 
315         if (TextUtils.isEmpty(command)) {
316             GenNotification.notifyMessage(this.project, "执行命令文件为空", "空命令行提示", NotificationType.ERROR);
317             return false;
318         }
319         Process process = Runtime.getRuntime().exec(command);
320         genResultLog(process);
321         StreamConsumer errConsumer = new StreamConsumer(process.getErrorStream());
322         StreamConsumer outputConsumer = new StreamConsumer(process.getInputStream());
323         errConsumer.start();
324         outputConsumer.start();
325 
326         if (generateSuccess) {
327             writeCommand();
328         } else {
329             GenNotification.notifyMessage(project, sErrorMessage, "提示", NotificationType.ERROR);
330             return false;
331         }
332         errConsumer.join();
333         outputConsumer.join();
334         return true;
335     }
336 
337     /**
338      * 写makeFile.txt文件
339      */
writeCommand()340     private void writeCommand() {
341         FileUtil fileUtil = new FileUtil();
342         String filePath = fileUtil.makeFile(genOutDir + "/makeFile.txt");
343         if (TextUtils.isEmpty(filePath)) {
344             LOG.info("makeFile is fail");
345             return;
346         }
347         Matcher matcher = LF_PATTERN.matcher(COMMAND_STATEMENT);
348         String statement = matcher.replaceAll(scriptOutDir);
349         try {
350             if (!fileUtil.findStringInFile(filePath, statement)) {
351                 fileUtil.writeErrorToTxt(filePath, statement);
352             }
353         } catch (IOException ioException) {
354             LOG.error("writeCommand io error" + ioException);
355         }
356     }
357 
358     /**
359      * 赋值可执行文件权限。
360      *
361      * @param execFn 可执行命令
362      * @throws IOException          打开文件异常
363      * @throws InterruptedException 中断异常
364      */
executable(String execFn)365     private void executable(String execFn) throws IOException, InterruptedException {
366         callExtProcess("chmod a+x " + execFn);
367     }
368 
369     /**
370      * 拷贝可执行文件到临时文件夹
371      *
372      * @param path 目标文件路径
373      * @param bs   字节内容
374      * @throws IOException exception
375      */
writeTmpFile(String path, byte[] bs)376     private void writeTmpFile(String path, byte[] bs) throws IOException {
377         File file = new File(path);
378         if (!file.exists()) {
379             boolean isNewFile = file.createNewFile();
380             if (!isNewFile) {
381                 LOG.info("writeTmpFile createNewFile error");
382             }
383         }
384         FileOutputStream fw = new FileOutputStream(file);
385         fw.write(bs, 0, bs.length);
386         fw.close();
387     }
388 
389     /**
390      * 获取生成成功结果文件。
391      *
392      * @param process 进程ID
393      */
genResultLog(Process process)394     private void genResultLog(Process process) {
395         BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
396         BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
397         String sErr;
398         String sOut;
399         sErr = getErrorResult(stdError);
400         if (TextUtils.isEmpty(sErr)) {
401             sOut = genInputLog(stdInput);
402             if (!generateIsSuccess(sOut)) {
403                 sErrorMessage = sOut;
404             }
405             return;
406         }
407         generateSuccess = false;
408         sErrorMessage = sErr;
409     }
410 
411     /**
412      * 获取生成失败结果文件。
413      *
414      * @param stdError error buff
415      * @return ErrorResult
416      */
getErrorResult(BufferedReader stdError)417     private String getErrorResult(BufferedReader stdError) {
418         StringBuilder sErr = new StringBuilder();
419         while (true) {
420             String sTmp;
421             try {
422                 if ((sTmp = stdError.readLine()) == null) {
423                     break;
424                 }
425                 sErr.append(sTmp).append(FileUtil.getNewline());
426             } catch (IOException ioException) {
427                 LOG.error(" genResultLog stdInput error" + ioException);
428             }
429         }
430         return sErr.toString();
431     }
432 
generateIsSuccess(String sOut)433     private boolean generateIsSuccess(String sOut) {
434         generateSuccess = sOut.contains("success") || TextUtils.isEmpty(sOut);
435         return generateSuccess;
436     }
437 
438     /**
439      * 获取生成文本内容。
440      *
441      * @param stdInput input buff
442      * @return 返回当前输入框内容
443      */
genInputLog(BufferedReader stdInput)444     private String genInputLog(BufferedReader stdInput) {
445         StringBuilder sOut = new StringBuilder();
446         while (true) {
447             String sTmp;
448             try {
449                 if ((sTmp = stdInput.readLine()) == null) {
450                     break;
451                 }
452                 sOut.append(sTmp).append(FileUtil.getNewline());
453             } catch (IOException ioException) {
454                 LOG.error(" genResultLog stdInput error" + ioException);
455             }
456         }
457         return sOut.toString();
458     }
459 
460     static class StreamConsumer extends Thread {
461         InputStream inS;
462 
StreamConsumer(InputStream in)463         StreamConsumer(InputStream in) {
464             super.setName("StreamConsumer");
465             this.is = in;
466         }
467 
468         @Override
run()469         public void run() {
470             try {
471                 InputStreamReader inStr = new InputStreamReader(in);
472                 BufferedReader bufR = new BufferedReader(inStr);
473                 String lineStr;
474                 while ((lineStr = bufR.readLine()) != null) {
475                     LOG.error("StreamConsumer" + lineStr);
476                 }
477             } catch (IOException ioException) {
478                 LOG.error("StreamConsumer io error" + ioException);
479             }
480         }
481     }
482 
483     /**
484      * 执行主程序入口
485      *
486      * @return 执行状态
487      */
runFunH2ts()488     public boolean runFunH2ts() {
489         GenNotification.notifyMessage(this.project, "", "正在生成", NotificationType.INFORMATION);
490         copyFileToLocalPath("header_parser");
491         String command;
492         command = genCommandH2ts();
493         try {
494             if (!TextUtils.isEmpty(command) && callExtProcess(command)) {
495                 GenNotification.notifyMessage(project, textFieldSelectOutPath.getText(), "提示",
496                         NotificationType.INFORMATION, true);
497                 return true;
498             }
499         } catch (IOException | InterruptedException ex) {
500             GenNotification.notifyMessage(project, textFieldSelectOutPath.getText(), "Command exec error",
501                     NotificationType.ERROR);
502             LOG.error(ex);
503         }
504         return false;
505     }
506 
507     /**
508      * 生成命令行指令
509      *
510      * @return 返回命令行执行内容
511      */
genCommandH2ts()512     private String genCommandH2ts() {
513         String sysName = System.getProperties().getProperty("os.name").toUpperCase();
514         String tmpDirFile = System.getProperty("java.io.tmpdir");
515         if (sysName.contains("WIN")) {
516             copyFileToLocalPath("napi_generator-win");
517             tmpDirFile += "napi_generator-win.exe";
518         } else if (sysName.contains("LINUX")) {
519             copyFileToLocalPath("napi_generator-linux");
520             tmpDirFile += "napi_generator-linux";
521         } else {
522             copyFileToLocalPath("napi_generator-macos");
523             tmpDirFile += "napi_generator-macos";
524         }
525         File file = new File(tmpDirFile);
526         String command = file.toString();
527         command += " -f " + textFieldSelectH.getText() + " -o " + textFieldSelectOutPath.getText();
528         return command;
529     }
530 
getContentPanel()531     JPanel getContentPanel() {
532         return contentPane;
533     }
534 
getSelectedIndex()535     public int getSelectedIndex() {
536         return selectedIndex;
537     }
538 }
539