001package com.ganteater.ae.desktop.editor; 002 003import java.awt.BorderLayout; 004import java.awt.Dimension; 005import java.awt.FileDialog; 006import java.awt.Point; 007import java.awt.event.ActionEvent; 008import java.awt.event.ActionListener; 009import java.awt.event.FocusAdapter; 010import java.awt.event.FocusEvent; 011import java.awt.event.KeyAdapter; 012import java.awt.event.KeyEvent; 013import java.awt.event.MouseAdapter; 014import java.awt.event.MouseEvent; 015import java.util.ArrayList; 016import java.util.Arrays; 017import java.util.Comparator; 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Properties; 022import java.util.Set; 023import java.util.StringTokenizer; 024import java.util.stream.Collectors; 025 026import javax.swing.DefaultListModel; 027import javax.swing.JList; 028import javax.swing.JMenuItem; 029import javax.swing.JOptionPane; 030import javax.swing.JRootPane; 031import javax.swing.JScrollPane; 032import javax.swing.SwingUtilities; 033import javax.swing.event.ListSelectionEvent; 034import javax.swing.event.ListSelectionListener; 035import javax.swing.text.BadLocationException; 036 037import org.apache.commons.lang.StringEscapeUtils; 038import org.apache.commons.lang3.StringUtils; 039 040import com.ganteater.ae.AEWorkspace; 041import com.ganteater.ae.OperationHolder; 042import com.ganteater.ae.TaskCancelingException; 043import com.ganteater.ae.desktop.ui.DialogPopupMenu; 044import com.ganteater.ae.desktop.ui.OptionPane; 045import com.ganteater.ae.desktop.view.View; 046import com.ganteater.ae.processor.BaseProcessor; 047import com.ganteater.ae.processor.CommandInfo; 048import com.ganteater.ae.processor.Processor; 049import com.ganteater.ae.util.ClassUtils; 050import com.ganteater.ae.util.xml.easyparser.EasyParser; 051import com.ganteater.ae.util.xml.easyparser.Node; 052 053public class CommandHelperDialog extends HelperDialog { 054 055 private static final long serialVersionUID = 1L; 056 057 private DefaultListModel<String> commandListModel = new DefaultListModel<>(); 058 private JList<String> commands = new JList<String>(commandListModel); 059 060 private DefaultListModel<String> examplesListModel = new DefaultListModel<>(); 061 private JList<String> examples = new JList<String>(examplesListModel); 062 063 private String startSymbols; 064 065 private Map<String, List<String>> exampleMap = new HashMap<>(); 066 067 public CommandHelperDialog(CodeHelper codeHelper) { 068 super(codeHelper); 069 070 setAlwaysOnTop(true); 071 setUndecorated(true); 072 JScrollPane comp = new JScrollPane(commands); 073 comp.setPreferredSize(new Dimension(150, 200)); 074 075 getContentPane().add(comp, BorderLayout.WEST); 076 077 commands.setBackground(getBackground()); 078 commands.addFocusListener(new FocusAdapter() { 079 @Override 080 public void focusLost(FocusEvent e) { 081 if (e.getOppositeComponent() != CommandHelperDialog.this && e.getOppositeComponent() != examples) { 082 setVisible(false); 083 } 084 } 085 }); 086 commands.addMouseListener(new MouseAdapter() { 087 @Override 088 public void mouseClicked(MouseEvent e) { 089 String commandName = commands.getSelectedValue(); 090 if (e.getClickCount() == 2) { 091 if (examples.getModel().getSize() > 1) { 092 if (examples.isSelectionEmpty()) { 093 examples.requestFocus(); 094 examples.setSelectedIndex(0); 095 } else { 096 perform(commandName); 097 } 098 } else { 099 perform(commandName); 100 } 101 } else { 102 fillExamples(commandName); 103 } 104 } 105 }); 106 commands.addListSelectionListener(new ListSelectionListener() { 107 @Override 108 public void valueChanged(ListSelectionEvent e) { 109 String commandName = commands.getSelectedValue(); 110 fillExamples(commandName); 111 } 112 }); 113 commands.addKeyListener(new KeyAdapter() { 114 @Override 115 public void keyPressed(KeyEvent e) { 116 int keyCode = e.getKeyCode(); 117 if (keyCode == KeyEvent.VK_ESCAPE) { 118 setVisible(false); 119 } else { 120 if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_ENTER) { 121 if (examples.getModel().getSize() > 1) { 122 examples.requestFocus(); 123 examples.setSelectedIndex(0); 124 } else { 125 String commandName = commands.getSelectedValue(); 126 perform(commandName); 127 } 128 } 129 } 130 } 131 }); 132 133 JScrollPane comp1 = new JScrollPane(examples); 134 comp1.setPreferredSize(new Dimension(400, 200)); 135 136 getContentPane().add(comp1, BorderLayout.CENTER); 137 138 examples.setBackground(getBackground()); 139 examples.addMouseListener(new MouseAdapter() { 140 @Override 141 public void mouseClicked(MouseEvent e) { 142 String commandName = examples.getSelectedValue(); 143 if (e.getClickCount() == 2) { 144 perform(commandName); 145 } 146 } 147 }); 148 examples.addFocusListener(new FocusAdapter() { 149 @Override 150 public void focusLost(FocusEvent e) { 151 if (e.getOppositeComponent() != CommandHelperDialog.this && e.getOppositeComponent() != commands) { 152 setVisible(false); 153 } 154 } 155 }); 156 examples.addKeyListener(new KeyAdapter() { 157 @Override 158 public void keyPressed(KeyEvent e) { 159 int keyCode = e.getKeyCode(); 160 if (keyCode == KeyEvent.VK_ESCAPE) { 161 setVisible(false); 162 } else { 163 if (keyCode == KeyEvent.VK_ENTER) { 164 String commandName = examples.getSelectedValue(); 165 perform(commandName); 166 } else if (keyCode == KeyEvent.VK_LEFT) { 167 commands.requestFocus(); 168 } 169 } 170 } 171 }); 172 173 } 174 175 private void perform(String commandName) { 176 List<String> examples = exampleMap.get(commandName); 177 setVisible(false); 178 if (examples != null && !examples.isEmpty()) { 179 int selectedIndex = this.examples.getSelectedIndex(); 180 if (selectedIndex < 0) { 181 selectedIndex = 0; 182 } 183 184 String text = examples.get(selectedIndex); 185 if (!StringUtils.startsWith(text, "<") && StringUtils.contains(text, ":")) { 186 text = StringUtils.substringAfter(text, ":"); 187 } 188 insertTag(startSymbols, text); 189 } 190 if (examples == null) { 191 commandName = commandName.trim(); 192 if (!StringUtils.startsWith(commandName, "<") && StringUtils.contains(commandName, ":")) { 193 commandName = StringUtils.substringAfter(commandName, ":"); 194 } 195 insertTag(startSymbols, commandName); 196 } 197 } 198 199 private void fillExamples(String commandName) { 200 List<String> list = exampleMap.get(commandName); 201 if (list != null) { 202 examplesListModel.removeAllElements(); 203 for (String example : list) { 204 examplesListModel.addElement(example); 205 } 206 if (!list.isEmpty()) { 207 examples.setSelectedIndex(0); 208 } 209 } 210 } 211 212 public void fillCommandList(String text, Processor makeProcessor) { 213 this.startSymbols = text; 214 text = StringUtils.substring(text, 1); 215 commandListModel.removeAllElements(); 216 exampleMap.clear(); 217 218 List<CommandInfo> commandInfos = getCodeHelper().getCommandList(text, makeProcessor.getClass()); 219 commandInfos.sort(Comparator.comparing(CommandInfo::getName)); 220 List<CommandInfo> active = commandInfos.stream() 221 .filter((a) -> !a.getClassName().equals(BaseProcessor.class.getSimpleName())) 222 .collect(Collectors.toList()); 223 List<CommandInfo> base = commandInfos.stream() 224 .filter((a) -> a.getClassName().equals(BaseProcessor.class.getSimpleName())) 225 .collect(Collectors.toList()); 226 227 List<CommandInfo> sortedCommandInfos = new ArrayList<CommandInfo>(active); 228 sortedCommandInfos.addAll(base); 229 230 for (CommandInfo commandInfo : sortedCommandInfos) { 231 String name = commandInfo.getName(); 232 commandListModel.addElement(name); 233 exampleMap.put(name, commandInfo.getExamples()); 234 } 235 236 if (commandListModel.isEmpty() || commandListModel.getSize() > 1 237 || exampleMap.get(commandListModel.firstElement()).size() > 1) { 238 showDialog(); 239 240 SwingUtilities.invokeLater(() -> { 241 commands.requestFocusInWindow(); 242 commands.setSelectedIndex(0); 243 fillExamples(commands.getSelectedValue()); 244 }); 245 } else { 246 String firstElement = commandListModel.firstElement(); 247 perform(firstElement); 248 } 249 } 250 251 public void helpWith(String text, boolean isCommand) { 252 if (isCommand && (StringUtils.startsWith(text, "<") || StringUtils.isBlank(text))) { 253 Processor taskProcessor = getCurrentProcessor(); 254 fillCommandList(text, taskProcessor); 255 } else { 256 DialogPopupMenu menu = new DialogPopupMenu(getCodeHelper().getEditor()); 257 menu.setAutoscrolls(true); 258 if (!isCommand) { 259 showMacroPopupMenu(text, menu); 260 } else { 261 menu = getCodeHelper().getEditor().getRecipePanel().contextHelp(menu); 262 } 263 Point magicCaretPosition = getCodeHelper().getEditor().getRecipePanel().getMagicCaretPosition(); 264 if (magicCaretPosition == null) 265 magicCaretPosition = new Point(); 266 menu.show(getCodeHelper().getEditor(), magicCaretPosition.x, magicCaretPosition.y); 267 } 268 } 269 270 private Processor getCurrentProcessor() { 271 TaskEditor recipePanel = getCodeHelper().getRecipePanel(); 272 TextEditor editor = recipePanel.getEditor(); 273 String text = editor.getText(); 274 int curpos = editor.getCaretPosition(); 275 276 Processor makeProcessor = recipePanel.getProcessor(); 277 String substring = StringUtils.substring(text, 0, curpos); 278 int startExternTag = StringUtils.lastIndexOf(substring, "<Extern"); 279 int closeExternTag = StringUtils.lastIndexOf(substring, "</Extern"); 280 if (closeExternTag < 0 || closeExternTag < startExternTag) { 281 int endExternTag = StringUtils.indexOf(substring, '>', startExternTag); 282 String externTag = StringUtils.substring(substring, startExternTag, endExternTag); 283 if (StringUtils.isNotBlank(externTag)) { 284 externTag = externTag + "/>"; 285 try { 286 Node externNode = new EasyParser().getObject(externTag); 287 makeProcessor = makeProcessor.makeProcessor(externNode); 288 289 } catch (Exception e1) { 290 // do nothing. 291 } 292 } 293 } 294 return makeProcessor; 295 } 296 297 private void showMacroPopupMenu(final String theStartMacroIns, DialogPopupMenu menu) { 298 299 JMenuItem menuItem = new JMenuItem("$var{type:property,type:data}"); 300 if ("var{".startsWith(theStartMacroIns)) { 301 menu.add(menuItem); 302 menuItem.addActionListener(new ActionListener() { 303 public void actionPerformed(ActionEvent e) { 304 Node object; 305 try { 306 object = new EasyParser() 307 .getObject("<Data variable_name='type:property' default_value='type:data'/>"); 308 parsingCommandParamters(object); 309 String theStartMacroIns = getStartMacroIns(); 310 String text = "var{" + object.getAttribute("variable_name") + "," 311 + object.getAttribute("default_value") + "}"; 312 insertText(theStartMacroIns, text); 313 } catch (Exception e1) { 314 e1.printStackTrace(); 315 } 316 } 317 }); 318 } 319 320 menuItem = new JMenuItem("$tag{type:property,type:data}"); 321 if ("tag{".startsWith(theStartMacroIns)) { 322 menu.add(menuItem); 323 menuItem.addActionListener(new ActionListener() { 324 325 public void actionPerformed(ActionEvent e) { 326 Node object; 327 try { 328 object = new EasyParser() 329 .getObject("<Data variable_name='type:property' default_value='type:data'/>"); 330 parsingCommandParamters(object); 331 332 String theStartMacroIns = getStartMacroIns(); 333 334 insertText(theStartMacroIns, "tag{" + object.getAttribute("variable_name") + "," 335 + object.getAttribute("default_value") + "}"); 336 } catch (Exception e1) { 337 e1.printStackTrace(); 338 } 339 } 340 }); 341 } 342 343 if ("call{".startsWith(theStartMacroIns)) { 344 menuItem = new JMenuItem("$call{type:recipe,type:data}"); 345 menu.add(menuItem); 346 menuItem.addActionListener(new ActionListener() { 347 public void actionPerformed(ActionEvent e) { 348 Node object; 349 try { 350 object = new EasyParser() 351 .getObject("<Data task_name='type:recipe' return_variable='type:property'/>"); 352 parsingCommandParamters(object); 353 String return_variable = object.getAttribute("return_variable"); 354 insertText(getStartMacroIns(), "call{" + object.getAttribute("task_name") 355 + (return_variable == null ? "" : "," + return_variable) + "}"); 356 } catch (Exception e1) { 357 e1.printStackTrace(); 358 } 359 } 360 }); 361 } 362 363 if ("file{".startsWith(theStartMacroIns)) 364 365 { 366 menuItem = new JMenuItem("$file{type:path}"); 367 menu.add(menuItem); 368 menuItem.addActionListener(e -> { 369 Node object; 370 try { 371 object = new EasyParser().getObject("<Data file_name='type:path'/>"); 372 parsingCommandParamters(object); 373 insertText(getStartMacroIns(), "file{" + object.getAttribute("file_name") + "}"); 374 } catch (Exception e1) { 375 e1.printStackTrace(); 376 } 377 }); 378 } 379 } 380 381 private boolean parsingCommandParamters(Node node) { 382 boolean isHelpRequired = true; 383 Properties attributes = new Properties(); 384 Node example = node; 385 if (example.isEmpty()) { 386 example = new Node("example"); 387 example.add(node); 388 } 389 for (Node object : example) { 390 for (Object name : object.getAttributes().keySet()) { 391 String attribute = object.getAttribute((String) name); 392 String theName = ((String) name).replace('_', ' '); 393 if (attribute.startsWith("type:")) { 394 attribute = attribute.substring(5); 395 JRootPane rootPane = SwingUtilities.getRootPane(getCodeHelper().getEditor()); 396 if (attribute.equals("integer")) { 397 boolean valid = true; 398 do { 399 attribute = JOptionPane.showInputDialog(rootPane, 400 "Type: " + attribute + "\nAttribute: " + theName); 401 if (attribute == null) { 402 isHelpRequired = false; 403 break; 404 } 405 406 object.setAttribute((String) name, attribute); 407 408 } while (!valid); 409 410 } else if (attribute.equals("operation")) { 411 Map<String, OperationHolder> operationsMethods = AEWorkspace.getInstance() 412 .getOperationsMethods(); 413 Set<String> keySet = operationsMethods.keySet(); 414 showListDialog(object, name, attribute, keySet.toArray(new String[keySet.size()]), false, 415 "Operations:"); 416 String operationName = object.getAttribute("method"); 417 if (!StringUtils.isEmpty(operationName)) { 418 OperationHolder operationsMethod = AEWorkspace.getInstance() 419 .getOperationsMethod(operationName); 420 object.setAttribute("description", operationsMethod.getDescription()); 421 Processor processor = getCodeHelper().getEditor().getRecipePanel().getProcessor(); 422 String[] array = processor.getVariables().keySet().toArray(new String[keySet.size()]); 423 Class<?> returnType = operationsMethod.getMethod().getReturnType(); 424 if (returnType != void.class) { 425 object.setAttribute("name", "type:property"); 426 427 if (!showListDialog(object, "name", "property", array, true, "Return: " 428 + operationsMethod.getReturnDescription() + "\nSelect variable name:")) { 429 isHelpRequired = false; 430 break; 431 } 432 } 433 434 Class<?>[] parameterTypes = operationsMethod.getMethod().getParameterTypes(); 435 for (int i = 0; i < parameterTypes.length; i++) { 436 String type = parameterTypes[i].getName(); 437 438 object.setAttribute("arg" + (i + 1), type); 439 String value = (String) JOptionPane.showInputDialog(rootPane, 440 "Type: " + type + "\nAttribute: " + operationsMethod.getParameterName(i), 441 "Input parameter", JOptionPane.INFORMATION_MESSAGE, null, null, null); 442 if (attribute == null) { 443 isHelpRequired = false; 444 break; 445 } 446 447 object.setAttribute("arg" + (i + 1), value); 448 } 449 } 450 isHelpRequired = false; 451 break; 452 } else if (attribute.equals("attr")) { 453 attribute = attributes.getProperty(theName); 454 object.setAttribute((String) name, attribute); 455 } else if (StringUtils.containsAny(attribute, "string", "url", "regex")) { 456 String value = OptionPane.showInputDialog(rootPane, "Input command attribute", "Attribute: " + theName + ", Type: " + attribute, ""); 457 if (value == null) { 458 isHelpRequired = false; 459 break; 460 } 461 462 value = StringEscapeUtils.escapeXml(value); 463 object.setAttribute((String) name, value); 464 } else if (attribute.equals("xpath")) { 465 attribute = JOptionPane.showInputDialog(rootPane, "Attribute: " + theName + "\nType: xpath", 466 ""); 467 if (attribute != null) { 468 attribute = attribute.replace("\"", "\'"); 469 } 470 if (attribute == null) { 471 isHelpRequired = false; 472 break; 473 } 474 object.setAttribute((String) name, attribute); 475 } else if (attribute.equals("time")) { 476 String[] choiceArray = new String[] { "", "s", "m", "h", "d", "M", "Y" }; 477 if (!showListDialog(object, name, "time", choiceArray, false, null)) { 478 isHelpRequired = false; 479 break; 480 } 481 } else if (attribute.equals("recipe")) { 482 if (!showListDialog(object, name, attribute, 483 getCodeHelper().getEditor().getRecipePanel().getManager().getTestsList(), true)) { 484 isHelpRequired = false; 485 break; 486 } 487 } else if (attribute.equals("double")) { 488 attribute = JOptionPane.showInputDialog(rootPane, "Attribute: " + theName + "\nType: double", 489 ""); 490 if (attribute == null) { 491 isHelpRequired = false; 492 break; 493 } 494 object.setAttribute((String) name, attribute); 495 } else if (attribute.equals("property")) { 496 Processor processor = getCodeHelper().getEditor().getRecipePanel().getProcessor(); 497 if (processor == null) { 498 TaskEditor recipePanel = getCodeHelper().getEditor().getRecipePanel(); 499 processor = new BaseProcessor(recipePanel.getManager(), recipePanel.getLogger(), 500 getCodeHelper().getEditor().getRecipePanel().getManager().getStartDir()); 501 } 502 Set<String> keySet = processor.getVariables().keySet(); 503 if (!showListDialog(object, name, attribute, keySet.toArray(new String[keySet.size()]), true)) { 504 isHelpRequired = false; 505 break; 506 } 507 } else if (attribute.equals("path")) { 508 FileDialog theFileDialog = new FileDialog(JOptionPane.getRootFrame(), "Input path", 509 FileDialog.LOAD); 510 String absolutePath = getCodeHelper().getEditor().getRecipePanel().getProcessor() 511 .getBaseDir().getAbsolutePath(); 512 theFileDialog.setDirectory(absolutePath); 513 theFileDialog.setVisible(true); 514 if (theFileDialog.getFile() != null) { 515 String prefDir = theFileDialog.getDirectory(); 516 if (theFileDialog.getDirectory().startsWith(absolutePath)) { 517 prefDir = prefDir.substring(absolutePath.length() + 1, prefDir.length()); 518 } 519 object.setAttribute((String) name, prefDir + theFileDialog.getFile()); 520 } 521 } else if (attribute.startsWith("boolean")) { 522 String type = "boolean"; 523 String[] choiceArray = new String[] { "true", "false" }; 524 if (!showListDialog(object, name, type, choiceArray, false)) { 525 isHelpRequired = false; 526 break; 527 } 528 } else if (attribute.startsWith("processor")) { 529 String[] processors = ClassUtils.findAssignable(Processor.class, Processor.class.getPackage()); 530 531 if (!showListDialog(object, name, attribute, processors, false)) { 532 isHelpRequired = false; 533 break; 534 } 535 } else if (attribute.startsWith("view")) { 536 String[] processors = ClassUtils.findAssignable(View.class, View.class.getPackage()); 537 538 if (!showListDialog(object, name, attribute, processors, false)) { 539 isHelpRequired = false; 540 break; 541 } 542 } 543 544 } else if (attribute.startsWith("enum:")) { 545 String type = attribute.substring(0, 4); 546 String choice = attribute.substring(5); 547 StringTokenizer stringTokenizer = new StringTokenizer(choice, "|"); 548 String[] choiceArray = new String[stringTokenizer.countTokens()]; 549 for (int i = 0; i < choiceArray.length; i++) { 550 choiceArray[i] = stringTokenizer.nextToken(); 551 } 552 if (!showListDialog(object, name, type, choiceArray, false)) { 553 isHelpRequired = false; 554 break; 555 } 556 } 557 } 558 attributes.putAll(object.getAttributes()); 559 } 560 return isHelpRequired; 561 } 562 563 private boolean showListDialog(Node object, Object name, String type, String[] sourceList, boolean b) { 564 Arrays.sort(sourceList); 565 return showListDialog(object, name, type, sourceList, b, null); 566 } 567 568 private boolean showListDialog(Node object, Object name, String type, String[] sourceList, boolean b, 569 String description) { 570 571 String prefix = "Attribute: "; 572 573 String message = StringUtils.defaultString(description, prefix + name + ", Type: " + type); 574 JRootPane rootPane = SwingUtilities.getRootPane(getCodeHelper().getEditor().getRecipePanel().getEditor()); 575 String value = OptionPane.showInputDialog(rootPane, message, "Input command attribute", sourceList, ""); 576 577 if (value == null) { 578 return false; 579 } 580 581 object.setAttribute((String) name, value); 582 return true; 583 } 584 585 void insertTag(final String startSymbols, String text) { 586 try { 587 Node object = new EasyParser().getObject("<example>" + text + "</example>"); 588 parsingCommandParamters(object); 589 StringBuilder code = new StringBuilder(); 590 for (Node node : object) { 591 code.append(node.getXMLText()); 592 } 593 insertText(startSymbols, code.toString().trim()); 594 } catch (TaskCancelingException e) { 595 } catch (Exception e) { 596 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), e.getMessage()); 597 } 598 } 599 600 private String getStartMacroIns() { 601 int curpos = getCodeHelper().getEditor().getCaretPosition(); 602 String text = getCodeHelper().getEditor().getText(); 603 String theStartMacroIns = null; 604 605 int i; 606 for (i = curpos - 1; i >= 0; i--) { 607 char charAt = text.charAt(i); 608 609 if (charAt == '$') { 610 theStartMacroIns = text.substring(i + 1, curpos); 611 break; 612 } 613 } 614 615 return (i > curpos - 7) ? theStartMacroIns : ""; 616 } 617 618 private void insertText(String theStartCommand, String text) { 619 int caretPosition2 = getCodeHelper().getEditor().getCaretPosition(); 620 int i = caretPosition2 - theStartCommand.length(); 621 try { 622 String text2 = getCodeHelper().getEditor().getRecipePanel().getEditor().getText(i - 1, 1); 623 if ("<".equals(text2)) { 624 i--; 625 } 626 } catch (BadLocationException e) { 627 e.printStackTrace(); 628 } 629 getCodeHelper().getEditor().replaceRange(text, i, caretPosition2); 630 } 631 632}