KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > JavaIndenter


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.text;
12
13 import org.eclipse.core.runtime.Assert;
14
15 import org.eclipse.jface.text.BadLocationException;
16 import org.eclipse.jface.text.IDocument;
17 import org.eclipse.jface.text.IRegion;
18
19 import org.eclipse.jdt.core.IJavaProject;
20 import org.eclipse.jdt.core.JavaCore;
21 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
22
23 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
24
25
26 /**
27  * Uses the {@link org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner} to
28  * get the indentation level for a certain position in a document.
29  *
30  * <p>
31  * An instance holds some internal position in the document and is therefore
32  * not threadsafe.
33  * </p>
34  *
35  * @since 3.0
36  */

37 public final class JavaIndenter {
38     
39     /**
40      * The JDT Core preferences.
41      * @since 3.2
42      */

43     private final class CorePrefs {
44         final boolean prefUseTabs;
45         final int prefTabSize;
46         final int prefIndentationSize;
47         final boolean prefArrayDimensionsDeepIndent;
48         final int prefArrayIndent;
49         final boolean prefArrayDeepIndent;
50         final boolean prefTernaryDeepAlign;
51         final int prefTernaryIndent;
52         final int prefCaseIndent;
53         final int prefAssignmentIndent;
54         final int prefCaseBlockIndent;
55         final int prefSimpleIndent;
56         final int prefBracketIndent;
57         final boolean prefMethodDeclDeepIndent;
58         final int prefMethodDeclIndent;
59         final boolean prefMethodCallDeepIndent;
60         final int prefMethodCallIndent;
61         final boolean prefParenthesisDeepIndent;
62         final int prefParenthesisIndent;
63         final int prefBlockIndent;
64         final int prefMethodBodyIndent;
65         final int prefTypeIndent;
66         final boolean prefIndentBracesForBlocks;
67         final boolean prefIndentBracesForArrays;
68         final boolean prefIndentBracesForMethods;
69         final boolean prefIndentBracesForTypes;
70         final int prefContinuationIndent;
71         final boolean prefHasGenerics;
72         final String JavaDoc prefTabChar;
73         
74         private final IJavaProject fProject;
75
76         /**
77          * Returns <code>true</code> if the class is used outside the workbench,
78          * <code>false</code> in normal mode
79          *
80          * @return <code>true</code> if the plug-ins are not available
81          */

82         private boolean isStandalone() {
83             return JavaCore.getPlugin() == null;
84         }
85         
86         /**
87          * Returns the possibly project-specific core preference defined under <code>key</code>.
88          *
89          * @param key the key of the preference
90          * @return the value of the preference
91          * @since 3.1
92          */

93         private String JavaDoc getCoreFormatterOption(String JavaDoc key) {
94             if (fProject == null)
95                 return JavaCore.getOption(key);
96             return fProject.getOption(key, true);
97         }
98         
99         CorePrefs(IJavaProject project) {
100             fProject= project;
101             if (isStandalone()) {
102                 prefUseTabs= true;
103                 prefTabSize= 4;
104                 prefIndentationSize= 4;
105                 prefArrayDimensionsDeepIndent= true;
106                 prefContinuationIndent= 2;
107                 prefBlockIndent= 1;
108                 prefArrayIndent= prefContinuationIndent;
109                 prefArrayDeepIndent= true;
110                 prefTernaryDeepAlign= false;
111                 prefTernaryIndent= prefContinuationIndent;
112                 prefCaseIndent= 0;
113                 prefAssignmentIndent= prefBlockIndent;
114                 prefCaseBlockIndent= prefBlockIndent;
115                 prefIndentBracesForBlocks= false;
116                 prefSimpleIndent= (prefIndentBracesForBlocks && prefBlockIndent == 0) ? 1 : prefBlockIndent;
117                 prefBracketIndent= prefBlockIndent;
118                 prefMethodDeclDeepIndent= true;
119                 prefMethodDeclIndent= 1;
120                 prefMethodCallDeepIndent= false;
121                 prefMethodCallIndent= 1;
122                 prefParenthesisDeepIndent= false;
123                 prefParenthesisIndent= prefContinuationIndent;
124                 prefMethodBodyIndent= 1;
125                 prefTypeIndent= 1;
126                 prefIndentBracesForArrays= false;
127                 prefIndentBracesForMethods= false;
128                 prefIndentBracesForTypes= false;
129                 prefHasGenerics= false;
130                 prefTabChar= JavaCore.TAB;
131             } else {
132                 prefUseTabs= prefUseTabs();
133                 prefTabSize= prefTabSize();
134                 prefIndentationSize= prefIndentationSize();
135                 prefArrayDimensionsDeepIndent= prefArrayDimensionsDeepIndent();
136                 prefContinuationIndent= prefContinuationIndent();
137                 prefBlockIndent= prefBlockIndent();
138                 prefArrayIndent= prefArrayIndent();
139                 prefArrayDeepIndent= prefArrayDeepIndent();
140                 prefTernaryDeepAlign= prefTernaryDeepAlign();
141                 prefTernaryIndent= prefTernaryIndent();
142                 prefCaseIndent= prefCaseIndent();
143                 prefAssignmentIndent= prefAssignmentIndent();
144                 prefCaseBlockIndent= prefCaseBlockIndent();
145                 prefIndentBracesForBlocks= prefIndentBracesForBlocks();
146                 prefSimpleIndent= prefSimpleIndent();
147                 prefBracketIndent= prefBracketIndent();
148                 prefMethodDeclDeepIndent= prefMethodDeclDeepIndent();
149                 prefMethodDeclIndent= prefMethodDeclIndent();
150                 prefMethodCallDeepIndent= prefMethodCallDeepIndent();
151                 prefMethodCallIndent= prefMethodCallIndent();
152                 prefParenthesisDeepIndent= prefParenthesisDeepIndent();
153                 prefParenthesisIndent= prefParenthesisIndent();
154                 prefMethodBodyIndent= prefMethodBodyIndent();
155                 prefTypeIndent= prefTypeIndent();
156                 prefIndentBracesForArrays= prefIndentBracesForArrays();
157                 prefIndentBracesForMethods= prefIndentBracesForMethods();
158                 prefIndentBracesForTypes= prefIndentBracesForTypes();
159                 prefHasGenerics= hasGenerics();
160                 prefTabChar= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR);
161             }
162         }
163         
164         private boolean prefUseTabs() {
165             return !JavaCore.SPACE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
166         }
167
168         private int prefTabSize() {
169             return CodeFormatterUtil.getTabWidth(fProject);
170         }
171
172         private int prefIndentationSize() {
173             return CodeFormatterUtil.getIndentWidth(fProject);
174         }
175
176         private boolean prefArrayDimensionsDeepIndent() {
177             return true; // sensible default, no formatter setting
178
}
179
180         private int prefArrayIndent() {
181             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
182             try {
183                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
184                     return 1;
185             } catch (IllegalArgumentException JavaDoc e) {
186                 // ignore and return default
187
}
188
189             return prefContinuationIndent(); // default
190
}
191
192         private boolean prefArrayDeepIndent() {
193             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
194             try {
195                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
196             } catch (IllegalArgumentException JavaDoc e) {
197                 // ignore and return default
198
}
199
200             return true;
201         }
202
203         private boolean prefTernaryDeepAlign() {
204             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
205             try {
206                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
207             } catch (IllegalArgumentException JavaDoc e) {
208                 // ignore and return default
209
}
210             return false;
211         }
212
213         private int prefTernaryIndent() {
214             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
215             try {
216                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
217                     return 1;
218                 else
219                     return prefContinuationIndent();
220             } catch (IllegalArgumentException JavaDoc e) {
221                 // ignore and return default
222
}
223
224             return prefContinuationIndent();
225         }
226
227         private int prefCaseIndent() {
228             if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
229                 return prefBlockIndent();
230             else
231                 return 0;
232         }
233
234         private int prefAssignmentIndent() {
235             return prefBlockIndent();
236         }
237
238         private int prefCaseBlockIndent() {
239             if (true)
240                 return prefBlockIndent();
241
242             if (DefaultCodeFormatterConstants.TRUE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
243                 return prefBlockIndent();
244             else
245                 return 0;
246         }
247
248         private int prefSimpleIndent() {
249             if (prefIndentBracesForBlocks() && prefBlockIndent() == 0)
250                 return 1;
251             else return prefBlockIndent();
252         }
253
254         private int prefBracketIndent() {
255             return prefBlockIndent();
256         }
257
258         private boolean prefMethodDeclDeepIndent() {
259             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
260             try {
261                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
262             } catch (IllegalArgumentException JavaDoc e) {
263                 // ignore and return default
264
}
265
266             return true;
267         }
268
269         private int prefMethodDeclIndent() {
270             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
271             try {
272                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
273                     return 1;
274                 else
275                     return prefContinuationIndent();
276             } catch (IllegalArgumentException JavaDoc e) {
277                 // ignore and return default
278
}
279             return 1;
280         }
281
282         private boolean prefMethodCallDeepIndent() {
283             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
284             try {
285                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
286             } catch (IllegalArgumentException JavaDoc e) {
287                 // ignore and return default
288
}
289             return false; // sensible default
290
}
291
292         private int prefMethodCallIndent() {
293             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
294             try {
295                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
296                     return 1;
297                 else
298                     return prefContinuationIndent();
299             } catch (IllegalArgumentException JavaDoc e) {
300                 // ignore and return default
301
}
302
303             return 1; // sensible default
304
}
305
306         private boolean prefParenthesisDeepIndent() {
307             if (true) // don't do parenthesis deep indentation
308
return false;
309
310             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
311             try {
312                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
313             } catch (IllegalArgumentException JavaDoc e) {
314                 // ignore and return default
315
}
316
317             return false; // sensible default
318
}
319
320         private int prefParenthesisIndent() {
321             return prefContinuationIndent();
322         }
323
324         private int prefBlockIndent() {
325             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BLOCK);
326             if (DefaultCodeFormatterConstants.FALSE.equals(option))
327                 return 0;
328
329             return 1; // sensible default
330
}
331
332         private int prefMethodBodyIndent() {
333             if (DefaultCodeFormatterConstants.FALSE.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_STATEMENTS_COMPARE_TO_BODY)))
334                 return 0;
335
336             return 1; // sensible default
337
}
338
339         private int prefTypeIndent() {
340             String JavaDoc option= getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_TYPE_HEADER);
341             if (DefaultCodeFormatterConstants.FALSE.equals(option))
342                 return 0;
343
344             return 1; // sensible default
345
}
346
347         private boolean prefIndentBracesForBlocks() {
348             return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK));
349         }
350
351         private boolean prefIndentBracesForArrays() {
352             return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER));
353         }
354
355         private boolean prefIndentBracesForMethods() {
356             return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION));
357         }
358
359         private boolean prefIndentBracesForTypes() {
360             return DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED.equals(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION));
361         }
362
363         private int prefContinuationIndent() {
364             try {
365                 return Integer.parseInt(getCoreFormatterOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION));
366             } catch (NumberFormatException JavaDoc e) {
367                 // ignore and return default
368
}
369
370             return 2; // sensible default
371
}
372         private boolean hasGenerics() {
373             return JavaCore.VERSION_1_5.compareTo(getCoreFormatterOption(JavaCore.COMPILER_SOURCE)) <= 0;
374         }
375     }
376
377     /** The document being scanned. */
378     private final IDocument fDocument;
379     /** The indentation accumulated by <code>findReferencePosition</code>. */
380     private int fIndent;
381     /**
382      * The absolute (character-counted) indentation offset for special cases
383      * (method defs, array initializers)
384      */

