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