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}