385     private int fAlign;
386     /** The stateful scanposition for the indentation methods. */
387     private int fPosition;
388     /** The previous position. */
389     private int fPreviousPos;
390     /** The most recent token. */
391     private int fToken;
392     /** The line of <code>fPosition</code>. */
393     private int fLine;
394     /**
395      * The scanner we will use to scan the document. It has to be installed
396      * on the same document as the one we get.
397      */

398     private final JavaHeuristicScanner fScanner;
399     /**
400      * The JDT Core preferences.
401      * @since 3.2
402      */

403     private final CorePrefs fPrefs;
404
405     /**
406      * Creates a new instance.
407      *
408      * @param document the document to scan
409      * @param scanner the {@link JavaHeuristicScanner} to be used for scanning
410      * the document. It must be installed on the same <code>IDocument</code>.
411      */

412     public JavaIndenter(IDocument document, JavaHeuristicScanner scanner) {
413         this(document, scanner, null);
414     }
415
416     /**
417      * Creates a new instance.
418      *
419      * @param document the document to scan
420      * @param scanner the {@link JavaHeuristicScanner}to be used for scanning
421      * the document. It must be installed on the same
422      * <code>IDocument</code>.
423      * @param project the java project to get the formatter preferences from, or
424      * <code>null</code> to use the workspace settings
425      * @since 3.1
426      */

427     public JavaIndenter(IDocument document, JavaHeuristicScanner scanner, IJavaProject project) {
428         Assert.isNotNull(document);
429         Assert.isNotNull(scanner);
430         fDocument= document;
431         fScanner= scanner;
432         fPrefs= new CorePrefs(project);
433     }
434
435     /**
436      * Computes the indentation at the reference point of <code>position</code>.
437      *
438      * @param offset the offset in the document
439      * @return a String which reflects the indentation at the line in which the
440      * reference position to <code>offset</code> resides, or <code>null</code>
441      * if it cannot be determined
442      */

443     public StringBuffer JavaDoc getReferenceIndentation(int offset) {
444         return getReferenceIndentation(offset, false);
445     }
446
447     /**
448      * Computes the indentation at the reference point of <code>position</code>.
449      *
450      * @param offset the offset in the document
451      * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
452      * @return a String which reflects the indentation at the line in which the
453      * reference position to <code>offset</code> resides, or <code>null</code>
454      * if it cannot be determined
455      */

