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