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