456     private StringBuffer JavaDoc getReferenceIndentation(int offset, boolean assumeOpeningBrace) {
457
458         int unit;
459         if (assumeOpeningBrace)
460             unit= findReferencePosition(offset, Symbols.TokenLBRACE);
461         else
462             unit= findReferencePosition(offset, peekChar(offset));
463
464         // if we were unable to find anything, return null
465
if (unit == JavaHeuristicScanner.NOT_FOUND)
466             return null;
467
468         return getLeadingWhitespace(unit);
469
470     }
471
472     /**
473      * Computes the indentation at <code>offset</code>.
474      *
475      * @param offset the offset in the document
476      * @return a String which reflects the correct indentation for the line in
477      * which offset resides, or <code>null</code> if it cannot be
478      * determined
479      */

480     public StringBuffer JavaDoc computeIndentation(int offset) {
481         return computeIndentation(offset, false);
482     }
483
484     /**
485      * Computes the indentation at <code>offset</code>.
486      *
487      * @param offset the offset in the document
488      * @param assumeOpeningBrace <code>true</code> if an opening brace should be assumed
489      * @return a String which reflects the correct indentation for the line in
490      * which offset resides, or <code>null</code> if it cannot be
491      * determined
492      */

493     public StringBuffer JavaDoc computeIndentation(int offset, boolean assumeOpeningBrace) {
494
495         StringBuffer JavaDoc reference= getReferenceIndentation(offset, assumeOpeningBrace);
496
497         // handle special alignment
498
if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
499             try {
500                 // a special case has been detected.
501
IRegion line= fDocument.getLineInformationOfOffset(fAlign);
502                 int lineOffset= line.getOffset();
503                 return createIndent(lineOffset, fAlign, false);
504             } catch (BadLocationException e) {
505                 return null;
506             }
507         }
508
509         if (reference == null)
510             return null;
511
512         // add additional indent
513
return createReusingIndent(reference, fIndent);
514     }
515
516     /**
517      * Computes the length of a <code>CharacterSequence</code>, counting
518      * a tab character as the size until the next tab stop and every other
519      * character as one.
520      *
521      * @param indent the string to measure
522      * @return the visual length in characters
523      */

524     private int computeVisualLength(CharSequence JavaDoc indent) {
525         final int tabSize= fPrefs.prefTabSize;
526         int length= 0;
527         for (int i= 0; i < indent.length(); i++) {
528             char ch= indent.charAt(i);
529             switch (ch) {
530                 case '\t':
531                     if (tabSize > 0) {
532                         int reminder= length % tabSize;
533                         length += tabSize - reminder;
534                     }
535                     break;
536                 case ' ':
537                     length++;
538                     break;
539             }
540         }
541         return length;
542     }
543
544     /**
545      * Strips any characters off the end of <code>reference</code> that exceed
546      * <code>indentLength</code>.
547      *
548      * @param reference the string to measure
549      * @param indentLength the maximum visual indentation length
550      * @return the stripped <code>reference</code>
551      */

552     private StringBuffer JavaDoc stripExceedingChars(StringBuffer JavaDoc reference, int indentLength) {
553         final int tabSize= fPrefs.prefTabSize;
554         int measured= 0;
555         int chars= reference.length();
556         int i= 0;
557         for (; measured < indentLength && i < chars; i++) {
558             char ch= reference.charAt(i);
559             switch (ch) {
560                 case '\t':
561                     if (tabSize > 0) {
562                         int reminder= measured % tabSize;
563                         measured += tabSize - reminder;
564                     }
565                     break;
566                 case ' ':
567                     measured++;
568                     break;
569             }
570         }
571         int deleteFrom= measured > indentLength ? i - 1 : i;
572
573         return reference.delete(deleteFrom, chars);
574     }
575
576     /**
577      * Returns the indentation of the line at <code>offset</code> as a
578      * <code>StringBuffer</code>. If the offset is not valid, the empty string
579      * is returned.
580      *
581      * @param offset the offset in the document
582      * @return the indentation (leading whitespace) of the line in which
583      * <code>offset</code> is located
584      */

585     private StringBuffer JavaDoc getLeadingWhitespace(int offset) {
586         StringBuffer JavaDoc indent= new StringBuffer JavaDoc();
587         try {
588             IRegion line= fDocument.getLineInformationOfOffset(offset);
589             int lineOffset= line.getOffset();
590             int nonWS= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, lineOffset + line.getLength());
591             indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
592             return indent;
593         } catch (BadLocationException e) {
594             return indent;
595         }
596     }
597
598     /**
599      * Creates an indentation string of the length indent - start, consisting of
600      * the content in <code>fDocument</code> in the range [start, indent),
601      * with every character replaced by a space except for tabs, which are kept
602      * as such.
603      * <p>
604      * If <code>convertSpaceRunsToTabs</code> is <code>true</code>, every
605      * run of the number of spaces that make up a tab are replaced by a tab
606      * character. If it is not set, no conversion takes place, but tabs in the
607      * original range are still copied verbatim.
608      * </p>
609      *
610      * @param start the start of the document region to copy the indent from
611      * @param indent the exclusive end of the document region to copy the indent
612      * from
613      * @param convertSpaceRunsToTabs whether to convert consecutive runs of
614      * spaces to tabs
615      * @return the indentation corresponding to the document content specified
616      * by <code>start</code> and <code>indent</code>
617      */

618     private StringBuffer JavaDoc createIndent(int start, final int indent, final boolean convertSpaceRunsToTabs) {
619         final boolean convertTabs= fPrefs.prefUseTabs && convertSpaceRunsToTabs;
620         final int tabLen= fPrefs.prefTabSize;
621         final StringBuffer JavaDoc ret= new StringBuffer JavaDoc();
622         try {
623             int spaces= 0;
624             while (start < indent) {
625
626                 char ch= fDocument.getChar(start);
627                 if (ch == '\t') {
628                     ret.append('\t');
629                     spaces= 0;
630                 } else if (convertTabs) {
631                     spaces++;
632                     if (spaces == tabLen) {
633                         ret.append('\t');
634                         spaces= 0;
635                     }
636                 } else {
637                     ret.append(' ');
638                 }
639
640                 start++;
641             }
642             // remainder
643
while (spaces-- > 0)
644                 ret.append(' ');
645
646         } catch (BadLocationException e) {
647         }
648
649         return ret;
650     }
651
652     /**
653      * Creates a string with a visual length of the given
654      * <code>indentationSize</code>.
655      *
656      * @param buffer the original indent to reuse if possible
657      * @param additional the additional indentation units to add or subtract to
658      * reference
659      * @return the modified <code>buffer</code> reflecting the indentation
660      * adapted to <code>additional</code>
661      */

