KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > menu > MenuRefactor


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 JavaDoc;
18 import java.awt.*;
19 import java.util.List JavaDoc;
20 /*
21
22 [The "BSD licence"]
23 Copyright (c) 2005 Jean Bovet
24 All rights reserved.
25
26 Redistribution and use in source and binary forms, with or without
27 modification, are permitted provided that the following conditions
28 are met:
29
30 1. Redistributions of source code must retain the above copyright
31 notice, this list of conditions and the following disclaimer.
32 2. Redistributions in binary form must reproduce the above copyright
33 notice, this list of conditions and the following disclaimer in the
34 documentation and/or other materials provided with the distribution.
35 3. The name of the author may not be used to endorse or promote products
36 derived from this software without specific prior written permission.
37
38 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
39 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
40 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
41 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
42 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
43 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
47 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
49 */

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 JavaDoc s = (String JavaDoc) 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 JavaDoc s = (String JavaDoc)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 JavaDoc name) {
105         // First insert the rule at the end of the grammar
106
mutator.insert(editor.getText().length(), "\n\n"+name+"\n\t:\t"+t.getAttribute()+"\n\t;");
107
108         // Then rename all strings token
109
List JavaDoc<ATEToken> tokens = editor.getTokens();
110         String JavaDoc 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 JavaDoc<ATEToken> tokens = editor.getTokens();
145         for(int index = tokens.size()-1; index>0; index--) {
146             ATEToken token = tokens.get(index);
147
148             String JavaDoc attribute;
149             String JavaDoc stripped;
150             String JavaDoc 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                 // Only one character allowed
160
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                 // String with one character should be converted to single-quote
171

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 JavaDoc<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             // FIX AW-56
195
if(RefactorEngine.ignoreScopeForDoubleQuoteLiteral(token.scope))
196                 continue;
197
198             String JavaDoc attribute = token.getAttribute();
199             String JavaDoc 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 JavaDoc escapeStringQuote(String JavaDoc s, char escapeQuote, char unescapeQuote) {
208         // Escape the quote found in s.
209
// Example:
210
// "hello'world" -> 'hello\'world'
211
// "hello\'world" -> 'hello\'world'
212
// "hello\"world" -> 'hello"world'
213

214         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc<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 JavaDoc 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 JavaDoc ruleName = (String JavaDoc)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 JavaDoc 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 JavaDoc oldContent = editor.getText();
326
327         beginRefactor("Inline");
328
329         String JavaDoc ruleName = rule.name;
330         String JavaDoc ruleContent = Utils.trimString(oldContent.substring(rule.colon.getEndIndex(), rule.end.getStartIndex()));
331
332         List JavaDoc<ElementRule> rules = editor.rules.getRules();
333         if(rule.end.index - rule.colon.index > 2) {
334             // More than one token, append ()
335
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 JavaDoc<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 JavaDoc name, String JavaDoc 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         // Add the rule in the next line by default
375
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                     // Add new rule just after this one
383
insertionIndex = rule.getEndIndex();
384                 } else {
385                     // Add new rule after the last parser rule
386
ElementRule last = editor.rules.getLastParserRule();
387                     if(last != null) insertionIndex = last.getEndIndex();
388                 }
389             } else {
390                 if(lexer) {
391                     // Add new rule after the last lexer rule
392
ElementRule last = editor.rules.getLastLexerRule();
393                     if(last != null) {
394                         insertionIndex = last.getEndIndex();
395                     } else {
396                         // Add new rule after the last rule
397
last = editor.rules.getLastRule();
398                         if(last != null) insertionIndex = last.getEndIndex();
399                     }
400                 } else {
401                     // Add new rule just after this one
402
insertionIndex = rule.getEndIndex();
403                 }
404             }
405         }
406         return insertionIndex;
407     }
408
409     public String JavaDoc createRule(String JavaDoc name, String JavaDoc content) {
410         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
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 JavaDoc rule, int index) {
428         mutator.insertAtLinesBoundary(index, rule);
429     }
430
431     protected void beginRefactor(String JavaDoc 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 JavaDoc 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 JavaDoc mutableText;
455
456         public EditorTextMutator() {
457             mutableText = new StringBuffer JavaDoc(editor.getText());
458         }
459
460         public void replace(int start, int end, String JavaDoc s) {
461             mutableText.replace(start, end, s);
462         }
463
464         public void insert(int index, String JavaDoc s) {
465             mutableText.insert(index, s);
466         }
467
468         public void insertAtLinesBoundary(int index, String JavaDoc 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 JavaDoc text = mutableText.toString();
486             String JavaDoc 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 JavaDoc {
497
498         public String JavaDoc oldContent;
499         public String JavaDoc newContent;
500
501         public UndoableRefactoringEdit(String JavaDoc oldContent, String JavaDoc 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