1 package org.antlr.works.menu; 2 3 import org.antlr.xjlib.appkit.undo.XJUndo; 4 import org.antlr.xjlib.appkit.utils.XJAlert; 5 import org.antlr.works.ate.syntax.generic.ATESyntaxLexer; 6 import org.antlr.works.ate.syntax.misc.ATEToken; 7 import org.antlr.works.components.grammar.CEditorGrammar; 8 import org.antlr.works.grammar.RefactorEngine; 9 import org.antlr.works.grammar.RefactorMutator; 10 import org.antlr.works.prefs.AWPrefs; 11 import org.antlr.works.stats.StatisticsAW; 12 import org.antlr.works.syntax.element.ElementReference; 13 import org.antlr.works.syntax.element.ElementRule; 14 import org.antlr.works.utils.Utils; 15 16 import javax.swing.*; 17 import javax.swing.undo.AbstractUndoableEdit ; 18 import java.awt.*; 19 import java.util.List ; 20 50 51 public class MenuRefactor extends MenuAbstract { 52 53 private RefactorEngine engine; 54 private EditorTextMutator mutator; 55 56 public MenuRefactor(CEditorGrammar editor) { 57 super(editor); 58 engine = new RefactorEngine(); 59 } 60 61 public void rename() { 62 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_RENAME); 63 64 ATEToken token = editor.getCurrentToken(); 65 if(token == null) 66 return; 67 68 String s = (String ) JOptionPane.showInputDialog(editor.getJavaContainer(), "Rename '"+token.getAttribute()+"' and its usages to:", "Rename", 69 JOptionPane.QUESTION_MESSAGE, null, null, token.getAttribute()); 70 if(s != null && !s.equals(token.getAttribute())) { 71 beginRefactor("Rename"); 72 engine.renameToken(token, s); 73 endRefactor(); 74 } 75 } 76 77 public boolean canReplaceLiteralWithTokenLabel() { 78 ATEToken token = editor.getCurrentToken(); 79 return token != null && (token.type == ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING || token.type == ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING); 80 } 81 82 public void replaceLiteralWithTokenLabel() { 83 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_REPLACE_LITERALS); 84 85 ATEToken token = editor.getCurrentToken(); 86 if(token == null) 87 return; 88 89 if(token.type != ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING && token.type != ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING) { 90 XJAlert.display(editor.getJavaContainer(), "Cannot Replace Literal With Token Label", "The current token is not a literal."); 91 return; 92 } 93 94 editor.selectTextRange(token.getStartIndex(), token.getEndIndex()); 95 String s = (String )JOptionPane.showInputDialog(editor.getJavaContainer(), "Replace Literal '"+token.getAttribute()+"' with token label:", "Replace Literal With Token Label", 96 JOptionPane.QUESTION_MESSAGE, null, null, ""); 97 if(s != null && !s.equals(token.getAttribute())) { 98 beginRefactor("Replace Literal With Token Label"); 99 replaceLiteralTokenWithTokenLabel(token, s); 100 endRefactor(); 101 } 102 } 103 104 public void replaceLiteralTokenWithTokenLabel(ATEToken t, String name) { 105 mutator.insert(editor.getText().length(), "\n\n"+name+"\n\t:\t"+t.getAttribute()+"\n\t;"); 107 108 List <ATEToken> tokens = editor.getTokens(); 110 String attr = t.getAttribute(); 111 for(int index = tokens.size()-1; index>0; index--) { 112 ATEToken token = tokens.get(index); 113 if(token.type != ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING && token.type != ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING) 114 continue; 115 116 if(!token.getAttribute().equals(attr)) 117 continue; 118 119 mutator.replace(token.getStartIndex(), token.getEndIndex(), name); 120 } 121 } 122 123 public void convertLiteralsToSingleQuote() { 124 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_CONVERT_LITERALS_TO_SINGLE); 125 126 beginRefactor("Convert Literals To Single Quote Literals"); 127 convertLiteralsToSpecifiedQuote(ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING, '\'', '"'); 128 endRefactor(); 129 } 130 131 public void convertLiteralsToDoubleQuote() { 132 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_CONVERT_LITERALS_TO_DOUBLE); 133 134 beginRefactor("Convert Literals To Double Quote Literals"); 135 convertLiteralsToSpecifiedQuote(ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING, '"', '\''); 136 endRefactor(); 137 } 138 139 public void convertLiteralsToCStyleQuote() { 140 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_CONVERT_LITERALS_TO_CSTYLE); 141 142 beginRefactor("Convert Literals To C-style Quote Literals"); 143 144 List <ATEToken> tokens = editor.getTokens(); 145 for(int index = tokens.size()-1; index>0; index--) { 146 ATEToken token = tokens.get(index); 147 148 String attribute; 149 String stripped; 150 String replaced = null; 151 152 if(token.type == ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING || token.type == ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING) { 153 attribute = token.getAttribute(); 154 stripped = attribute.substring(1, attribute.length()-1); 155 } else 156 continue; 157 158 if(token.type == ATESyntaxLexer.TOKEN_SINGLE_QUOTE_STRING) { 159 if(stripped.length() == 1) 161 continue; 162 else if(stripped.length() == 2 && stripped.charAt(0) == '\\') 163 continue; 164 165 if(stripped.indexOf('"') != -1 || stripped.indexOf('\'') != -1) 166 stripped = escapeStringQuote(stripped, '"', '\''); 167 168 replaced = '"'+stripped+'"'; 169 } else if(token.type == ATESyntaxLexer.TOKEN_DOUBLE_QUOTE_STRING) { 170 172 if(stripped.length() > 1 && stripped.charAt(0) != '\\') 173 continue; 174 175 if(stripped.indexOf('\'') != -1 || stripped.indexOf('"') != -1) 176 stripped = escapeStringQuote(stripped, '\'', '"'); 177 178 replaced = '\''+stripped+'\''; 179 } 180 181 mutator.replace(token.getStartIndex(), token.getEndIndex(), replaced); 182 } 183 184 endRefactor(); 185 } 186 187 protected void convertLiteralsToSpecifiedQuote(int tokenType, char quote, char unescapeQuote) { 188 List <ATEToken> tokens = editor.getTokens(); 189 for(int index = tokens.size()-1; index>0; index--) { 190 ATEToken token = tokens.get(index); 191 if(token.type != tokenType) 192 continue; 193 194 if(RefactorEngine.ignoreScopeForDoubleQuoteLiteral(token.scope)) 196 continue; 197 198 String attribute = token.getAttribute(); 199 String stripped = attribute.substring(1, attribute.length()-1); 200 if(stripped.indexOf(quote) != -1 || stripped.indexOf(unescapeQuote) != -1) 201 stripped = escapeStringQuote(stripped, quote, unescapeQuote); 202 203 mutator.replace(token.getStartIndex(), token.getEndIndex(), quote+stripped+quote); 204 } 205 } 206 207 protected String escapeStringQuote(String s, char escapeQuote, char unescapeQuote) { 208 214 StringBuffer sb = new StringBuffer (); 215 for(int i=0; i<s.length(); i++) { 216 char c = s.charAt(i); 217 if(c == '\\') { 218 i++; 219 char c1 = s.charAt(i); 220 if(c1 == unescapeQuote) 221 sb.append(c1); 222 else { 223 sb.append('\\'); 224 sb.append(c1); 225 } 226 } else if(c == escapeQuote) { 227 sb.append('\\'); 228 sb.append(escapeQuote); 229 } else 230 sb.append(c); 231 } 232 233 return sb.toString(); 234 } 235 236 public void removeLeftRecursion() { 237 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_REMOVE_LEFT_RECURSION); 238 239 ElementRule rule = editor.rules.getEnclosingRuleAtPosition(editor.getCaretPosition()); 240 if(rule == null) { 241 XJAlert.display(editor.getWindowContainer(), "Remove Left Recursion", "There is no rule at cursor position."); 242 return; 243 } 244 245 if(!rule.hasLeftRecursion()) { 246 XJAlert.display(editor.getWindowContainer(), "Remove Left Recursion", "The rule doesn't have a left recursion."); 247 return; 248 } 249 250 beginRefactor("Remove Left Recursion"); 251 String ruleText = rule.getTextRuleAfterRemovingLeftRecursion(); 252 mutator.replace(rule.getInternalTokensStartIndex(), rule.getInternalTokensEndIndex(), ruleText); 253 endRefactor(); 254 } 255 256 public void removeAllLeftRecursion() { 257 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_REMOVE_ALL_LEFT_RECURSION); 258 259 beginRefactor("Remove All Left Recursion"); 260 List <ElementRule> rules = editor.rules.getRules(); 261 for(int index = rules.size()-1; index >= 0; index--) { 262 ElementRule rule = rules.get(index); 263 if(rule.hasLeftRecursion()) { 264 String ruleText = rule.getTextRuleAfterRemovingLeftRecursion(); 265 mutator.replace(rule.getInternalTokensStartIndex(), rule.getInternalTokensEndIndex(), ruleText); 266 } 267 } 268 endRefactor(); 269 } 270 271 public boolean canExtractRule() { 272 int leftIndex = editor.getSelectionLeftIndexOnTokenBoundary(); 273 int rightIndex = editor.getSelectionRightIndexOnTokenBoundary(); 274 return leftIndex != -1 && rightIndex != -1; 275 } 276 277 public void extractRule() { 278 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_EXTRACT_RULE); 279 280 if(!canExtractRule()) { 281 XJAlert.display(editor.getWindowContainer(), "Extract Rule", "At least one token must be selected."); 282 return; 283 } 284 285 int leftIndex = editor.getSelectionLeftIndexOnTokenBoundary(); 286 int rightIndex = editor.getSelectionRightIndexOnTokenBoundary(); 287 288 editor.selectTextRange(leftIndex, rightIndex); 289 290 String ruleName = (String )JOptionPane.showInputDialog(editor.getJavaContainer(), "Rule name:", "Extract Rule", 291 JOptionPane.QUESTION_MESSAGE, null, null, ""); 292 if(ruleName != null && ruleName.length() > 0) { 293 beginRefactor("Extract Rule"); 294 boolean lexer = ATEToken.isLexerName(ruleName); 295 int index = insertionIndexForRule(lexer); 296 String ruleContent = editor.getText().substring(leftIndex, rightIndex); 297 if(index > editor.getCaretPosition()) { 298 insertRuleAtIndex(createRule(ruleName, ruleContent), index); 299 mutator.replace(leftIndex, rightIndex, ruleName); 300 } else { 301 mutator.replace(leftIndex, rightIndex, ruleName); 302 insertRuleAtIndex(createRule(ruleName, ruleContent), index); 303 } 304 endRefactor(); 305 } 306 } 307 308 public boolean canInlineRule() { 309 return editor.rules.getEnclosingRuleAtPosition(editor.getCaretPosition()) != null; 310 } 311 312 public void inlineRule() { 313 StatisticsAW.shared().recordEvent(StatisticsAW.EVENT_INLINE_RULE); 314 315 ElementRule rule = editor.rules.getEnclosingRuleAtPosition(editor.getCaretPosition()); 316 if(rule == null) { 317 XJAlert.display(editor.getWindowContainer(), "Inline Rule", "There is no rule at cursor position."); 318 return; 319 } 320 321 inlineRule(rule); 322 } 323 324 protected void inlineRule(ElementRule rule) { 325 String oldContent = editor.getText(); 326 327 beginRefactor("Inline"); 328 329 String ruleName = rule.name; 330 String ruleContent = Utils.trimString(oldContent.substring(rule.colon.getEndIndex(), rule.end.getStartIndex())); 331 332 List <ElementRule> rules = editor.rules.getRules(); 333 if(rule.end.index - rule.colon.index > 2) { 334 ruleContent = "("+ruleContent+")"; 336 } 337 338 for(int r=rules.size()-1; r>=0; r--) { 339 ElementRule candidate = rules.get(r); 340 if(candidate == rule) { 341 mutator.delete(rule.getStartIndex(), rule.getEndIndex()+1); 342 } else { 343 List <ElementReference> references = candidate.getReferences(); 344 if(references == null) 345 continue; 346 347 for(int index=references.size()-1; index>=0; index--) { 348 ElementReference ref = references.get(index); 349 if(ref.token.getAttribute().equals(ruleName)) { 350 mutator.replace(ref.token.getStartIndex(), ref.token.getEndIndex(), ruleContent); 351 } 352 } 353 } 354 } 355 356 endRefactor(); 357 } 358 359 public void createRuleAtIndex(boolean lexer, String name, String content) { 360 beginRefactor("Create Rule"); 361 int index = insertionIndexForRule(lexer); 362 insertRuleAtIndex(createRule(name, content), index); 363 setCaretPosition(index); 364 endRefactor(); 365 } 366 367 public void deleteRuleAtIndex(int index) { 368 ElementRule r = editor.rules.getEnclosingRuleAtPosition(index); 369 if(r != null) 370 editor.replaceText(r.getStartIndex(), r.getEndIndex(), ""); 371 } 372 373 public int insertionIndexForRule(boolean lexer) { 374 Point p = editor.getTextEditor().getLineTextPositionsAtTextPosition(getCaretPosition()); 376 int insertionIndex = p.y; 377 378 ElementRule rule = editor.rules.getEnclosingRuleAtPosition(getCaretPosition()); 379 if(rule != null) { 380 if(rule.lexer) { 381 if(lexer) { 382 insertionIndex = rule.getEndIndex(); 384 } else { 385 ElementRule last = editor.rules.getLastParserRule(); 387 if(last != null) insertionIndex = last.getEndIndex(); 388 } 389 } else { 390 if(lexer) { 391 ElementRule last = editor.rules.getLastLexerRule(); 393 if(last != null) { 394 insertionIndex = last.getEndIndex(); 395 } else { 396 last = editor.rules.getLastRule(); 398 if(last != null) insertionIndex = last.getEndIndex(); 399 } 400 } else { 401 insertionIndex = rule.getEndIndex(); 403 } 404 } 405 } 406 return insertionIndex; 407 } 408 409 public String createRule(String name, String content) { 410 StringBuffer sb = new StringBuffer (); 411 412 sb.append("\n"); 413 sb.append(name); 414 415 if(name.length() >= AWPrefs.getEditorTabSize()) 416 sb.append("\n"); 417 418 sb.append("\t:"); 419 if(content != null && content.length() > 0) { 420 sb.append("\t"); 421 sb.append(content); 422 } 423 sb.append("\n\t;"); 424 return sb.toString(); 425 } 426 427 protected void insertRuleAtIndex(String rule, int index) { 428 mutator.insertAtLinesBoundary(index, rule); 429 } 430 431 protected void beginRefactor(String name) { 432 editor.beginGroupChange(name); 433 mutator = new EditorTextMutator(); 434 engine.setMutator(mutator); 435 engine.setTokens(editor.getTokens()); 436 } 437 438 protected void endRefactor() { 439 mutator.apply(); 440 mutator = null; 441 editor.endGroupChange(); 442 } 443 444 protected void refactorReplaceEditorText(String text) { 445 int oldCaretPosition = editor.getCaretPosition(); 446 editor.disableTextPaneUndo(); 447 editor.setText(text); 448 editor.enableTextPaneUndo(); 449 editor.getTextEditor().setCaretPosition(Math.min(oldCaretPosition, text.length()), false, false); 450 } 451 452 public class EditorTextMutator implements RefactorMutator { 453 454 public StringBuffer mutableText; 455 456 public EditorTextMutator() { 457 mutableText = new StringBuffer (editor.getText()); 458 } 459 460 public void replace(int start, int end, String s) { 461 mutableText.replace(start, end, s); 462 } 463 464 public void insert(int index, String s) { 465 mutableText.insert(index, s); 466 } 467 468 public void insertAtLinesBoundary(int index, String s) { 469 if(!(mutableText.charAt(index) == '\n' && mutableText.charAt(index-1) == '\n')) { 470 mutableText.insert(index++, '\n'); 471 } 472 mutableText.insert(index, s); 473 int end = index+s.length(); 474 if(!(mutableText.charAt(end) == '\n' && mutableText.charAt(end+1) == '\n')) 475 { 476 mutableText.insert(end, '\n'); 477 } 478 } 479 480 public void delete(int start, int end) { 481 mutableText.delete(start, end); 482 } 483 484 public void apply() { 485 String text = mutableText.toString(); 486 String oldContent = editor.getText(); 487 488 refactorReplaceEditorText(text); 489 490 XJUndo undo = editor.getXJFrame().getUndo(getTextPane()); 491 undo.addEditEvent(new UndoableRefactoringEdit(oldContent, text)); 492 } 493 494 } 495 496 protected class UndoableRefactoringEdit extends AbstractUndoableEdit { 497 498 public String oldContent; 499 public String newContent; 500 501 public UndoableRefactoringEdit(String oldContent, String newContent) { 502 this.oldContent = oldContent; 503 this.newContent = newContent; 504 } 505 506 public void redo() { 507 super.redo(); 508 refactorReplaceEditorText(newContent); 509 } 510 511 public void undo() { 512 super.undo(); 513 refactorReplaceEditorText(oldContent); 514 } 515 } 516 517 } 518 | Popular Tags |