662     private StringBuffer JavaDoc createReusingIndent(StringBuffer JavaDoc buffer, int additional) {
663         int refLength= computeVisualLength(buffer);
664         int addLength= fPrefs.prefIndentationSize * additional; // may be < 0
665
int totalLength= Math.max(0, refLength + addLength);
666
667
668         // copy the reference indentation for the indent up to the last tab
669
// stop within the maxCopy area
670
int minLength= Math.min(totalLength, refLength);
671         int tabSize= fPrefs.prefTabSize;
672         int maxCopyLength= tabSize > 0 ? minLength - minLength % tabSize : minLength; // maximum indent to copy
673
stripExceedingChars(buffer, maxCopyLength);
674
675
676         // add additional indent
677
int missing= totalLength - maxCopyLength;
678         final int tabs, spaces;
679         if (JavaCore.SPACE.equals(fPrefs.prefTabChar)) {
680             tabs= 0;
681             spaces= missing;
682         } else if (JavaCore.TAB.equals(fPrefs.prefTabChar)) {
683             tabs= tabSize > 0 ? missing / tabSize : 0;
684             spaces= tabSize > 0 ? missing % tabSize : missing;
685         } else if (DefaultCodeFormatterConstants.MIXED.equals(fPrefs.prefTabChar)) {
686             tabs= tabSize > 0 ? missing / tabSize : 0;
687             spaces= tabSize > 0 ? missing % tabSize : missing;
688         } else {
689             Assert.isTrue(false);
690             return null;
691         }
692         for(int i= 0; i < tabs; i++)
693             buffer.append('\t');
694         for(int i= 0; i < spaces; i++)
695             buffer.append(' ');
696         return buffer;
697     }
698
699     /**
700      * Returns the reference position regarding to indentation for <code>offset</code>,
701      * or <code>NOT_FOUND</code>. This method calls
702      * {@link #findReferencePosition(int, int) findReferencePosition(offset, nextChar)} where
703      * <code>nextChar</code> is the next character after <code>offset</code>.
704      *
705      * @param offset the offset for which the reference is computed
706      * @return the reference statement relative to which <code>offset</code>
707      * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
708      */

709     public int findReferencePosition(int offset) {
710         return findReferencePosition(offset, peekChar(offset));
711     }
712
713     /**
714      * Peeks the next char in the document that comes after <code>offset</code>
715      * on the same line as <code>offset</code>.
716      *
717      * @param offset the offset into document
718      * @return the token symbol of the next element, or TokenEOF if there is none
719      */

720     private int peekChar(int offset) {
721         if (offset < fDocument.getLength()) {
722             try {
723                 IRegion line= fDocument.getLineInformationOfOffset(offset);
724                 int lineOffset= line.getOffset();
725                 int next= fScanner.nextToken(offset, lineOffset + line.getLength());
726                 return next;
727             } catch (BadLocationException e) {
728             }
729         }
730         return Symbols.TokenEOF;
731     }
732
733     /**
734      * Returns the reference position regarding to indentation for <code>position</code>,
735      * or <code>NOT_FOUND</code>.
736      *
737      * <p>If <code>peekNextChar</code> is <code>true</code>, the next token after
738      * <code>offset</code> is read and taken into account when computing the
739      * indentation. Currently, if the next token is the first token on the line
740      * (i.e. only preceded by whitespace), the following tokens are specially
741      * handled:
742      * <ul>
743      * <li><code>switch</code> labels are indented relative to the switch block</li>
744      * <li>opening curly braces are aligned correctly with the introducing code</li>
745      * <li>closing curly braces are aligned properly with the introducing code of
746      * the matching opening brace</li>
747      * <li>closing parenthesis' are aligned with their opening peer</li>
748      * <li>the <code>else</code> keyword is aligned with its <code>if</code>, anything
749      * else is aligned normally (i.e. with the base of any introducing statements).</li>
750      * <li>if there is no token on the same line after <code>offset</code>, the indentation
751      * is the same as for an <code>else</code> keyword</li>
752      * </ul>
753      *
754      * @param offset the offset for which the reference is computed
755      * @param nextToken the next token to assume in the document
756      * @return the reference statement relative to which <code>offset</code>
757      * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
758      */

759     public int findReferencePosition(int offset, int nextToken) {
760         boolean danglingElse= false;
761         boolean unindent= false;
762         boolean indent= false;
763         boolean matchBrace= false;
764         boolean matchParen= false;
765         boolean matchCase= false;
766
767         // account for un-indentation characters already typed in, but after position
768
// if they are on a line by themselves, the indentation gets adjusted
769
// accordingly
770
//
771
// also account for a dangling else
772
if (offset < fDocument.getLength()) {
773             try {
774                 IRegion line= fDocument.getLineInformationOfOffset(offset);
775                 int lineOffset= line.getOffset();
776                 int prevPos= Math.max(offset - 1, 0);
777                 boolean isFirstTokenOnLine= fDocument.get(lineOffset, prevPos + 1 - lineOffset).trim().length() == 0;
778                 int prevToken= fScanner.previousToken(prevPos, JavaHeuristicScanner.UNBOUND);
779                 boolean bracelessBlockStart= fScanner.isBracelessBlockStart(prevPos, JavaHeuristicScanner.UNBOUND);
780
781                 switch (nextToken) {
782                     case Symbols.TokenELSE:
783                         danglingElse= true;
784                         break;
785                     case Symbols.TokenCASE:
786                     case Symbols.TokenDEFAULT:
787                         if (isFirstTokenOnLine)
788                             matchCase= true;
789                         break;
790                     case Symbols.TokenLBRACE: // for opening-brace-on-new-line style
791
if (bracelessBlockStart && !fPrefs.prefIndentBracesForBlocks)
792                             unindent= true;
793                         else if ((prevToken == Symbols.TokenCOLON || prevToken == Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET) && !fPrefs.prefIndentBracesForArrays)
794                             unindent= true;
795                         else if (!bracelessBlockStart && fPrefs.prefIndentBracesForMethods)
796                             indent= true;
797                         break;
798                     case Symbols.TokenRBRACE: // closing braces get unindented
799
if (isFirstTokenOnLine)
800                             matchBrace= true;
801                         break;
802                     case Symbols.TokenRPAREN:
803                         if (isFirstTokenOnLine)
804                             matchParen= true;
805                         break;
806                 }
807             } catch (BadLocationException e) {
808             }
809         } else {
810             // don't assume an else could come if we are at the end of file
811
danglingElse= false;
812         }
813
814         int ref= findReferencePosition(offset, danglingElse, matchBrace, matchParen, matchCase);
815         if (unindent)
816             fIndent--;
817         if (indent)
818             fIndent++;
819         return ref;
820     }
821
822     /**
823      * Returns the reference position regarding to indentation for <code>position</code>,
824      * or <code>NOT_FOUND</code>.<code>fIndent</code> will contain the
825      * relative indentation (in indentation units, not characters) after the
826      * call. If there is a special alignment (e.g. for a method declaration
827      * where parameters should be aligned), <code>fAlign</code> will contain
828      * the absolute position of the alignment reference in <code>fDocument</code>,
829      * otherwise <code>fAlign</code> is set to <code>JavaHeuristicScanner.NOT_FOUND</code>.
830      *
831      * @param offset the offset for which the reference is computed
832      * @param danglingElse whether a dangling else should be assumed at <code>position</code>
833      * @param matchBrace whether the position of the matching brace should be
834      * returned instead of doing code analysis
835      * @param matchParen whether the position of the matching parenthesis
836      * should be returned instead of doing code analysis
837      * @param matchCase whether the position of a switch statement reference
838      * should be returned (either an earlier case statement or the
839      * switch block brace)
840      * @return the reference statement relative to which <code>position</code>
841      * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
842      */

