KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > JavaMode


1 /*
2  * JavaMode.java
3  *
4  * Copyright (C) 1998-2004 Peter Graves
5  * $Id: JavaMode.java,v 1.16 2004/09/08 00:48:28 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import gnu.regexp.RE;
25 import gnu.regexp.REMatch;
26 import gnu.regexp.UncheckedRE;
27 import java.awt.event.KeyEvent JavaDoc;
28 import java.awt.event.MouseEvent JavaDoc;
29 import javax.swing.JMenuItem JavaDoc;
30 import javax.swing.JPopupMenu JavaDoc;
31 import javax.swing.undo.CompoundEdit JavaDoc;
32
33 public class JavaMode extends AbstractMode implements Constants, Mode
34 {
35     private static final String JavaDoc[] javaConditionals = {
36         "if",
37         "else",
38         "do",
39         "while",
40         "for",
41         "switch",
42         "try",
43         "catch",
44         "finally",
45         "synchronized"
46     };
47
48     private static Mode mode;
49     private static Object JavaDoc jdb;
50
51     protected String JavaDoc[] conditionals;
52
53     private JavaMode()
54     {
55         super(JAVA_MODE, JAVA_MODE_NAME);
56         keywords = new Keywords(this);
57         conditionals = javaConditionals;
58     }
59
60     protected JavaMode(int id, String JavaDoc displayName)
61     {
62         super(id, displayName);
63     }
64
65     // Don't construct the singleton class instance until we actually need it,
66
// to avoid unnecessary overhead for the derived classes.
67
public static Mode getMode()
68     {
69         if (mode == null)
70             mode = new JavaMode();
71         return mode;
72     }
73
74     public static final Object JavaDoc getJdb()
75     {
76         return jdb;
77     }
78
79     public static final void setJdb(Object JavaDoc obj)
80     {
81         jdb = obj;
82     }
83
84     public boolean canIndent()
85     {
86         return true;
87     }
88
89     public SyntaxIterator getSyntaxIterator(Position pos)
90     {
91         return new JavaSyntaxIterator(pos);
92     }
93
94     public String JavaDoc getCommentStart()
95     {
96         return "// ";
97     }
98
99     public Formatter getFormatter(Buffer buffer)
100     {
101         return new JavaFormatter(buffer);
102     }
103
104     protected void setKeyMapDefaults(KeyMap km)
105     {
106         km.mapKey('{', "electricOpenBrace");
107         km.mapKey('}', "electricCloseBrace");
108         km.mapKey(KeyEvent.VK_TAB, CTRL_MASK, "insertTab");
109         km.mapKey(KeyEvent.VK_TAB, 0, "tab");
110         km.mapKey(KeyEvent.VK_ENTER, 0, "newlineAndIndent");
111         km.mapKey(';', "electricSemi");
112         km.mapKey(':', "electricColon");
113         km.mapKey('*', "electricStar");
114         km.mapKey(KeyEvent.VK_T, CTRL_MASK, "findTag");
115         km.mapKey(KeyEvent.VK_PERIOD, ALT_MASK, "findTagAtDot");
116         km.mapKey(KeyEvent.VK_COMMA, ALT_MASK, "listMatchingTagsAtDot");
117         km.mapKey(KeyEvent.VK_PERIOD, CTRL_MASK | ALT_MASK, "findTagAtDotOtherWindow");
118         km.mapKey(')', "closeParen");
119         km.mapKey(KeyEvent.VK_I, ALT_MASK, "cycleIndentSize");
120
121         km.mapKey(KeyEvent.VK_9, CTRL_MASK | SHIFT_MASK, "insertParentheses");
122         km.mapKey(KeyEvent.VK_0, CTRL_MASK | SHIFT_MASK, "movePastCloseAndReindent");
123
124         km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK | SHIFT_MASK, "insertBraces");
125         // Duplicate mapping for 1.4.
126
km.mapKey(KeyEvent.VK_BRACELEFT, CTRL_MASK | SHIFT_MASK, "insertBraces");
127
128         km.mapKey(KeyEvent.VK_F12, 0, "wrapComment");
129
130         // Duplicate mapping to support IBM 1.3 for Linux.
131
km.mapKey(0xffc9, 0, "wrapComment"); // F12
132

133         km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK, "fold");
134         km.mapKey(KeyEvent.VK_CLOSE_BRACKET, CTRL_MASK, "unfold");
135
136         km.mapKey(KeyEvent.VK_F9, 0, "compile");
137         km.mapKey(KeyEvent.VK_F9, CTRL_MASK, "recompile");
138         km.mapKey(KeyEvent.VK_F1, ALT_MASK, "jdkHelp");
139         km.mapKey(KeyEvent.VK_F1, CTRL_MASK, "source");
140
141         // This is the "normal" mapping.
142
km.mapKey(KeyEvent.VK_COMMA, CTRL_MASK | SHIFT_MASK, "htmlInsertTag");
143         // The "normal" mapping doesn't work for Linux, but this does.
144
km.mapKey(0x7c, CTRL_MASK | SHIFT_MASK, "htmlInsertTag");
145
146         if (Editor.checkExperimental()) {
147             km.mapKey(KeyEvent.VK_SEMICOLON, ALT_MASK, "JavaMode.insertComment");
148             km.mapKey(KeyEvent.VK_ENTER, ALT_MASK, "JavaMode.newlineAndIndentForComment");
149         }
150
151         if (Platform.isPlatformLinux()) {
152             // Blackdown 1.1.7v3, 1.2pre2, IBM 1.1.8.
153
// Duplicate mappings needed for VK_9, VK_0 and VK_OPEN_BRACKET.
154
km.mapKey(0x68, CTRL_MASK | SHIFT_MASK, "insertParentheses");
155             km.mapKey(0x69, CTRL_MASK | SHIFT_MASK, "movePastCloseAndReindent");
156             km.mapKey(0xbb, CTRL_MASK | SHIFT_MASK, "insertBraces");
157         }
158     }
159
160     public void populateModeMenu(Editor editor, Menu menu)
161     {
162         menu.add(editor, "Compile...", 'C', "compile");
163         menu.add(editor, "Recompile", 'R', "recompile");
164         boolean enabled = CompilationCommands.getCompilationBuffer() != null;
165         menu.addSeparator();
166         menu.add(editor, "Next Error", 'N', "nextError", enabled);
167         menu.add(editor, "Previous Error", 'P', "previousError", enabled);
168         menu.add(editor, "Show Error Message", 'M', "showMessage", enabled);
169         menu.addSeparator();
170         MenuItem jdbMenuItem = menu.add(editor, "Debug...", 'D', "jdb");
171         if (jdb != null)
172             jdbMenuItem.setEnabled(false);
173         else {
174             try {
175                 Class.forName("com.sun.jdi.Bootstrap");
176             }
177             catch (ClassNotFoundException JavaDoc e) {
178                 jdbMenuItem.setEnabled(false);
179             }
180         }
181     }
182
183     public JPopupMenu JavaDoc getContextMenu(Editor editor)
184     {
185         final JPopupMenu JavaDoc popup = new JPopupMenu JavaDoc();
186         if (jdb != null) {
187             final Line line = editor.getDotLine();
188             if (line != null) {
189                 final Dispatcher dispatcher = editor.getDispatcher();
190                 JMenuItem JavaDoc menuItem =
191                     addContextMenuItem("Set breakpoint",
192                         "jdbSetBreakpoint", popup, dispatcher);
193                 if (line.isBlank() || line.getAnnotation() != null)
194                     menuItem.setEnabled(false);
195                 menuItem =
196                     addContextMenuItem("Delete breakpoint",
197                         "jdbDeleteBreakpoint", popup, dispatcher);
198                 if (line.getAnnotation() == null)
199                     menuItem.setEnabled(false);
200                 menuItem =
201                     addContextMenuItem("Run to current line",
202                         "jdbRunToCurrentLine", popup, dispatcher);
203                 if (line.isBlank())
204                     menuItem.setEnabled(false);
205                 popup.addSeparator();
206             }
207         }
208         addDefaultContextMenuItems(editor, popup);
209         popup.pack();
210         return popup;
211     }
212
213     public NavigationComponent getSidebarComponent(Editor editor)
214     {
215         if (getId() == JAVA_MODE) {
216             View view = editor.getCurrentView();
217             if (view == null)
218                 return null; // Shouldn't happen.
219
if (view.getSidebarComponent() == null)
220                 view.setSidebarComponent(new JavaTree(editor));
221             return view.getSidebarComponent();
222         }
223         // For subclasses...
224
return super.getSidebarComponent(editor);
225     }
226
227     public Tagger getTagger(SystemBuffer buffer)
228     {
229         return new JavaTagger(buffer);
230     }
231
232     public boolean isTaggable()
233     {
234         return true;
235     }
236
237     public boolean hasQualifiedNames()
238     {
239         return true;
240     }
241
242     public boolean isQualifiedName(String JavaDoc s)
243     {
244         return s.indexOf('.') >= 0;
245     }
246
247     public int getCorrectIndentation(final Line line, final Buffer buffer)
248     {
249         if (line.flags() == STATE_COMMENT)
250             return indentComment(line, buffer);
251         final String JavaDoc text = line.trim();
252         final char textFirstChar = text.length() > 0 ? text.charAt(0) : 0;
253         if (textFirstChar == '}')
254             return indentClosingBrace(line, buffer);
255
256         if (textFirstChar == 'c' || textFirstChar == 'd') {
257             // Does line begin with "case" or "default"?
258
final String JavaDoc firstIdentifier = getFirstIdentifier(text);
259             if (firstIdentifier.equals("case") || firstIdentifier.equals("default"))
260                 return indentSwitchLabel(line, buffer);
261             // Otherwise fall through...
262
} else if (textFirstChar == 'e') {
263             // Does line begin with "else" or "elseif"?
264
final String JavaDoc firstIdentifier = getFirstIdentifier(text);
265             if (firstIdentifier.equals("else") || firstIdentifier.equals("elseif")) {
266                 Position match = matchElse(new Position(line, 0));
267                 if (match != null)
268                     return buffer.getIndentation(match.getLine());
269             }
270             // Otherwise fall through...
271
}
272
273         Position paren = findEnclosingParen(new Position(line, 0));
274         if (paren != null)
275             return indentInParen(paren, buffer);
276
277         final Line model = findModel(line);
278         if (model == null)
279             return 0;
280
281         final int indentSize = buffer.getIndentSize();
282
283         final String JavaDoc firstIdentifier = getFirstIdentifier(text);
284         if (firstIdentifier.equals("throws") ||
285             firstIdentifier.equals("implements"))
286         {
287             Position pos = findBeginningOfStatement(new Position(model, 0));
288             return buffer.getIndentation(pos.getLine()) + indentSize;
289         }
290
291         final String JavaDoc modelText = trimSyntacticWhitespace(model.getText());
292
293         // Model line can't be blank, so this is safe.
294
final char modelLastChar = modelText.charAt(modelText.length()-1);
295
296         if (modelLastChar == '{')
297             return indentAfterOpeningBrace(model, modelText, buffer);
298
299         if (modelLastChar == ')')
300             return indentAfterCloseParen(model, text, textFirstChar, buffer);
301
302         final String JavaDoc lastIdentifier = getLastIdentifier(modelText);
303         if (lastIdentifier != null && lastIdentifier.equals("else"))
304             return indentAfterElse(model, text, textFirstChar, buffer);
305
306         final char modelFirstChar = modelText.charAt(0);
307         if (modelFirstChar == 'c' || modelFirstChar == 'd') {
308             final String JavaDoc modelFirstIdentifier = getFirstIdentifier(modelText);
309             if (modelFirstIdentifier.equals("case") ||
310                 modelFirstIdentifier.equals("default"))
311                 return indentAfterSwitchLabel(model, text, textFirstChar, buffer);
312             // Otherwise fall through...
313
}
314
315         final int indent = getIndentationOfEnclosingScope(line, buffer);
316
317         if (textFirstChar == '{') {
318             if (buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE)) {
319                 // Never indent before the opening brace of a class or method.
320
if (!isOpeningBraceOfClassOrMethod(line))
321                     return indent + indentSize;
322             }
323             return indent;
324         }
325
326         if (modelLastChar == ',') {
327             if (buffer.getModeId() == CPP_MODE && modelFirstChar == ':') {
328                 // Model line is start of member initialization list, current
329
// line is continuation.
330
return indent + 2;
331             }
332             if (isInArrayInitializer(line))
333                 return indent;
334             // Otherwise it's a continuation line.
335
return indent + indentSize;
336         }
337
338         // Check for continuation line.
339
if (isContinued(modelText, modelLastChar))
340             return indent + indentSize;
341
342         return indent;
343     }
344
345     private final int indentComment(Line line, Buffer buffer)
346     {
347         final Line model = findModel(line);
348         if (model == null)
349             return 0;
350         int indent = buffer.getIndentation(model);
351         if (model.trim().startsWith("/*"))
352             if (line.trim().startsWith("*"))
353                 return indent+1;
354         return indent;
355     }
356
357     private final int indentClosingBrace(Line line, Buffer buffer)
358     {
359         Position pos = matchClosingBrace(new Position(line, 0));
360         if (isOpeningBraceOfClassOrMethod(pos.getLine()))
361             pos = findBeginningOfStatement(pos);
362         else if (!pos.getLine().trim().startsWith("{"))
363             pos = findPreviousConditional(pos);
364         return buffer.getIndentation(pos.getLine());
365     }
366
367     private final int indentSwitchLabel(Line line, Buffer buffer)
368     {
369         Line switchLine = findSwitch(line);
370         if (switchLine != null)
371             return buffer.getIndentation(switchLine) + buffer.getIndentSize();
372         return 0;
373     }
374
375     private final int indentInParen(Position posParen, Buffer buffer)
376     {
377         final Line line = posParen.getLine();
378         if (line.trim().endsWith("(") || !buffer.getBooleanProperty(Property.LINEUP_ARGLIST))
379             return buffer.getIndentation(line) + buffer.getIndentSize();
380         final int limit = line.length();
381         int offset = posParen.getOffset();
382         do {
383             ++offset;
384         } while (offset < limit && line.charAt(offset) <= ' ');
385         if (offset <= limit)
386             return buffer.getCol(line, offset);
387         return 0;
388     }
389
390     private final int indentAfterOpeningBrace(Line model, String JavaDoc modelText,
391         Buffer buffer)
392     {
393         final int indentSize = buffer.getIndentSize();
394         if (isOpeningBraceOfClassOrMethod(model)) {
395             Position pos = findBeginningOfStatement(new Position(model, 0));
396             int indent = buffer.getIndentation(pos.getLine());
397             if (buffer.getBooleanProperty(Property.INDENT_AFTER_OPENING_BRACE))
398                 indent += indentSize;
399             return indent;
400         }
401         Position pos = new Position(model, model.length()-1);
402         if (modelText.charAt(0) != '{')
403             pos = findPreviousConditional(pos);
404         int indent = buffer.getIndentation(pos.getLine());
405         if (buffer.getBooleanProperty(Property.INDENT_AFTER_BRACE))
406             indent += indentSize;
407         final boolean indentBeforeBrace =
408             buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE);
409         if (indentBeforeBrace && pos.getLine() != model)
410             indent += indentSize;
411         return indent;
412     }
413
414     private final int indentAfterElse(Line model, String JavaDoc text,
415         char textFirstChar, Buffer buffer)
416     {
417         int indent = buffer.getIndentation(model);
418         final boolean indentBeforeBrace =
419             buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE);
420         if (indentBeforeBrace || textFirstChar != '{')
421             return indent + buffer.getIndentSize();
422         else
423             return indent;
424     }
425
426     private final int indentAfterSwitchLabel(Line model, String JavaDoc text,
427         char textFirstChar, Buffer buffer)
428     {
429         int indent = buffer.getIndentation(model);
430         final boolean indentBeforeBrace =
431             buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE);
432         if (indentBeforeBrace || textFirstChar != '{')
433             return indent + buffer.getIndentSize();
434         else
435             return indent;
436     }
437
438     private final int indentAfterCloseParen(Line model, String JavaDoc text,
439         char textFirstChar, Buffer buffer)
440     {
441         // Find matching '('.
442
SyntaxIterator it = getSyntaxIterator(new Position(model, model.length()));
443         char c;
444         do {
445             c = it.prevChar();
446         } while (c != SyntaxIterator.DONE && c != ')');
447         Position pos = it.getPosition();
448         pos = matchClosingParen(pos);
449         boolean indent = false;
450         final String JavaDoc s = getIdentifierBefore(pos);
451         final String JavaDoc[] indentAfter = {"if", "while", "for", "switch", "catch"};
452         if (Utilities.isOneOf(s, indentAfter)) {
453             indent = true;
454         } else if (buffer.getModeId() == JAVA_MODE) {
455             if (s.equals("synchronized"))
456                 indent = true;
457         } else if (buffer.getModeId() == PHP_MODE) {
458             if (s.equals("elseif") || s.equals("foreach"))
459                 indent = true;
460         }
461         if (indent) {
462             int modelIndent = buffer.getIndentation(pos.getLine());
463             if (textFirstChar != '{' ||
464                 buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE))
465                 return modelIndent + buffer.getIndentSize();
466             else
467                 return modelIndent;
468         }
469         if (buffer.getModeId() == JAVA_MODE) {
470             RE re = new UncheckedRE("\\s+new\\s+");
471             if (re.getMatch(pos.getLine().getText().substring(0, pos.getOffset())) != null)
472                 indent = true;
473         }
474         int modelIndent =
475             buffer.getIndentation(findBeginningOfStatement(pos).getLine());
476         if (indent && (textFirstChar != '{' || buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE)))
477             return modelIndent + buffer.getIndentSize();
478         else
479             return modelIndent;
480     }
481
482     private final int getIndentationOfEnclosingScope(Line line, Buffer buffer)
483     {
484         SyntaxIterator it = getSyntaxIterator(new Position(line, 0));
485       loop:
486         while (true) {
487             switch (it.prevChar()) {
488                 case ')': {
489                     Position pos = matchClosingParen(it.getPosition());
490                     it = getSyntaxIterator(pos);
491                     break;
492                 }
493                 case '}': {
494                     Position pos = matchClosingBrace(it.getPosition());
495                     pos = findBeginningOfStatement(pos);
496                     return buffer.getIndentation(pos.getLine());
497                 }
498                 case '{': {
499                     Line model = it.getLine();
500                     String JavaDoc modelText = trimSyntacticWhitespace(model.getText());
501                     if (modelText.equals("{"))
502                         return buffer.getIndentation(model) + buffer.getIndentSize();
503                     return indentAfterOpeningBrace(model, modelText, buffer);
504                 }
505                 case ':': {
506                     String JavaDoc firstIdentifier = getFirstIdentifier(it.getLine());
507                     if (firstIdentifier.equals("case") || firstIdentifier.equals("default"))
508                         return buffer.getIndentation(it.getLine()) + buffer.getIndentSize();
509                     break;
510                 }
511                 case SyntaxIterator.DONE:
512                     return 0;
513             }
514         }
515     }
516
517     private boolean isInArrayInitializer(Line line)
518     {
519         // Find matching opening brace.
520
Position match = matchClosingBrace(new Position(line, 0));
521         SyntaxIterator it = getSyntaxIterator(match);
522         char c;
523         do {
524             c = it.prevChar();
525         } while (c != SyntaxIterator.DONE && Character.isWhitespace(c));
526         if (c == '=' || c == ']')
527             return true;
528         return false;
529     }
530
531     protected static Line findModel(Line line)
532     {
533         Line model = line.previous();
534         if (line.flags() == STATE_COMMENT) {
535             // Any non-blank line is an acceptable model.
536
while (model != null && model.isBlank())
537                 model = model.previous();
538         } else {
539             while (model != null) {
540                 if (isAcceptableModel(model))
541                     break; // Found an acceptable model.
542
else
543                     model = model.previous();
544             }
545         }
546         return model;
547     }
548
549     private static final boolean isAcceptableModel(Line line)
550     {
551         int flags = line.flags();
552         if (flags == STATE_COMMENT || flags == STATE_QUOTE)
553             return false;
554         if (line.isBlank())
555             return false;
556         String JavaDoc trim = line.trim();
557         char firstChar = trim.charAt(0);
558         if (firstChar == '/') {
559             if (trim.length() > 1 && trim.charAt(1) =='/')
560                 return false;
561         } else if (firstChar == '#')
562             return false;
563         String JavaDoc s = trimSyntacticWhitespace(line.getText());
564         if (s.length() == 0)
565             return false;
566         return true;
567     }
568
569     // Returns true if line contains opening brace of class or method.
570
private boolean isOpeningBraceOfClassOrMethod(Line line)
571     {
572         if (line.length() == 0)
573             return false;
574         String JavaDoc text = trimSyntacticWhitespace(line.getText());
575         if (!text.endsWith("{"))
576             return false;
577         if (text.equals("{")) {
578             Line modelLine = findModel(line);
579             if (modelLine == null)
580                 return true;
581             Position beginningOfStatement =
582                 findBeginningOfStatement(new Position(modelLine, 0));
583             text = beginningOfStatement.getLine().trim();
584         } else
585             text = text.substring(0, text.length()-1).trim();
586         if (text.indexOf('=') >= 0)
587             return false;
588         final String JavaDoc firstIdentifier = getFirstIdentifier(text);
589         if (Utilities.isOneOf(firstIdentifier, conditionals))
590             return false;
591         if (firstIdentifier.equals("case") || firstIdentifier.equals("default"))
592             return false;
593         return true;
594     }
595
596     protected String JavaDoc getFirstIdentifier(String JavaDoc s)
597     {
598         return Utilities.getFirstIdentifier(s, this);
599     }
600
601     protected final String JavaDoc getFirstIdentifier(Line line)
602     {
603         return getFirstIdentifier(line.trim());
604     }
605
606     protected final String JavaDoc getLastIdentifier(String JavaDoc s)
607     {
608         int i = s.length()-1;
609         while (i >= 0) {
610             if (isIdentifierPart(s.charAt(i))) {
611                 if (i > 0)
612                     --i;
613                 else
614                     break;
615             } else {
616                 ++i;
617                 break;
618             }
619         }
620         if (i >= 0 && i < s.length())
621             return s.substring(i);
622         return null;
623     }
624
625     private final String JavaDoc getIdentifierBefore(Position pos)
626     {
627         while (pos.prev())
628             if (!Character.isWhitespace(pos.getChar()))
629                 break;
630         while (isIdentifierPart(pos.getChar()) && pos.prev())
631             ;
632         while (!isIdentifierStart(pos.getChar()) && pos.next())
633             ;
634         return pos.getIdentifier(this);
635     }
636
637     protected final Position matchClosingBrace(Position start)
638     {
639         SyntaxIterator it = getSyntaxIterator(start);
640         int count = 1;
641         while (true) {
642             switch (it.prevChar()) {
643                 case '}':
644                     ++count;
645                     break;
646                 case '{':
647                     --count;
648                     if (count == 0)
649                         return it.getPosition();
650                     break;
651                 case SyntaxIterator.DONE:
652                     return it.getPosition();
653                 default:
654                     break;
655             }
656         }
657     }
658
659     protected final Position matchClosingParen(Position start)
660     {
661         SyntaxIterator it = getSyntaxIterator(start);
662         int count = 1;
663         while (true) {
664             switch (it.prevChar()) {
665                 case ')':
666                     ++count;
667                     break;
668                 case '(':
669                     --count;
670                     if (count == 0)
671                         return it.getPosition();
672                     break;
673                 case SyntaxIterator.DONE:
674                     return it.getPosition();
675                 default:
676                     break;
677             }
678         }
679     }
680
681     // Scan backwards from starting position, looking for unmatched opening
682
// parenthesis.
683
protected Position findEnclosingParen(Position start)
684     {
685         SyntaxIterator it = getSyntaxIterator(start);
686         int parenCount = 0;
687         int braceCount = 0;
688         boolean seenBrace = false;
689         while (true) {
690             switch (it.prevChar()) {
691                 case '{':
692                     if (braceCount == 0)
693                         return null; // Found unmatched '{'.
694
--braceCount;
695                     seenBrace = true;
696                     break;
697                 case '}':
698                     ++braceCount;
699                     seenBrace = true;
700                     break;
701                 case ';':
702                     if (seenBrace)
703                         return null;
704                     break;
705                 case ')':
706                     ++parenCount;
707                     break;
708                 case '(':
709                     if (parenCount == 0)
710                         return it.getPosition(); // Found unmatched '('.
711
--parenCount;
712                     break;
713                 case SyntaxIterator.DONE:
714                     return null;
715                 default:
716                     break;
717             }
718         }
719     }
720
721     private final Position findEnclosingBrace(Position start)
722     {
723         SyntaxIterator it = getSyntaxIterator(start);
724         int count = 0;
725         while (true) {
726             switch (it.prevChar()) {
727                 case '}':
728                     ++count;
729                     break;
730                 case '{':
731                     if (count == 0)
732                         return it.getPosition(); // Found unmatched '{'.
733
--count;
734                     break;
735                 case SyntaxIterator.DONE:
736                     return null;
737                 default:
738                     break;
739             }
740         }
741     }
742
743     // Scan backwards from line, looking for the start of a switch statement.
744
protected final Line findSwitch(Line line)
745     {
746         Position pos = findEnclosingBrace(new Position(line, 0));
747         if (pos != null) {
748             line = pos.getLine();
749             do {
750                 String JavaDoc s = getFirstIdentifier(line);
751                 if (s.equals("switch"))
752                     return line;
753             } while ((line = line.previous()) != null);
754         }
755         return null;
756     }
757
758     private Position matchElse(Position start)
759     {
760         SyntaxIterator it = getSyntaxIterator(start);
761         int count = 1;
762         char c;
763         while ((c = it.prevChar()) != SyntaxIterator.DONE) {
764             if (c == '}') {
765                 Position match = matchClosingBrace(it.getPosition());
766                 it = getSyntaxIterator(match);
767                 continue;
768             }
769             if (c == 'e') {
770                 Position pos = it.getPosition();
771                 if (pos.getIdentifier(this).equals("else")) {
772                     ++count;
773                     continue;
774                 }
775             }
776             if (c == 'i') {
777                 Position pos = it.getPosition();
778                 if (pos.getIdentifier(this).equals("if")) {
779                     --count;
780                     if (count == 0)
781                         return pos;
782                     continue;
783                 }
784             }
785         }
786         return null;
787     }
788
789     public Position findBeginningOfStatement(Position start)
790     {
791         Position pos = new Position(start);
792
793         final Position posParen = findEnclosingParen(pos);
794
795         if (posParen != null)
796             pos = posParen;
797
798         final String JavaDoc trim = trimSyntacticWhitespace(pos.getLine().getText());
799         final String JavaDoc lastIdentifier = getLastIdentifier(trim);
800         if (lastIdentifier != null && lastIdentifier.equals("else"))
801             return new Position(pos.getLine(), 0); // BUG!! This is clearly wrong!
802
final String JavaDoc firstIdentifier = getFirstIdentifier(trim);
803         if (firstIdentifier != null &&
804             (firstIdentifier.equals("case") || firstIdentifier.equals("default")))
805             return new Position(pos.getLine(), 0);
806
807         while (pos.getLine().trim().startsWith("}") && pos.getPreviousLine() != null) {
808             pos.moveTo(pos.getPreviousLine(), pos.getPreviousLine().length());
809             pos = matchClosingBrace(pos);
810         }
811
812         SyntaxIterator it = getSyntaxIterator(pos);
813         boolean inParen = false;
814         int count = 0;
815         while (true) {
816             char c = it.prevChar();
817             if (c == SyntaxIterator.DONE)
818                 return it.getPosition();
819             if (inParen) {
820                 if (c == ')')
821                     ++count;
822                 else if (c == '(') {
823                     --count;
824                     if (count == 0) // Found it!
825
inParen = false;
826                 }
827                 continue;
828             }
829             if (c == ')') {
830                 inParen = true;
831                 count = 1;
832                 continue;
833             }
834             if (c == '{') {
835                 // If previous non-whitespace char is '=' then this is an array
836
// initializer.
837
pos = it.getPosition(); // Save position.
838
char ch;
839                 do {
840                     ch = it.prevChar();
841                 } while (ch != SyntaxIterator.DONE && Character.isWhitespace(ch));
842                 if (ch == '=' || ch == ']') {
843                     // It is an array initializer.
844
pos = it.getPosition();
845                     pos.moveTo(pos.getLine(), 0);
846                     return pos;
847                 }
848                 // Not an array initializer.
849
it = getSyntaxIterator(pos); // Restore position.
850
// Fall through...
851
}
852             if (";{}:".indexOf(c) >= 0) {
853                 do {
854                     c = it.nextChar();
855                 } while (c != SyntaxIterator.DONE && Character.isWhitespace(c));
856                 pos = it.getPosition();
857                 pos.setOffset(0);
858                 return pos;
859             }
860         }
861     }
862
863     public Position findPreviousConditional(Position start)
864     {
865         Position pos = start.copy();
866         Position posParen = findEnclosingParen(pos);
867         if (posParen != null)
868             pos = posParen;
869         while (pos.getLine().trim().startsWith("}") && pos.getPreviousLine() != null) {
870             pos.moveTo(pos.getPreviousLine(), pos.getPreviousLine().length());
871             pos = matchClosingBrace(pos);
872         }
873         while (true) {
874             if (pos.getLine().flags() != STATE_COMMENT) {
875                 String JavaDoc text = pos.getLine().trim();
876                 // Handle "} else".
877
if (text.startsWith("}"))
878                     text = text.substring(1).trim();
879                 String JavaDoc firstIdentifier = getFirstIdentifier(text);
880                 if (Utilities.isOneOf(firstIdentifier, conditionals)) {
881                     pos.setOffset(pos.getLine().getText().indexOf(firstIdentifier));
882                     return pos;
883                 }
884             }
885             Line previousLine = pos.getPreviousLine();
886             if (previousLine == null)
887                 return new Position(pos.getLine(), 0);
888             pos.moveTo(previousLine, previousLine.length());
889             if (pos.getLine().flags() == STATE_COMMENT)
890                 continue;
891             posParen = findEnclosingParen(pos);
892             if (posParen != null) {
893                 pos = posParen;
894                 continue;
895             }
896             String JavaDoc s = trimSyntacticWhitespace(pos.getLine().getText());
897             if (s.length() > 0) {
898                 if (s.charAt(0) == '#') // C preprocessor.
899
break;
900                 char lastChar = s.charAt(s.length()-1);
901                 if (lastChar == ';' || lastChar == '{' || lastChar == '}' ||
902                     lastChar == ':')
903                     break;
904             }
905         }
906         // No conditional found.
907
return start;
908     }
909
910     protected final boolean isContinued(String JavaDoc text, char lastChar)
911     {
912         switch (lastChar) {
913             case '+':
914                 return !text.endsWith("++");
915             case '/':
916                 return !text.endsWith("//");
917             case '=':
918                 return (!text.endsWith("==") && !text.endsWith("!="));
919             case ',':
920                 return true;
921             case '.':
922                 return true;
923             case '|':
924                 return text.endsWith("||");
925             case '&':
926                 return text.endsWith("&&");
927             default:
928                 return false;
929         }
930     }
931
932     // Replaces syntactic whitespace (quotes and comments) with actual space
933
// characters, then returns trimmed string.
934
protected static String JavaDoc trimSyntacticWhitespace(String JavaDoc s)
935     {
936         JavaSyntaxIterator it = new JavaSyntaxIterator(null);
937         return new String JavaDoc(it.hideSyntacticWhitespace(s)).trim();
938     }
939
940     public boolean isIdentifierStart(char c)
941     {
942         return Character.isJavaIdentifierStart(c);
943     }
944
945     public boolean isIdentifierPart(char c)
946     {
947         return Character.isJavaIdentifierPart(c);
948     }
949
950     public boolean isInComment(Buffer buffer, Position pos)
951     {
952         if (buffer == null || pos == null) {
953             Debug.bug();
954             return false;
955         }
956         final Line line = pos.getLine();
957         final String JavaDoc text = line.getText();
958         if (text == null)
959             return false;
960         final char[] chars = text.toCharArray();
961         final int offset = pos.getOffset();
962         if (buffer.needsParsing())
963             buffer.getFormatter().parseBuffer();
964         int state = line.flags();
965         final int length = chars.length;
966         for (int i = 0; i < length; i++) {
967             if (i == offset)
968                 return state == STATE_COMMENT;
969             char c = chars[i];
970             if (c == '\\' && i < length-1) {
971                 // Escape character.
972
continue;
973             }
974             if (state == STATE_QUOTE) {
975                 if (c == '"')
976                     state = STATE_NEUTRAL;
977                 continue;
978             }
979             if (state == STATE_SINGLEQUOTE) {
980                 if (c == '\'')
981                     state = STATE_NEUTRAL;
982                 continue;
983             }
984             if (state == STATE_COMMENT) {
985                 if (c == '*' && i < length-1 && chars[i+1] == '/') {
986                     // /* */ comment ending
987
state = STATE_NEUTRAL;
988                 }
989                 continue;
990             }
991             // Reaching here, STATE_NEUTRAL...
992
if (c == '"') {
993                 state = STATE_QUOTE;
994                 continue;
995             }
996             if (c == '\'') {
997                 state = STATE_SINGLEQUOTE;
998                 continue;
999             }
1000            if (c == '/') {
1001                if (i < length-1) {
1002                    if (chars[i+1] == '*') {
1003                        // /* */ comment starting
1004
state = STATE_COMMENT;
1005                        continue;
1006                    }
1007                    if (chars[i+1] == '/') {
1008                        // "//" comment starting
1009
return true;
1010                    }
1011                }
1012            }
1013        }
1014        return state == STATE_COMMENT;
1015    }
1016
1017    public boolean isCommentLine(Line line)
1018    {
1019        return line.trim().startsWith("//");
1020    }
1021
1022    public static void insertComment()
1023    {
1024        if (!Editor.checkExperimental())
1025            return;
1026        final Editor editor = Editor.currentEditor();
1027        String JavaDoc toBeInserted =
1028            Editor.preferences().getStringProperty(Property.JAVA_MODE_INSERT_COMMENT_TEXT);
1029        if (toBeInserted == null)
1030            toBeInserted = "/**\\n * |\\n */";
1031        Position caretPos = null;
1032        CompoundEdit JavaDoc compoundEdit = editor.beginCompoundEdit();
1033        final int limit = toBeInserted.length();
1034        for (int i = 0; i < limit; i++) {
1035            char c = toBeInserted.charAt(i);
1036            if (c == '|') {
1037                caretPos = new Position(editor.getDot());
1038                continue;
1039            }
1040            if (c == '\\' && i < limit-1) {
1041                c = toBeInserted.charAt(++i);
1042                if (c == 'n') {
1043                    editor.newlineAndIndent();
1044                    continue;
1045                }
1046                // Otherwise fall through...
1047
}
1048            editor.insertChar(c);
1049        }
1050        if (caretPos != null)
1051            editor.moveDotTo(caretPos);
1052        editor.moveCaretToDotCol();
1053        editor.endCompoundEdit(compoundEdit);
1054        editor.getFormatter().parseBuffer();
1055    }
1056
1057    public static void newlineAndIndentForComment()
1058    {
1059        final Editor editor = Editor.currentEditor();
1060        if (!editor.checkReadOnly())
1061            return;
1062        final Buffer buffer = editor.getBuffer();
1063        final Display display = editor.getDisplay();
1064        String JavaDoc commentPrefix = null;
1065        String JavaDoc s = editor.getDotLine().getText().trim();
1066        int flags = editor.getDotLine().flags();
1067        if (flags == STATE_COMMENT) {
1068            if (s.startsWith("*") && !s.endsWith("*/"))
1069                commentPrefix = "* ";
1070        } else {
1071            // Look for start of comment on current line.
1072
if (s.startsWith("/*") && !s.endsWith("*/"))
1073                commentPrefix = "* ";
1074            else if (s.startsWith("//"))
1075                commentPrefix = "// ";
1076        }
1077        if (commentPrefix == null){
1078            // No special handling necessary.
1079
editor.newlineAndIndent();
1080            return;
1081        }
1082        CompoundEdit JavaDoc compoundEdit = buffer.beginCompoundEdit();
1083        if (editor.getMark() != null)
1084            editor.deleteRegion();
1085        editor.addUndo(SimpleEdit.INSERT_LINE_SEP);
1086        editor.insertLineSeparator();
1087        // Trim leading whitespace. (This code actually trims trailing
1088
// whitespace too.)
1089
editor.addUndo(SimpleEdit.LINE_EDIT);
1090        editor.getDotLine().setText(editor.getDotLine().getText().trim());
1091        // Insert the comment prefix at the start of the new line.
1092
editor.addUndo(SimpleEdit.INSERT_STRING);
1093        editor.insertStringInternal(commentPrefix);
1094        // Make the indentation code think we're still in a multi-line
1095
// comment, if we were in one before.
1096
editor.getDotLine().setFlags(flags);
1097        editor.indentLine();
1098        // We want dot to end up right after the comment prefix.
1099
editor.moveDotToIndentation();
1100        editor.getDot().skip(commentPrefix.length());
1101        display.moveCaretToDotCol();
1102        buffer.endCompoundEdit(compoundEdit);
1103    }
1104
1105    public String JavaDoc getToolTipText(Editor editor, MouseEvent JavaDoc e)
1106    {
1107        if (editor.getModeId() == JAVA_MODE) {
1108            if (editor.getBuffer().getBooleanProperty(Property.ENABLE_TOOL_TIPS)) {
1109                Position pos =
1110                    editor.getDisplay().positionFromPoint(e.getPoint());
1111                if (pos != null) {
1112                    final String JavaDoc name = getQualifiedName(pos);
1113                    if (name != null) {
1114                        JavaContext context = new JavaContext(editor);
1115                        context.parseContext(pos);
1116                        JavaVariable var = context.findDeclaration(name);
1117                        if (var != null)
1118                            return var.toString();
1119                    }
1120                }
1121            }
1122        }
1123        return null;
1124    }
1125
1126    private String JavaDoc getQualifiedName(Position pos)
1127    {
1128        Line line = pos.getLine();
1129        int offset = pos.getOffset();
1130        final int limit = line.length();
1131        if (offset < limit) {
1132            char c = line.charAt(offset);
1133            if (isIdentifierPart(c)) {
1134                while (offset > 0) {
1135                    --offset;
1136                    c = line.charAt(offset);
1137                    if (!isIdentifierPart(c) && c != '.') {
1138                        ++offset;
1139                        break;
1140                    }
1141                }
1142                // Now we're looking at the first character of the identifier.
1143
c = line.charAt(offset);
1144                if (isIdentifierStart(c)) {
1145                    FastStringBuffer sb = new FastStringBuffer();
1146                    sb.append(c);
1147                    while (++offset < limit) {
1148                        c = line.charAt(offset);
1149                        if (isIdentifierPart(c)) {
1150                            sb.append(c);
1151                        } else if (c == '.' && offset < pos.getOffset()) {
1152                            // We don't want to go beyond the end of the
1153
// simple name at pos.
1154
sb.append(c);
1155                        } else
1156                            break;
1157                    }
1158                    return sb.toString();
1159                }
1160            }
1161        }
1162        return null;
1163    }
1164
1165    public Expression getExpressionAtDot(final Editor editor, final boolean exact)
1166    {
1167        if (editor.getModeId() == OBJC_MODE)
1168            return super.getExpressionAtDot(editor, exact);
1169        if (editor.getDot() == null)
1170            return null;
1171        Position begin;
1172        if (editor.getMark() != null) {
1173            // Start at beginning of marked block.
1174
Region r = new Region(editor);
1175            begin = r.getBegin();
1176        } else
1177            begin = editor.getDot();
1178        final Line line = begin.getLine();
1179        final int offset = begin.getOffset();
1180        Position posExpr = null;
1181        if (exact) {
1182            if (offset < line.length() && isIdentifierPart(line.charAt(offset)))
1183                posExpr = findIdentifierStart(line, offset);
1184            if (posExpr == null)
1185                return null;
1186        }
1187        if (posExpr == null) {
1188            // Not exact, or no identifier there. Try to be smart.
1189
RE re = new UncheckedRE("([A-Za-z_$]+[A-Za-z_$0-9]*)\\s*\\(");
1190            final String JavaDoc text = editor.getDotLine().getText();
1191            int index = 0;
1192            REMatch match;
1193            while ((match = re.getMatch(text, index)) != null) {
1194                String JavaDoc identifier = match.toString(1);
1195                if (!isKeyword(identifier)) {
1196                    posExpr = new Position(line, match.getStartIndex());
1197                    // If we've found a match to the right of dot, we're done.
1198
if (match.getEndIndex() > offset)
1199                        break;
1200                }
1201                index = match.getEndIndex();
1202            }
1203        }
1204        if (posExpr == null) {
1205            // Smart didn't help. Go back to exact.
1206
if (offset < line.length() && isIdentifierStart(line.charAt(offset)))
1207                posExpr = findIdentifierStart(line, offset);
1208        }
1209        if (posExpr == null)
1210            return null;
1211        Position pos = posExpr.copy();
1212        // Gather up method name.
1213
FastStringBuffer sb = new FastStringBuffer();
1214        while (true) {
1215            char c = pos.getChar();
1216            if (!isIdentifierPart(c))
1217                break;
1218            sb.append(c);
1219            if (!pos.next())
1220                break;
1221        }
1222        String JavaDoc name = sb.toString().trim();
1223        // Skip whitespace (if any) between identifier and '('.
1224
while (true) {
1225            char c = pos.getChar();
1226            if (!Character.isWhitespace(c))
1227                break;
1228            if (!pos.next())
1229                break;
1230        }
1231        final int arity;
1232        if (editor.getModeId() == JAVASCRIPT_MODE)
1233            arity = -1; // Can't trust arity in JavaScript.
1234
else
1235            arity = getArity(editor, pos);
1236        if (arity >= 0)
1237            return new JavaExpression(name, arity);
1238        else
1239            return new JavaExpression(name, arity, TAG_UNKNOWN);
1240    }
1241
1242    private int getArity(Editor editor, Position pos)
1243    {
1244        if (pos.getChar() != '(')
1245            return -1;
1246        if (!pos.next())
1247            return -1;
1248        final Position start = pos.copy();
1249        int parenCount = 0;
1250        int arity = 0;
1251        char quoteChar = '\0';
1252        boolean inQuote = false;
1253        while (!pos.atEnd()) {
1254            char c = pos.getChar();
1255            if (inQuote) {
1256                if (c == quoteChar)
1257                    inQuote = false;
1258                pos.next();
1259                continue;
1260            }
1261            // Not in a quoted string.
1262
if (c == '"' || c == '\'') {
1263                inQuote = true;
1264                quoteChar = c;
1265                pos.next();
1266                continue;
1267            }
1268            if (c == ',') {
1269                if (parenCount == 0) // Top level.
1270
++arity;
1271                pos.next();
1272                continue;
1273            }
1274            if (c == '(') {
1275                ++parenCount;
1276                pos.next();
1277                continue;
1278            }
1279            if (c == ')') {
1280                --parenCount;
1281                if (parenCount < 0) {
1282                    // Closing paren, done.
1283
if (arity == 0) {
1284                        // We haven't seen a comma.
1285
Region r = new Region(editor.getBuffer(), start, pos);
1286                        if (r.toString().trim().length() > 0)
1287                            arity = 1;
1288                    } else
1289                        ++arity;
1290                    return arity;
1291                }
1292                pos.next();
1293                continue;
1294            }
1295            pos.next();
1296        }
1297        return -1;
1298    }
1299}
1300
Popular Tags