001package com.ganteater.ae.desktop.editor; 002 003import java.awt.event.KeyAdapter; 004import java.awt.event.KeyEvent; 005import java.lang.reflect.Method; 006import java.util.ArrayList; 007import java.util.List; 008import java.util.Stack; 009 010import org.apache.commons.lang.ClassUtils; 011import org.apache.commons.lang.StringUtils; 012 013import com.ganteater.ae.processor.BaseProcessor; 014import com.ganteater.ae.processor.CommandInfo; 015import com.ganteater.ae.processor.Processor; 016import com.ganteater.ae.processor.annotation.CommandDescription; 017import com.ganteater.ae.processor.annotation.CommandExamples; 018import com.ganteater.ae.util.xml.easyparser.EasyParser; 019import com.ganteater.ae.util.xml.easyparser.Node; 020 021public class CodeHelper extends KeyAdapter { 022 023 static final String RUN_COMMAND_METHODE_PREFIX = "runCommand"; 024 025 private TextEditor editor; 026 private CommandHelperDialog dialog; 027 028 private HelperDialog defaultDialog; 029 030 public CodeHelper(TextEditor recipeEditor) { 031 this.editor = recipeEditor; 032 dialog = createPopup(); 033 } 034 035 protected CommandHelperDialog createPopup() { 036 return new CommandHelperDialog(this); 037 } 038 039 @Override 040 public void keyPressed(KeyEvent e) { 041 int keyCode = e.getKeyCode(); 042 if (keyCode == KeyEvent.VK_D && e.isControlDown()) { 043 editor.removeLine(); 044 } else if (keyCode == KeyEvent.VK_F9) { 045 editor.getRecipePanel().runTask(); 046 } else if (keyCode == KeyEvent.VK_SPACE && e.isControlDown()) { 047 showCommandsMenu(); 048 } 049 } 050 051 @Override 052 public void keyTyped(KeyEvent e) { 053 if (e.getKeyChar() == '<') { 054 // showCommandsMenu(); 055 } 056 } 057 058 public void showCommandsMenu() { 059 String selectedText = editor.getSelectedText(); 060 061 String text = editor.getText(); 062 int curpos = editor.getCaretPosition(); 063 064 Processor makeProcessor = editor.getRecipePanel().getTaskProcessor(); 065 String substring = StringUtils.substring(text, 0, curpos); 066 int startExternTag = StringUtils.lastIndexOf(substring, "<Extern"); 067 int closeExternTag = StringUtils.lastIndexOf(substring, "</Extern"); 068 if (closeExternTag < 0 || closeExternTag < startExternTag) { 069 int endExternTag = StringUtils.indexOf(substring, '>', startExternTag); 070 String externTag = StringUtils.substring(substring, startExternTag, endExternTag); 071 if (StringUtils.isNotBlank(externTag)) { 072 externTag = externTag + "/>"; 073 try { 074 Node externNode = new EasyParser().getObject(externTag); 075 makeProcessor = makeProcessor.makeProcessor(externNode); 076 077 } catch (Exception e1) { 078 // do nothing. 079 } 080 } 081 } 082 083 boolean isCommand = true; 084 String startText = null; 085 086 if (selectedText == null) { 087 for (int i = curpos - 1; i >= 0 && startText == null; i--) { 088 char charAt = text.charAt(i); 089 switch (charAt) { 090 case '>': 091 startText = text.substring(i + 1, curpos).trim(); 092 break; 093 094 case '<': 095 startText = text.substring(i, curpos); 096 break; 097 098 case '$': 099 isCommand = false; 100 startText = text.substring(i + 1, curpos); 101 break; 102 103 case '/': 104 charAt = text.charAt(i - 1); 105 if (charAt == '<') { 106 String lastUnclosedTag = findLastUnclosedTag(substring); 107 editor.insert(lastUnclosedTag + ">", curpos); 108 return; 109 } 110 break; 111 112 default: 113 startText = null; 114 } 115 } 116 } 117 118 if (defaultDialog != null && (StringUtils.isBlank(startText) || StringUtils.startsWith(startText, "<!") 119 || !StringUtils.startsWith(startText, "<")) || StringUtils.contains(startText, " ")) { 120 defaultDialog.showDialog(); 121 } else { 122 dialog.helpWith(startText, isCommand); 123 } 124 } 125 126 public List<CommandInfo> getCommandList(String text, Class<?> processorClass) { 127 List<CommandInfo> list = new ArrayList<>(); 128 129 Method[] methods = processorClass.getMethods(); 130 for (Method method : methods) { 131 if (StringUtils.startsWith(method.getName(), CodeHelper.RUN_COMMAND_METHODE_PREFIX) 132 || (text == null && "init".equals(method.getName()))) { 133 @SuppressWarnings("unchecked") 134 Class<? extends Processor> declaringClass = (Class<? extends Processor>) method.getDeclaringClass(); 135 String name = StringUtils.substringAfter(method.getName(), CodeHelper.RUN_COMMAND_METHODE_PREFIX); 136 137 if (((text == null && declaringClass != BaseProcessor.class) || StringUtils.startsWith(name, text)) 138 && ClassUtils.isAssignable(declaringClass, Processor.class)) { 139 140 CommandDescription annotation = method.getAnnotation(CommandDescription.class); 141 String description = null; 142 if (annotation != null) { 143 description = annotation.value(); 144 } 145 146 String commandName; 147 148 if (StringUtils.isBlank(name)) { 149 name = method.getName(); 150 commandName = name; 151 } else { 152 commandName = CodeHelper.RUN_COMMAND_METHODE_PREFIX + name; 153 } 154 155 if (annotation != null) { 156 description = annotation.value(); 157 } 158 159 CommandInfo info = new CommandInfo(name, declaringClass, description); 160 161 List<String> exampleList = getExampleList(processorClass, commandName); 162 163 info.setExamples(exampleList); 164 list.add(info); 165 } 166 } 167 } 168 return list; 169 170 } 171 172 public static String findLastUnclosedTag(String xmlText) { 173 Stack<String> tagStack = new Stack<>(); 174 int index = 0; 175 176 while (index < xmlText.length()) { 177 int openTagStart = xmlText.indexOf("<", index); 178 179 // No more tags found 180 if (openTagStart == -1) { 181 break; 182 } 183 184 int openTagEnd = xmlText.indexOf(">", openTagStart); 185 if (openTagEnd == -1) { 186 break; // Malformed tag, end of string 187 } 188 189 String tagContent = xmlText.substring(openTagStart + 1, openTagEnd).trim(); 190 191 // Check for closing tag 192 if (tagContent.startsWith("/")) { 193 String closingTag = tagContent.substring(1); 194 195 if (!tagStack.isEmpty() && tagStack.peek().equals(closingTag)) { 196 tagStack.pop(); // Properly closed tag 197 } else { 198 // Closing tag without a matching opening tag 199 return closingTag; 200 } 201 } else if (!tagContent.endsWith("/")) { 202 // Opening tag (not self-closing) 203 int spaceIndex = tagContent.indexOf(" "); 204 String tagName = (spaceIndex == -1) ? tagContent : tagContent.substring(0, spaceIndex); 205 tagStack.push(tagName); 206 } 207 208 index = openTagEnd + 1; 209 } 210 211 // Return the last unclosed tag, if any 212 return tagStack.isEmpty() ? null : tagStack.peek(); 213 } 214 215 public TaskEditor getRecipePanel() { 216 return editor.getRecipePanel(); 217 } 218 219 public TextEditor getEditor() { 220 return editor; 221 } 222 223 public void hide() { 224 dialog.setVisible(false); 225 } 226 227 public void setDefaultDialog(HelperDialog defaultDialog) { 228 this.defaultDialog = defaultDialog; 229 } 230 231 public List<String> getExampleList(Class<?> class1, String methodName) { 232 String[] examples = null; 233 List<String> exampleList = new ArrayList<String>(); 234 try { 235 Method method = null; 236 try { 237 CommandExamples annotation = null; 238 if ("init".equals(methodName)) { 239 method = class1.getMethod(methodName, new Class[] { Processor.class, Node.class }); 240 annotation = method.getAnnotation(CommandExamples.class); 241 if (annotation == null) { 242 method = class1.getMethod(methodName, new Class[] {}); 243 annotation = method.getAnnotation(CommandExamples.class); 244 } 245 246 } else { 247 method = class1.getMethod(methodName, new Class[] { Node.class }); 248 annotation = method.getAnnotation(CommandExamples.class); 249 } 250 251 if (annotation != null) { 252 examples = annotation.value(); 253 } 254 255 } catch (NoSuchMethodException e) { 256 } 257 258 if (examples != null) { 259 for (String example : examples) { 260 exampleList.add(example); 261 } 262 } 263 264 } catch (Exception e1) { 265 // log.error("Popup menu creation failed.", e1); 266 } 267 268 return exampleList; 269 } 270 271}