843     public int findReferencePosition(int offset, boolean danglingElse, boolean matchBrace, boolean matchParen, boolean matchCase) {
844         fIndent= 0; // the indentation modification
845
fAlign= JavaHeuristicScanner.NOT_FOUND;
846         fPosition= offset;
847
848         // forward cases
849
// an unindentation happens sometimes if the next token is special, namely on braces, parens and case labels
850
// align braces, but handle the case where we align with the method declaration start instead of
851
// the opening brace.
852
if (matchBrace) {
853             if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
854                 try {
855                     // align with the opening brace that is on a line by its own
856
int lineOffset= fDocument.getLineOffset(fLine);
857                     if (lineOffset <= fPosition && fDocument.get(lineOffset, fPosition - lineOffset).trim().length() == 0)
858                         return fPosition;
859                 } catch (BadLocationException e) {
860                     // concurrent modification - walk default path
861
}
862                 // if the opening brace is not on the start of the line, skip to the start
863
int pos= skipToStatementStart(true, true);
864                 fIndent= 0; // indent is aligned with reference position
865
return pos;
866             } else {
867                 // if we can't find the matching brace, the heuristic is to unindent
868
// by one against the normal position
869
int pos= findReferencePosition(offset, danglingElse, false, matchParen, matchCase);
870                 fIndent--;
871                 return pos;
872             }
873         }
874
875         // align parenthesis'
876
if (matchParen) {
877             if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN))
878                 return fPosition;
879             else {
880                 // if we can't find the matching paren, the heuristic is to unindent
881
// by one against the normal position
882
int pos= findReferencePosition(offset, danglingElse, matchBrace, false, matchCase);
883                 fIndent--;
884                 return pos;
885             }
886         }
887
888         // the only reliable way to get case labels aligned (due to many different styles of using braces in a block)
889
// is to go for another case statement, or the scope opening brace
890
if (matchCase) {
891             return matchCaseAlignment();
892         }
893
894         nextToken();
895         switch (fToken) {
896             case Symbols.TokenGREATERTHAN:
897             case Symbols.TokenRBRACE:
898                 // skip the block and fall through
899
// if we can't complete the scope, reset the scan position
900
int pos= fPosition;
901                 if (!skipScope())
902                     fPosition= pos;
903             case Symbols.TokenSEMICOLON:
904                 // this is the 90% case: after a statement block
905
// the end of the previous statement / block previous.end
906
// search to the end of the statement / block before the previous; the token just after that is previous.start
907
return skipToStatementStart(danglingElse, false);
908
909             // scope introduction: special treat who special is
910
case Symbols.TokenLPAREN:
911             case Symbols.TokenLBRACE:
912             case Symbols.TokenLBRACKET:
913                 return handleScopeIntroduction(offset + 1);
914
915             case Symbols.TokenEOF:
916                 // trap when hitting start of document
917
return JavaHeuristicScanner.NOT_FOUND;
918
919             case Symbols.TokenEQUAL:
920                 // indent assignments
921
fIndent= fPrefs.prefAssignmentIndent;
922                 return fPosition;
923
924             case Symbols.TokenCOLON:
925                 // TODO handle ternary deep indentation
926
fIndent= fPrefs.prefCaseBlockIndent;
927                 return fPosition;
928
929             case Symbols.TokenQUESTIONMARK:
930                 if (fPrefs.prefTernaryDeepAlign) {
931                     setFirstElementAlignment(fPosition, offset + 1);
932                     return fPosition;
933                 } else {
934                     fIndent= fPrefs.prefTernaryIndent;
935                     return fPosition;
936                 }
937
938             // indentation for blockless introducers:
939
case Symbols.TokenDO:
940             case Symbols.TokenWHILE:
941             case Symbols.TokenELSE:
942                 fIndent= fPrefs.prefSimpleIndent;
943                 return fPosition;
944
945             case Symbols.TokenTRY:
946                 return skipToStatementStart(danglingElse, false);
947             case Symbols.TokenRPAREN:
948                 int line= fLine;
949                 if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
950                     int scope= fPosition;
951                     nextToken();
952                     if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE || fToken == Symbols.TokenFOR) {
953                         fIndent= fPrefs.prefSimpleIndent;
954                         return fPosition;
955                     }
956                     fPosition= scope;
957                     if (looksLikeMethodDecl()) {
958                         return skipToStatementStart(danglingElse, false);
959                     }
960                     if (fToken == Symbols.TokenCATCH) {
961                         return skipToStatementStart(danglingElse, false);
962                     }
963                     fPosition= scope;
964                     if (looksLikeAnonymousTypeDecl()) {
965                         return skipToStatementStart(danglingElse, false);
966                     }
967                 }
968                 // restore
969
fPosition= offset;
970                 fLine= line;
971                 // else: fall through to default
972

973             case Symbols.TokenCOMMA:
974                 // inside a list of some type
975
// easy if there is already a list item before with its own indentation - we just align
976
// if not: take the start of the list ( LPAREN, LBRACE, LBRACKET ) and either align or
977
// indent by list-indent
978
default:
979                 // inside whatever we don't know about: similar to the list case:
980
// if we are inside a continued expression, then either align with a previous line that has indentation
981
// or indent from the expression start line (either a scope introducer or the start of the expr).
982
return skipToPreviousListItemOrListStart();
983
984         }
985     }
986
987     /**
988      * Skips to the start of a statement that ends at the current position.
989      *
990      * @param danglingElse whether to indent aligned with the last <code>if</code>
991      * @param isInBlock whether the current position is inside a block, which limits the search scope to the next scope introducer
992      * @return the reference offset of the start of the statement
993      */

994     private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
995         final int NOTHING= 0;
996         final int READ_PARENS= 1;
997         final int READ_IDENT= 2;
998         int mayBeMethodBody= NOTHING;
999         boolean isTypeBody= false;
1000        while (true) {
1001            nextToken();
1002
1003            if (isInBlock) {
1004                switch (fToken) {
1005                    // exit on all block introducers
1006
case Symbols.TokenIF:
1007                    case Symbols.TokenELSE:
1008                    case Symbols.TokenCATCH:
1009                    case Symbols.TokenDO:
1010                    case Symbols.TokenWHILE:
1011                    case Symbols.TokenFINALLY:
1012                    case Symbols.TokenFOR:
1013                    case Symbols.TokenTRY:
1014                        return fPosition;
1015
1016                    case Symbols.TokenSTATIC:
1017                        mayBeMethodBody= READ_IDENT; // treat static blocks like methods
1018
break;
1019
1020                    case Symbols.TokenSYNCHRONIZED:
1021                        // if inside a method declaration, use body indentation
1022
// else use block indentation.
1023
if (mayBeMethodBody != READ_IDENT)
1024                            return fPosition;
1025                        break;
1026
1027                    case Symbols.TokenCLASS:
1028                    case Symbols.TokenINTERFACE:
1029                    case Symbols.TokenENUM:
1030                        isTypeBody= true;
1031                        break;
1032
1033                    case Symbols.TokenSWITCH:
1034                        fIndent= fPrefs.prefCaseIndent;
1035                        return fPosition;
1036                }
1037            }
1038
1039            switch (fToken) {
1040                // scope introduction through: LPAREN, LBRACE, LBRACKET
1041
// search stop on SEMICOLON, RBRACE, COLON, EOF
1042
// -> the next token is the start of the statement (i.e. previousPos when backward scanning)
1043
case Symbols.TokenLPAREN:
1044                case Symbols.TokenLBRACE:
1045                case Symbols.TokenLBRACKET:
1046                case Symbols.TokenSEMICOLON:
1047                case Symbols.TokenEOF:
1048                    if (isInBlock)
1049                        fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
1050                    // else: fIndent set by previous calls
1051
return fPreviousPos;
1052
1053                case Symbols.TokenCOLON:
1054                    int pos= fPreviousPos;
1055                    if (!isConditional())
1056                        return pos;
1057                    break;
1058
1059                case Symbols.TokenRBRACE:
1060                    // RBRACE is a little tricky: it can be the end of an array definition, but
1061
// usually it is the end of a previous block
1062
pos= fPreviousPos; // store state
1063
if (skipScope() && looksLikeArrayInitializerIntro()) {
1064                        continue; // it's an array
1065
} else {
1066                        if (isInBlock)
1067                            fIndent= getBlockIndent(mayBeMethodBody == READ_IDENT, isTypeBody);
1068                        return pos; // it's not - do as with all the above
1069
}
1070
1071                // scopes: skip them
1072
case Symbols.TokenRPAREN:
1073                    if (isInBlock)
1074                        mayBeMethodBody= READ_PARENS;
1075                case Symbols.TokenRBRACKET:
1076                case Symbols.TokenGREATERTHAN:
1077                    pos= fPreviousPos;
1078                    if (skipScope())
1079                        break;
1080                    else
1081                        return pos;
1082
1083                // IF / ELSE: align the position after the conditional block with the if
1084
// so we are ready for an else, except if danglingElse is false
1085
// in order for this to work, we must skip an else to its if
1086
case Symbols.TokenIF:
1087                    if (danglingElse)
1088                        return fPosition;
1089                    else
1090                        break;
1091                case Symbols.TokenELSE:
1092                    // skip behind the next if, as we have that one covered
1093
pos= fPosition;
1094                    if (skipNextIF())
1095                        break;
1096                    else
1097                        return pos;
1098
1099                case Symbols.TokenDO:
1100                    // align the WHILE position with its do
1101
return fPosition;
1102
1103                case Symbols.TokenWHILE:
1104                    // this one is tricky: while can be the start of a while loop
1105
// or the end of a do - while
1106
pos= fPosition;
1107                    if (hasMatchingDo()) {
1108                        // continue searching from the DO on
1109
break;
1110                    } else {
1111                        // continue searching from the WHILE on
1112
fPosition= pos;
1113                        break;
1114                    }
1115                case Symbols.TokenIDENT:
1116                    if (mayBeMethodBody == READ_PARENS)
1117                        mayBeMethodBody= READ_IDENT;
1118                    break;
1119
1120                default:
1121                    // keep searching
1122

1123            }
1124
1125        }
1126    }
1127
1128    private int getBlockIndent(boolean isMethodBody, boolean isTypeBody) {
1129        if (isTypeBody)
1130            return fPrefs.prefTypeIndent + (fPrefs.prefIndentBracesForTypes ? 1 : 0);
1131        else if (isMethodBody)
1132            return fPrefs.prefMethodBodyIndent + (fPrefs.prefIndentBracesForMethods ? 1 : 0);
1133        else
1134            return fIndent;
1135    }
1136
1137    /**
1138     * Returns true if the colon at the current position is part of a conditional
1139     * (ternary) expression, false otherwise.
1140     *
1141     * @return true if the colon at the current position is part of a conditional
1142     */

1143    private boolean isConditional() {
1144        while (true) {
1145            nextToken();
1146            switch (fToken) {
1147
1148                // search for case labels, which consist of (possibly qualified) identifiers or numbers
1149
case Symbols.TokenIDENT:
1150                case Symbols.TokenOTHER: // dots for qualified constants
1151
continue;
1152                case Symbols.TokenCASE:
1153                    return false;
1154
1155                default:
1156                    return true;
1157            }
1158        }
1159    }
1160
1161    /**
1162     * Returns as a reference any previous <code>switch</code> labels (<code>case</code>
1163     * or <code>default</code>) or the offset of the brace that scopes the switch
1164     * statement. Sets <code>fIndent</code> to <code>prefCaseIndent</code> upon
1165     * a match.
1166     *
1167     * @return the reference offset for a <code>switch</code> label
1168     */

1169    private int matchCaseAlignment() {
1170        while (true) {
1171            nextToken();
1172            switch (fToken) {
1173                // invalid cases: another case label or an LBRACE must come before a case
1174
// -> bail out with the current position
1175
case Symbols.TokenLPAREN:
1176                case Symbols.TokenLBRACKET:
1177                case Symbols.TokenEOF:
1178                    return fPosition;
1179                case Symbols.TokenLBRACE:
1180                    // opening brace of switch statement
1181
fIndent= fPrefs.prefCaseIndent;
1182                    return fPosition;
1183                case Symbols.TokenCASE:
1184                case Symbols.TokenDEFAULT:
1185                    // align with previous label
1186
fIndent= 0;
1187                    return fPosition;
1188
1189                // scopes: skip them
1190
case Symbols.TokenRPAREN:
1191                case Symbols.TokenRBRACKET:
1192                case Symbols.TokenRBRACE:
1193                case Symbols.TokenGREATERTHAN:
1194                    skipScope();
1195                    break;
1196
1197                default:
1198                    // keep searching
1199
continue;
1200
1201            }
1202        }
1203    }
1204
1205    /**
1206     * Returns the reference position for a list element. The algorithm
1207     * tries to match any previous indentation on the same list. If there is none,
1208     * the reference position returned is determined depending on the type of list:
1209     * The indentation will either match the list scope introducer (e.g. for
1210     * method declarations), so called deep indents, or simply increase the
1211     * indentation by a number of standard indents. See also {@link #handleScopeIntroduction(int)}.
1212     *
1213     * @return the reference position for a list item: either a previous list item
1214     * that has its own indentation, or the list introduction start.
1215     */

1216    private int skipToPreviousListItemOrListStart() {
1217        int startLine= fLine;
1218        int startPosition= fPosition;
1219        while (true) {
1220            nextToken();
1221
1222            // if any line item comes with its own indentation, adapt to it
1223
if (fLine < startLine) {
1224                try {
1225                    int lineOffset= fDocument.getLineOffset(startLine);
1226                    int bound= Math.min(fDocument.getLength(), startPosition + 1);
1227                    fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(lineOffset, bound);
1228                } catch (BadLocationException e) {
1229                    // ignore and return just the position
1230
}
1231                return startPosition;
1232            }
1233
1234            switch (fToken) {
1235                // scopes: skip them
1236
case Symbols.TokenRPAREN:
1237                case Symbols.TokenRBRACKET:
1238                case Symbols.TokenRBRACE:
1239                case Symbols.TokenGREATERTHAN:
1240                    skipScope();
1241                    break;
1242
1243                // scope introduction: special treat who special is
1244
case Symbols.TokenLPAREN:
1245                case Symbols.TokenLBRACE:
1246                case Symbols.TokenLBRACKET:
1247                    return handleScopeIntroduction(startPosition + 1);
1248
1249                case Symbols.TokenSEMICOLON:
1250                    return fPosition;
1251                case Symbols.TokenQUESTIONMARK:
1252                    if (fPrefs.prefTernaryDeepAlign) {
1253                        setFirstElementAlignment(fPosition - 1, fPosition + 1);
1254                        return fPosition;
1255                    } else {
1256                        fIndent= fPrefs.prefTernaryIndent;
1257                        return fPosition;
1258                    }
1259                case Symbols.TokenEOF:
1260                    return 0;
1261
1262            }
1263        }
1264    }
1265
1266    /**
1267     * Skips a scope and positions the cursor (<code>fPosition</code>) on the
1268     * token that opens the scope. Returns <code>true</code> if a matching peer
1269     * could be found, <code>false</code> otherwise. The current token when calling
1270     * must be one out of <code>Symbols.TokenRPAREN</code>, <code>Symbols.TokenRBRACE</code>,
1271     * and <code>Symbols.TokenRBRACKET</code>.
1272     *
1273     * @return <code>true</code> if a matching peer was found, <code>false</code> otherwise
1274     */

1275    private boolean skipScope() {
1276        switch (fToken) {
1277            case Symbols.TokenRPAREN:
1278                return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
1279            case Symbols.TokenRBRACKET:
1280                return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
1281            case Symbols.TokenRBRACE:
1282                return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
1283            case Symbols.TokenGREATERTHAN:
1284                if (!fPrefs.prefHasGenerics)
1285                    return false;
1286                int storedPosition= fPosition;
1287                int storedToken= fToken;
1288                nextToken();
1289                switch (fToken) {
1290                    case Symbols.TokenIDENT:
1291                        if (!JavaHeuristicScanner.isGenericStarter(getTokenContent()))
1292                            break;
1293                    case Symbols.TokenQUESTIONMARK:
1294                    case Symbols.TokenGREATERTHAN:
1295                        if (skipScope(Symbols.TokenLESSTHAN, Symbols.TokenGREATERTHAN))
1296                            return true;
1297                }
1298                // <> are harder to detect - restore the position if we fail
1299
fPosition= storedPosition;
1300                fToken= storedToken;
1301                return false;
1302
1303            default:
1304                Assert.isTrue(false);
1305                return false;
1306        }
1307    }
1308
1309    /**
1310     * Returns the contents of the current token.
1311     *
1312     * @return the contents of the current token
1313     * @since 3.1
1314     */

1315    private CharSequence JavaDoc getTokenContent() {
1316        return new DocumentCharacterIterator(fDocument, fPosition, fPreviousPos);
1317    }
1318
1319    /**
1320     * Handles the introduction of a new scope. The current token must be one out
1321     * of <code>Symbols.TokenLPAREN</code>, <code>Symbols.TokenLBRACE</code>,
1322     * and <code>Symbols.TokenLBRACKET</code>. Returns as the reference position
1323     * either the token introducing the scope or - if available - the first
1324     * java token after that.
1325     *
1326     * <p>Depending on the type of scope introduction, the indentation will align
1327     * (deep indenting) with the reference position (<code>fAlign</code> will be
1328     * set to the reference position) or <code>fIndent</code> will be set to
1329     * the number of indentation units.
1330     * </p>
1331     *
1332     * @param bound the bound for the search for the first token after the scope
1333     * introduction.
1334     * @return the indent
1335     */

1336    private int handleScopeIntroduction(int bound) {
1337        switch (fToken) {
1338            // scope introduction: special treat who special is
1339
case Symbols.TokenLPAREN:
1340                int pos= fPosition; // store
1341

1342                // special: method declaration deep indentation
1343
if (looksLikeMethodDecl()) {
1344                    if (fPrefs.prefMethodDeclDeepIndent)
1345                        return setFirstElementAlignment(pos, bound);
1346                    else {
1347                        fIndent= fPrefs.prefMethodDeclIndent;
1348                        return pos;
1349                    }
1350                } else {
1351                    fPosition= pos;
1352                    if (looksLikeMethodCall()) {
1353                        if (fPrefs.prefMethodCallDeepIndent)
1354                            return setFirstElementAlignment(pos, bound);
1355                        else {
1356                            fIndent= fPrefs.prefMethodCallIndent;
1357                            return pos;
1358                        }
1359                    } else if (fPrefs.prefParenthesisDeepIndent)
1360                        return setFirstElementAlignment(pos, bound);
1361                }
1362
1363                // normal: return the parenthesis as reference
1364
fIndent= fPrefs.prefParenthesisIndent;
1365                return pos;
1366
1367            case Symbols.TokenLBRACE:
1368                pos= fPosition; // store
1369

1370                // special: array initializer
1371
if (looksLikeArrayInitializerIntro())
1372                    if (fPrefs.prefArrayDeepIndent)
1373                        return setFirstElementAlignment(pos, bound);
1374                    else
1375                        fIndent= fPrefs.prefArrayIndent;
1376                else
1377                    fIndent= fPrefs.prefBlockIndent;
1378
1379                // normal: skip to the statement start before the scope introducer
1380
// opening braces are often on differently ending indents than e.g. a method definition
1381
if (looksLikeArrayInitializerIntro() && !fPrefs.prefIndentBracesForArrays
1382                        || !fPrefs.prefIndentBracesForBlocks) {
1383                    fPosition= pos; // restore
1384
return skipToStatementStart(true, true); // set to true to match the first if
1385
} else {
1386                    return pos;
1387                }
1388
1389            case Symbols.TokenLBRACKET:
1390                pos= fPosition; // store
1391

1392                // special: method declaration deep indentation
1393
if (fPrefs.prefArrayDimensionsDeepIndent) {
1394                    return setFirstElementAlignment(pos, bound);
1395                }
1396
1397                // normal: return the bracket as reference
1398
fIndent= fPrefs.prefBracketIndent;
1399                return pos; // restore
1400

1401            default:
1402                Assert.isTrue(false);
1403                return -1; // dummy
1404
}
1405    }
1406
1407    /**
1408     * Sets the deep indent offset (<code>fAlign</code>) to either the offset
1409     * right after <code>scopeIntroducerOffset</code> or - if available - the
1410     * first Java token after <code>scopeIntroducerOffset</code>, but before
1411     * <code>bound</code>.
1412     *
1413     * @param scopeIntroducerOffset the offset of the scope introducer
1414     * @param bound the bound for the search for another element
1415     * @return the reference position
1416     */

1417    private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
1418        int firstPossible= scopeIntroducerOffset + 1; // align with the first position after the scope intro
1419
fAlign= fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible, bound);
1420        if (fAlign == JavaHeuristicScanner.NOT_FOUND)
1421            fAlign= firstPossible;
1422        return fAlign;
1423    }
1424
1425
1426    /**
1427     * Returns <code>true</code> if the next token received after calling
1428     * <code>nextToken</code> is either an equal sign or an array designator ('[]').
1429     *
1430     * @return <code>true</code> if the next elements look like the start of an array definition
1431     */

1432    private boolean looksLikeArrayInitializerIntro() {
1433        nextToken();
1434        if (fToken == Symbols.TokenEQUAL || skipBrackets()) {
1435            return true;
1436        }
1437        return false;
1438    }
1439
1440    /**
1441     * Skips over the next <code>if</code> keyword. The current token when calling
1442     * this method must be an <code>else</code> keyword. Returns <code>true</code>
1443     * if a matching <code>if</code> could be found, <code>false</code> otherwise.
1444     * The cursor (<code>fPosition</code>) is set to the offset of the <code>if</code>
1445     * token.
1446     *
1447     * @return <code>true</code> if a matching <code>if</code> token was found, <code>false</code> otherwise
1448     */

1449    private boolean skipNextIF() {
1450        Assert.isTrue(fToken == Symbols.TokenELSE);
1451
1452        while (true) {
1453            nextToken();
1454            switch (fToken) {
1455                // scopes: skip them
1456
case Symbols.TokenRPAREN:
1457                case Symbols.TokenRBRACKET:
1458                case Symbols.TokenRBRACE:
1459                case Symbols.TokenGREATERTHAN:
1460                    skipScope();
1461                    break;
1462
1463                case Symbols.TokenIF:
1464                    // found it, return
1465
return true;
1466                case Symbols.TokenELSE:
1467                    // recursively skip else-if blocks
1468
skipNextIF();
1469                    break;
1470
1471                // shortcut scope starts
1472
case Symbols.TokenLPAREN:
1473                case Symbols.TokenLBRACE:
1474                case Symbols.TokenLBRACKET:
1475                case Symbols.TokenEOF:
1476                    return false;
1477                }
1478        }
1479    }
1480
1481
1482    /**
1483     * while(condition); is ambiguous when parsed backwardly, as it is a valid
1484     * statement by its own, so we have to check whether there is a matching
1485     * do. A <code>do</code> can either be separated from the while by a
1486     * block, or by a single statement, which limits our search distance.
1487     *
1488     * @return <code>true</code> if the <code>while</code> currently in
1489     * <code>fToken</code> has a matching <code>do</code>.
1490     */

1491    private boolean hasMatchingDo() {
1492        Assert.isTrue(fToken == Symbols.TokenWHILE);
1493        nextToken();
1494        switch (fToken) {
1495            case Symbols.TokenRBRACE:
1496                skipScope(); // and fall thru
1497
case Symbols.TokenSEMICOLON:
1498                skipToStatementStart(false, false);
1499                return fToken == Symbols.TokenDO;
1500        }
1501        return false;
1502    }
1503
1504    /**
1505     * Skips brackets if the current token is a RBRACKET. There can be nothing
1506     * but whitespace in between, this is only to be used for <code>[]</code> elements.
1507     *
1508     * @return <code>true</code> if a <code>[]</code> could be scanned, the
1509     * current token is left at the LBRACKET.
1510     */

1511    private boolean skipBrackets() {
1512        if (fToken == Symbols.TokenRBRACKET) {
1513            nextToken();
1514            if (fToken == Symbols.TokenLBRACKET) {
1515                return true;
1516            }
1517        }
1518        return false;
1519    }
1520
1521    /**
1522     * Reads the next token in backward direction from the heuristic scanner
1523     * and sets the fields <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
1524     * accordingly.
1525     */

1526    private void nextToken() {
1527        nextToken(fPosition);
1528    }
1529
1530    /**
1531     * Reads the next token in backward direction of <code>start</code> from
1532     * the heuristic scanner and sets the fields <code>fToken, fPreviousPosition</code>
1533     * and <code>fPosition</code> accordingly.
1534     *
1535     * @param start the start offset from which to scan backwards
1536     */

1537    private void nextToken(int start) {
1538        fToken= fScanner.previousToken(start - 1, JavaHeuristicScanner.UNBOUND);
1539        fPreviousPos= start;
1540        fPosition= fScanner.getPosition() + 1;
1541        try {
1542            fLine= fDocument.getLineOfOffset(fPosition);
1543        } catch (BadLocationException e) {
1544            fLine= -1;
1545        }
1546    }
1547
1548    /**
1549     * Returns <code>true</code> if the current tokens look like a method
1550     * declaration header (i.e. only the return type and method name). The
1551     * heuristic calls <code>nextToken</code> and expects an identifier
1552     * (method name) and a type declaration (an identifier with optional
1553     * brackets) which also covers the visibility modifier of constructors; it
1554     * does not recognize package visible constructors.
1555     *
1556     * @return <code>true</code> if the current position looks like a method
1557     * declaration header.
1558     */

1559    private boolean looksLikeMethodDecl() {
1560        /*
1561         * TODO This heuristic does not recognize package private constructors
1562         * since those do have neither type nor visibility keywords.
1563         * One option would be to go over the parameter list, but that might
1564         * be empty as well, or not typed in yet - hard to do without an AST...
1565         */

1566
1567        nextToken();
1568        if (fToken == Symbols.TokenIDENT) { // method name
1569
do nextToken();
1570            while (skipBrackets()); // optional brackets for array valued return types
1571

1572            return fToken == Symbols.TokenIDENT; // return type name
1573

1574        }
1575        return false;
1576    }
1577
1578    /**
1579     * Returns <code>true</code> if the current tokens look like an anonymous type declaration
1580     * header (i.e. a type name (potentially qualified) and a new keyword). The heuristic calls
1581     * <code>nextToken</code> and expects a possibly qualified identifier (type name) and a new
1582     * keyword
1583     *
1584     * @return <code>true</code> if the current position looks like a anonymous type declaration
1585     * header.
1586     */

1587    private boolean looksLikeAnonymousTypeDecl() {
1588        
1589        nextToken();
1590        if (fToken == Symbols.TokenIDENT) { // type name
1591
nextToken();
1592            while (fToken == Symbols.TokenOTHER) { // dot of qualification
1593
nextToken();
1594                if (fToken != Symbols.TokenIDENT) // qualificating name
1595
return false;
1596                nextToken();
1597            }
1598            return fToken == Symbols.TokenNEW;
1599        }
1600        return false;
1601    }
1602
1603    /**
1604     * Returns <code>true</code> if the current tokens look like a method
1605     * call header (i.e. an identifier as opposed to a keyword taking parenthesized
1606     * parameters such as <code>if</code>).
1607     * <p>The heuristic calls <code>nextToken</code> and expects an identifier
1608     * (method name).
1609     *
1610     * @return <code>true</code> if the current position looks like a method call
1611     * header.
1612     */

1613    private boolean looksLikeMethodCall() {
1614        // TODO [5.0] add awareness for constructor calls with generic types: new ArrayList<String>()
1615
nextToken();
1616        return fToken == Symbols.TokenIDENT; // method name
1617
}
1618
1619    /**
1620     * Scans tokens for the matching opening peer. The internal cursor
1621     * (<code>fPosition</code>) is set to the offset of the opening peer if found.
1622     *
1623     * @param openToken the opening peer token
1624     * @param closeToken the closing peer token
1625     * @return <code>true</code> if a matching token was found, <code>false</code>
1626     * otherwise
1627     */

1628    private boolean skipScope(int openToken, int closeToken) {
1629
1630        int depth= 1;
1631
1632        while (true) {
1633            nextToken();
1634
1635            if (fToken == closeToken) {
1636                depth++;
1637            } else if (fToken == openToken) {
1638                depth--;
1639                if (depth == 0)
1640                    return true;
1641            } else if (fToken == Symbols.TokenEOF) {
1642                    return false;
1643            }
1644        }
1645    }
1646}
1647
Popular Tags