ef0c4aec3aa9d1ac75bbfecaae90bc2960a2343d
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / internal / ui / text / JavaIndenter.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.text;
12
13 import net.sourceforge.phpdt.core.JavaCore;
14 import net.sourceforge.phpdt.core.formatter.DefaultCodeFormatterConstants;
15 import net.sourceforge.phpeclipse.ui.WebUI;
16 //import net.sourceforge.phpeclipse.PHPeclipsePlugin;
17
18 import org.eclipse.core.runtime.Plugin;
19 import org.eclipse.jface.text.Assert;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.IRegion;
23 import org.eclipse.jface.text.ITypedRegion;
24 import org.eclipse.jface.text.TextUtilities;
25 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
26
27 /**
28  * Uses the {@link net.sourceforge.phpdt.internal.ui.text.JavaHeuristicScanner}to
29  * get the indentation level for a certain position in a document.
30  * 
31  * <p>
32  * An instance holds some internal position in the document and is therefore not
33  * threadsafe.
34  * </p>
35  * 
36  * @since 3.0
37  */
38 public class JavaIndenter {
39
40         /** The document being scanned. */
41         private IDocument fDocument;
42
43         /** The indentation accumulated by <code>findPreviousIndenationUnit</code>. */
44         private int fIndent;
45
46         /**
47          * The absolute (character-counted) indentation offset for special cases
48          * (method defs, array initializers)
49          */
50         private int fAlign;
51
52         /** The stateful scanposition for the indentation methods. */
53         private int fPosition;
54
55         /** The previous position. */
56         private int fPreviousPos;
57
58         /** The most recent token. */
59         private int fToken;
60
61         /** The line of <code>fPosition</code>. */
62         private int fLine;
63
64         /**
65          * The scanner we will use to scan the document. It has to be installed on
66          * the same document as the one we get.
67          */
68         private JavaHeuristicScanner fScanner;
69
70         /**
71          * Creates a new instance.
72          * 
73          * @param document
74          *            the document to scan
75          * @param scanner
76          *            the {@link JavaHeuristicScanner} to be used for scanning the
77          *            document. It must be installed on the same
78          *            <code>IDocument</code>.
79          */
80         public JavaIndenter(IDocument document, JavaHeuristicScanner scanner) {
81                 Assert.isNotNull(document);
82                 Assert.isNotNull(scanner);
83                 fDocument = document;
84                 fScanner = scanner;
85         }
86
87         /**
88          * Computes the indentation at the reference point of <code>position</code>.
89          * 
90          * @param offset
91          *            the offset in the document
92          * @return a String which reflects the indentation at the line in which the
93          *         reference position to <code>offset</code> resides, or
94          *         <code>null</code> if it cannot be determined
95          */
96         public StringBuffer getReferenceIndentation(int offset) {
97                 return getReferenceIndentation(offset, false);
98         }
99
100         /**
101          * Computes the indentation at the reference point of <code>position</code>.
102          * 
103          * @param offset
104          *            the offset in the document
105          * @param assumeOpeningBrace
106          *            <code>true</code> if an opening brace should be assumed
107          * @return a String which reflects the indentation at the line in which the
108          *         reference position to <code>offset</code> resides, or
109          *         <code>null</code> if it cannot be determined
110          */
111         private StringBuffer getReferenceIndentation(int offset,
112                         boolean assumeOpeningBrace) {
113
114                 int unit;
115                 if (assumeOpeningBrace)
116                         unit = findReferencePosition(offset, Symbols.TokenLBRACE);
117                 else
118                         unit = findReferencePosition(offset, peekChar(offset));
119
120                 // if we were unable to find anything, return null
121                 if (unit == JavaHeuristicScanner.NOT_FOUND)
122                         return null;
123
124                 return getLeadingWhitespace(unit);
125
126         }
127
128         /**
129          * Computes the indentation at <code>offset</code>.
130          * 
131          * @param offset
132          *            the offset in the document
133          * @return a String which reflects the correct indentation for the line in
134          *         which offset resides, or <code>null</code> if it cannot be
135          *         determined
136          */
137         public StringBuffer computeIndentation(int offset) {
138                 return computeIndentation(offset, false);
139         }
140
141         /**
142          * Computes the indentation at <code>offset</code>.
143          * 
144          * @param offset
145          *            the offset in the document
146          * @param assumeOpeningBrace
147          *            <code>true</code> if an opening brace should be assumed
148          * @return a String which reflects the correct indentation for the line in
149          *         which offset resides, or <code>null</code> if it cannot be
150          *         determined
151          */
152         public StringBuffer computeIndentation(int offset,
153                         boolean assumeOpeningBrace) {
154
155                 StringBuffer indent = getReferenceIndentation(offset,
156                                 assumeOpeningBrace);
157
158                 // handle special alignment
159                 if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
160                         try {
161                                 // a special case has been detected.
162                                 IRegion line = fDocument.getLineInformationOfOffset(fAlign);
163                                 int lineOffset = line.getOffset();
164                                 return createIndent(lineOffset, fAlign);
165                         } catch (BadLocationException e) {
166                                 return null;
167                         }
168                 }
169
170                 if (indent == null)
171                         return null;
172
173                 // add additional indent
174                 //indent.append(createIndent(fIndent));
175                 indent.insert(0, createIndent(fIndent));
176                 if (fIndent < 0)
177                         unindent(indent);
178
179                 return indent;
180         }
181
182         /**
183          * Returns the indentation of the line at <code>offset</code> as a
184          * <code>StringBuffer</code>. If the offset is not valid, the empty
185          * string is returned.
186          * 
187          * @param offset
188          *            the offset in the document
189          * @return the indentation (leading whitespace) of the line in which
190          *         <code>offset</code> is located
191          */
192         private StringBuffer getLeadingWhitespace(int offset) {
193                 StringBuffer indent = new StringBuffer();
194                 try {
195                         IRegion line = fDocument.getLineInformationOfOffset(offset);
196                         int lineOffset = line.getOffset();
197                         int nonWS = fScanner.findNonWhitespaceForwardInAnyPartition(
198                                         lineOffset, lineOffset + line.getLength());
199                         indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
200                         return indent;
201                 } catch (BadLocationException e) {
202                         return indent;
203                 }
204         }
205
206         /**
207          * Reduces indentation in <code>indent</code> by one indentation unit.
208          * 
209          * @param indent
210          *            the indentation to be modified
211          */
212         private void unindent(StringBuffer indent) {
213                 CharSequence oneIndent = createIndent();
214                 int i = indent.lastIndexOf(oneIndent.toString()); //$NON-NLS-1$
215                 if (i != -1) {
216                         indent.delete(i, i + oneIndent.length());
217                 }
218         }
219
220         /**
221          * Creates an indentation string of the length indent - start + 1,
222          * consisting of the content in <code>fDocument</code> in the range
223          * [start, indent), with every character replaced by a space except for
224          * tabs, which are kept as such.
225          * 
226          * <p>
227          * Every run of the number of spaces that make up a tab are replaced by a
228          * tab character.
229          * </p>
230          * 
231          * @return the indentation corresponding to the document content specified
232          *         by <code>start</code> and <code>indent</code>
233          */
234         private StringBuffer createIndent(int start, int indent) {
235                 final int tabLen = prefTabLength();
236                 StringBuffer ret = new StringBuffer();
237                 try {
238                         int spaces = 0;
239                         while (start < indent) {
240
241                                 char ch = fDocument.getChar(start);
242                                 if (ch == '\t') {
243                                         ret.append('\t');
244                                         spaces = 0;
245                                 } else if (tabLen == -1) {
246                                         ret.append(' ');
247                                 } else {
248                                         spaces++;
249                                         if (spaces == tabLen) {
250                                                 ret.append('\t');
251                                                 spaces = 0;
252                                         }
253                                 }
254
255                                 start++;
256                         }
257                         // remainder
258                         if (spaces == tabLen)
259                                 ret.append('\t');
260                         else
261                                 while (spaces-- > 0)
262                                         ret.append(' ');
263
264                 } catch (BadLocationException e) {
265                 }
266
267                 return ret;
268         }
269
270         /**
271          * Creates a string that represents the given number of indents (can be
272          * spaces or tabs..)
273          * 
274          * @param indent
275          *            the requested indentation level.
276          * 
277          * @return the indentation specified by <code>indent</code>
278          */
279         public StringBuffer createIndent(int indent) {
280                 StringBuffer oneIndent = createIndent();
281
282                 StringBuffer ret = new StringBuffer();
283                 while (indent-- > 0)
284                         ret.append(oneIndent);
285
286                 return ret;
287         }
288
289         /**
290          * Creates a string that represents one indent (can be spaces or tabs..)
291          * 
292          * @return one indentation
293          */
294         private StringBuffer createIndent() {
295                 // get a sensible default when running without the infrastructure for
296                 // testing
297                 StringBuffer oneIndent = new StringBuffer();
298                 // JavaCore plugin= JavaCore.getJavaCore();
299                 WebUI plugin = WebUI.getDefault();
300                 if (plugin == null) {
301                         oneIndent.append('\t');
302                 } else {
303                         if (JavaCore.SPACE
304                                         .equals(JavaCore
305                                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) {
306                                 int tabLen = Integer
307                                                 .parseInt(JavaCore
308                                                                 .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE));
309                                 for (int i = 0; i < tabLen; i++)
310                                         oneIndent.append(' ');
311                         } else if (JavaCore.TAB
312                                         .equals(JavaCore
313                                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
314                                 oneIndent.append('\t');
315                         else
316                                 oneIndent.append('\t'); // default
317                 }
318                 return oneIndent;
319         }
320
321         /**
322          * Returns the reference position regarding to indentation for
323          * <code>offset</code>, or <code>NOT_FOUND</code>. This method calls
324          * {@link #findReferencePosition(int, int) findReferencePosition(offset, nextChar)}
325          * where <code>nextChar</code> is the next character after
326          * <code>offset</code>.
327          * 
328          * @param offset
329          *            the offset for which the reference is computed
330          * @return the reference statement relative to which <code>offset</code>
331          *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
332          */
333         public int findReferencePosition(int offset) {
334                 return findReferencePosition(offset, peekChar(offset));
335         }
336
337         /**
338          * Peeks the next char in the document that comes after <code>offset</code>
339          * on the same line as <code>offset</code>.
340          * 
341          * @param offset
342          *            the offset into document
343          * @return the token symbol of the next element, or TokenEOF if there is
344          *         none
345          */
346         private int peekChar(int offset) {
347                 if (offset < fDocument.getLength()) {
348                         try {
349                                 IRegion line = fDocument.getLineInformationOfOffset(offset);
350                                 int lineOffset = line.getOffset();
351                                 int next = fScanner.nextToken(offset, lineOffset
352                                                 + line.getLength());
353                                 return next;
354                         } catch (BadLocationException e) {
355                         }
356                 }
357                 return Symbols.TokenEOF;
358         }
359
360         /**
361          * Returns the reference position regarding to indentation for
362          * <code>position</code>, or <code>NOT_FOUND</code>.
363          * 
364          * <p>
365          * If <code>peekNextChar</code> is <code>true</code>, the next token
366          * after <code>offset</code> is read and taken into account when computing
367          * the indentation. Currently, if the next token is the first token on the
368          * line (i.e. only preceded by whitespace), the following tokens are
369          * specially handled:
370          * <ul>
371          * <li><code>switch</code> labels are indented relative to the switch
372          * block</li>
373          * <li>opening curly braces are aligned correctly with the introducing code</li>
374          * <li>closing curly braces are aligned properly with the introducing code
375          * of the matching opening brace</li>
376          * <li>closing parenthesis' are aligned with their opening peer</li>
377          * <li>the <code>else</code> keyword is aligned with its <code>if</code>,
378          * anything else is aligned normally (i.e. with the base of any introducing
379          * statements).</li>
380          * <li>if there is no token on the same line after <code>offset</code>,
381          * the indentation is the same as for an <code>else</code> keyword</li>
382          * </ul>
383          * 
384          * @param offset
385          *            the offset for which the reference is computed
386          * @param nextToken
387          *            the next token to assume in the document
388          * @return the reference statement relative to which <code>offset</code>
389          *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
390          */
391         public int findReferencePosition(int offset, int nextToken) {
392                 boolean danglingElse = false;
393                 boolean unindent = false;
394                 boolean indent = false;
395                 boolean matchBrace = false;
396                 boolean matchParen = false;
397                 boolean matchCase = false;
398
399                 // account for unindenation characters already typed in, but after
400                 // position
401                 // if they are on a line by themselves, the indentation gets adjusted
402                 // accordingly
403                 //
404                 // also account for a dangling else
405                 if (offset < fDocument.getLength()) {
406                         try {
407                                 IRegion line = fDocument.getLineInformationOfOffset(offset);
408                                 int lineOffset = line.getOffset();
409                                 int prevPos = Math.max(offset - 1, 0);
410                                 boolean isFirstTokenOnLine = fDocument.get(lineOffset,
411                                                 prevPos + 1 - lineOffset).trim().length() == 0;
412                                 int prevToken = fScanner.previousToken(prevPos,
413                                                 JavaHeuristicScanner.UNBOUND);
414                                 if (prevToken == Symbols.TokenEOF && nextToken == Symbols.TokenEOF) {
415                                         ITypedRegion partition = TextUtilities.getPartition(fDocument, IPHPPartitions.PHP_PARTITIONING, offset, true);
416                                         if (partition.getType().equals(IPHPPartitions.PHP_SINGLELINE_COMMENT)) {
417                                                 fAlign = fScanner.getPosition();
418                                         } else {
419                                                 fAlign = JavaHeuristicScanner.NOT_FOUND;
420                                         }
421                                         return JavaHeuristicScanner.NOT_FOUND;
422                                 }
423                                 boolean bracelessBlockStart = fScanner.isBracelessBlockStart(
424                                                 prevPos, JavaHeuristicScanner.UNBOUND);
425
426                                 switch (nextToken) {
427                                 case Symbols.TokenEOF:
428                                 case Symbols.TokenELSE:
429                                         danglingElse = true;
430                                         break;
431                                 case Symbols.TokenCASE:
432                                 case Symbols.TokenDEFAULT:
433                                         if (isFirstTokenOnLine)
434                                                 matchCase = true;
435                                         break;
436                                 case Symbols.TokenLBRACE: // for opening-brace-on-new-line
437                                                                                         // style
438                                 // if (bracelessBlockStart && !prefIndentBracesForBlocks())
439                                 // unindent= true;
440                                 // else if ((prevToken == Symbols.TokenCOLON || prevToken ==
441                                 // Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET) &&
442                                 // !prefIndentBracesForArrays())
443                                 // unindent= true;
444                                 // else if (!bracelessBlockStart &&
445                                 // prefIndentBracesForMethods())
446                                 // indent= true;
447                                 // break;
448                                         if (bracelessBlockStart)
449                                                 unindent = true;
450                                         else if ((prevToken == Symbols.TokenCOLON
451                                                         || prevToken == Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET))
452                                                 unindent = true;
453                                         else if (!bracelessBlockStart)
454                                                 indent = true;
455                                         break;
456                                 case Symbols.TokenRBRACE: // closing braces get unindented
457                                         if (isFirstTokenOnLine)
458                                                 matchBrace = true;
459                                         break;
460                                 case Symbols.TokenRPAREN:
461                                         if (isFirstTokenOnLine)
462                                                 matchParen = true;
463                                         break;
464                                 }
465                         } catch (BadLocationException e) {
466                         }
467                 } else {
468                         // assume an else could come if we are at the end of file
469                         danglingElse = true;
470                 }
471
472                 int ref = findReferencePosition(offset, danglingElse, matchBrace,
473                                 matchParen, matchCase);
474                 if (unindent)
475                         fIndent--;
476                 if (indent)
477                         fIndent++;
478                 return ref;
479         }
480
481         /**
482          * Returns the reference position regarding to indentation for
483          * <code>position</code>, or <code>NOT_FOUND</code>.<code>fIndent</code>
484          * will contain the relative indentation (in indentation units, not
485          * characters) after the call. If there is a special alignment (e.g. for a
486          * method declaration where parameters should be aligned),
487          * <code>fAlign</code> will contain the absolute position of the alignment
488          * reference in <code>fDocument</code>, otherwise <code>fAlign</code>
489          * is set to <code>JavaHeuristicScanner.NOT_FOUND</code>.
490          * 
491          * @param offset
492          *            the offset for which the reference is computed
493          * @param danglingElse
494          *            whether a dangling else should be assumed at
495          *            <code>position</code>
496          * @param matchBrace
497          *            whether the position of the matching brace should be returned
498          *            instead of doing code analysis
499          * @param matchParen
500          *            whether the position of the matching parenthesis should be
501          *            returned instead of doing code analysis
502          * @param matchCase
503          *            whether the position of a switch statement reference should be
504          *            returned (either an earlier case statement or the switch block
505          *            brace)
506          * @return the reference statement relative to which <code>position</code>
507          *         should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
508          */
509         public int findReferencePosition(int offset, boolean danglingElse,
510                         boolean matchBrace, boolean matchParen, boolean matchCase) {
511                 fIndent = 0; // the indentation modification
512                 fAlign = JavaHeuristicScanner.NOT_FOUND;
513                 fPosition = offset;
514
515                 // forward cases
516                 // an unindentation happens sometimes if the next token is special,
517                 // namely on braces, parens and case labels
518                 // align braces, but handle the case where we align with the method
519                 // declaration start instead of
520                 // the opening brace.
521                 if (matchBrace) {
522                         if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
523                                 try {
524                                         // align with the opening brace that is on a line by its own
525                                         int lineOffset = fDocument.getLineOffset(fLine);
526                                         if (lineOffset <= fPosition
527                                                         && fDocument
528                                                                         .get(lineOffset, fPosition - lineOffset)
529                                                                         .trim().length() == 0)
530                                                 return fPosition;
531                                 } catch (BadLocationException e) {
532                                         // concurrent modification - walk default path
533                                 }
534                                 // if the opening brace is not on the start of the line, skip to
535                                 // the start
536                                 int pos = skipToStatementStart(true, true);
537                                 fIndent = 0; // indent is aligned with reference position
538                                 return pos;
539                         } else {
540                                 // if we can't find the matching brace, the heuristic is to
541                                 // unindent
542                                 // by one against the normal position
543                                 int pos = findReferencePosition(offset, danglingElse, false,
544                                                 matchParen, matchCase);
545                                 fIndent--;
546                                 return pos;
547                         }
548                 }
549
550                 // align parenthesis'
551                 if (matchParen) {
552                         if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN))
553                                 return fPosition;
554                         else {
555                                 // if we can't find the matching paren, the heuristic is to
556                                 // unindent
557                                 // by one against the normal position
558                                 int pos = findReferencePosition(offset, danglingElse,
559                                                 matchBrace, false, matchCase);
560                                 fIndent--;
561                                 return pos;
562                         }
563                 }
564
565                 // the only reliable way to get case labels aligned (due to many
566                 // different styles of using braces in a block)
567                 // is to go for another case statement, or the scope opening brace
568                 if (matchCase) {
569                         return matchCaseAlignment();
570                 }
571
572                 nextToken();
573                 switch (fToken) {
574                 case Symbols.TokenRBRACE:
575                         // skip the block and fall through
576                         // if we can't complete the scope, reset the scan position
577                         int pos = fPosition;
578                         if (!skipScope())
579                                 fPosition = pos;
580                 case Symbols.TokenSEMICOLON:
581                         // this is the 90% case: after a statement block
582                         // the end of the previous statement / block previous.end
583                         // search to the end of the statement / block before the previous;
584                         // the token just after that is previous.start
585                         return skipToStatementStart(danglingElse, false);
586
587                         // scope introduction: special treat who special is
588                 case Symbols.TokenLPAREN:
589                 case Symbols.TokenLBRACE:
590                 case Symbols.TokenLBRACKET:
591                         return handleScopeIntroduction(offset + 1);
592
593                 case Symbols.TokenEOF:
594                         // trap when hitting start of document
595                         return 0;
596
597                 case Symbols.TokenEQUAL:
598                         // indent assignments
599                         fIndent = prefAssignmentIndent();
600                         return fPosition;
601
602                 case Symbols.TokenCOLON:
603                         // TODO handle ternary deep indentation
604                         fIndent = prefCaseBlockIndent();
605                         return fPosition;
606
607                 case Symbols.TokenQUESTIONMARK:
608                         if (prefTernaryDeepAlign()) {
609                                 setFirstElementAlignment(fPosition, offset + 1);
610                                 return fPosition;
611                         } else {
612                                 fIndent = prefTernaryIndent();
613                                 return fPosition;
614                         }
615
616                         // indentation for blockless introducers:
617                 case Symbols.TokenDO:
618                 case Symbols.TokenWHILE:
619                 case Symbols.TokenELSE:
620                         fIndent = prefSimpleIndent();
621                         return fPosition;
622                 case Symbols.TokenRPAREN:
623                         int line = fLine;
624                         if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
625                                 int scope = fPosition;
626                                 nextToken();
627                                 if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE
628                                                 || fToken == Symbols.TokenFOR) {
629                                         fIndent = prefSimpleIndent();
630                                         return fPosition;
631                                 }
632                                 fPosition = scope;
633                                 if (looksLikeMethodDecl()) {
634                                         return skipToStatementStart(danglingElse, false);
635                                 }
636                         }
637                         // restore
638                         fPosition = offset;
639                         fLine = line;
640                         // else: fall through to default
641
642                 case Symbols.TokenCOMMA:
643                         // inside a list of some type
644                         // easy if there is already a list item before with its own
645                         // indentation - we just align
646                         // if not: take the start of the list ( LPAREN, LBRACE, LBRACKET )
647                         // and either align or
648                         // indent by list-indent
649                 default:
650                         // inside whatever we don't know about: similar to the list case:
651                         // if we are inside a continued expression, then either align with a
652                         // previous line that has indentation
653                         // or indent from the expression start line (either a scope
654                         // introducer or the start of the expr).
655                         return skipToPreviousListItemOrListStart();
656
657                 }
658         }
659
660         /**
661          * Skips to the start of a statement that ends at the current position.
662          * 
663          * @param danglingElse
664          *            whether to indent aligned with the last <code>if</code>
665          * @param isInBlock
666          *            whether the current position is inside a block, which limits
667          *            the search scope to the next scope introducer
668          * @return the reference offset of the start of the statement
669          */
670         private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
671                 while (true) {
672                         nextToken();
673
674                         if (isInBlock) {
675                                 switch (fToken) {
676                                 // exit on all block introducers
677                                 case Symbols.TokenIF:
678                                 case Symbols.TokenELSE:
679                                 case Symbols.TokenSYNCHRONIZED:
680                                 case Symbols.TokenCOLON:
681                                 case Symbols.TokenSTATIC:
682                                 case Symbols.TokenCATCH:
683                                 case Symbols.TokenDO:
684                                 case Symbols.TokenWHILE:
685                                 case Symbols.TokenFINALLY:
686                                 case Symbols.TokenFOR:
687                                 case Symbols.TokenTRY:
688                                         return fPosition;
689
690                                 case Symbols.TokenSWITCH:
691                                         fIndent = prefCaseIndent();
692                                         return fPosition;
693                                 }
694                         }
695
696                         switch (fToken) {
697                         // scope introduction through: LPAREN, LBRACE, LBRACKET
698                         // search stop on SEMICOLON, RBRACE, COLON, EOF
699                         // -> the next token is the start of the statement (i.e. previousPos
700                         // when backward scanning)
701                         case Symbols.TokenLPAREN:
702                         case Symbols.TokenLBRACE:
703                         case Symbols.TokenLBRACKET:
704                         case Symbols.TokenSEMICOLON:
705                         case Symbols.TokenEOF:
706                                 return fPreviousPos;
707
708                         case Symbols.TokenCOLON:
709                                 int pos = fPreviousPos;
710                                 if (!isConditional())
711                                         return pos;
712                                 break;
713
714                         case Symbols.TokenRBRACE:
715                                 // RBRACE is a little tricky: it can be the end of an array
716                                 // definition, but
717                                 // usually it is the end of a previous block
718                                 pos = fPreviousPos; // store state
719                                 if (skipScope() && looksLikeArrayInitializerIntro())
720                                         continue; // it's an array
721                                 else
722                                         return pos; // it's not - do as with all the above
723
724                                 // scopes: skip them
725                         case Symbols.TokenRPAREN:
726                         case Symbols.TokenRBRACKET:
727                                 pos = fPreviousPos;
728                                 if (skipScope())
729                                         break;
730                                 else
731                                         return pos;
732
733                                 // IF / ELSE: align the position after the conditional block
734                                 // with the if
735                                 // so we are ready for an else, except if danglingElse is false
736                                 // in order for this to work, we must skip an else to its if
737                         case Symbols.TokenIF:
738                                 if (danglingElse)
739                                         return fPosition;
740                                 else
741                                         break;
742                         case Symbols.TokenELSE:
743                                 // skip behind the next if, as we have that one covered
744                                 pos = fPosition;
745                                 if (skipNextIF())
746                                         break;
747                                 else
748                                         return pos;
749
750                         case Symbols.TokenDO:
751                                 // align the WHILE position with its do
752                                 return fPosition;
753
754                         case Symbols.TokenWHILE:
755                                 // this one is tricky: while can be the start of a while loop
756                                 // or the end of a do - while
757                                 pos = fPosition;
758                                 if (hasMatchingDo()) {
759                                         // continue searching from the DO on
760                                         break;
761                                 } else {
762                                         // continue searching from the WHILE on
763                                         fPosition = pos;
764                                         break;
765                                 }
766                         default:
767                                 // keep searching
768
769                         }
770
771                 }
772         }
773
774         /**
775          * Returns true if the colon at the current position is part of a
776          * conditional (ternary) expression, false otherwise.
777          * 
778          * @return true if the colon at the current position is part of a
779          *         conditional
780          */
781         private boolean isConditional() {
782                 while (true) {
783                         nextToken();
784                         switch (fToken) {
785
786                         // search for case, otherwise return true
787                         case Symbols.TokenIDENT:
788                                 continue;
789                         case Symbols.TokenCASE:
790                                 return false;
791
792                         default:
793                                 return true;
794                         }
795                 }
796         }
797
798         /**
799          * Returns as a reference any previous <code>switch</code> labels (<code>case</code>
800          * or <code>default</code>) or the offset of the brace that scopes the
801          * switch statement. Sets <code>fIndent</code> to
802          * <code>prefCaseIndent</code> upon a match.
803          * 
804          * @return the reference offset for a <code>switch</code> label
805          */
806         private int matchCaseAlignment() {
807                 while (true) {
808                         nextToken();
809                         switch (fToken) {
810                         // invalid cases: another case label or an LBRACE must come before a
811                         // case
812                         // -> bail out with the current position
813                         case Symbols.TokenLPAREN:
814                         case Symbols.TokenLBRACKET:
815                         case Symbols.TokenEOF:
816                                 return fPosition;
817                         case Symbols.TokenLBRACE:
818                                 // opening brace of switch statement
819                                 fIndent = 1; //prefCaseIndent() is for Java
820                                 return fPosition;
821                         case Symbols.TokenCASE:
822                         case Symbols.TokenDEFAULT:
823                                 // align with previous label
824                                 fIndent = 0;
825                                 return fPosition;
826                         // scopes: skip them
827                         case Symbols.TokenRPAREN:
828                         case Symbols.TokenRBRACKET:
829                         case Symbols.TokenRBRACE:
830                                 skipScope();
831                                 break;
832                         default:
833                                 // keep searching
834                                 continue;
835                         }
836                 }
837         }
838
839          /**
840          * Returns the reference position for a list element. The algorithm tries to
841          * match any previous indentation on the same list. If there is none, the
842          * reference position returned is determined depending on the type of list:
843          * The indentation will either match the list scope introducer (e.g. for
844          * method declarations), so called deep indents, or simply increase the
845          * indentation by a number of standard indents. See also
846          * {@link #handleScopeIntroduction(int)}.
847          * 
848          * @return the reference position for a list item: either a previous list
849          *         item that has its own indentation, or the list introduction
850          *         start.
851          */
852         private int skipToPreviousListItemOrListStart() {
853                 int startLine = fLine;
854                 int startPosition = fPosition;
855                 while (true) {
856                         nextToken();
857
858                         // if any line item comes with its own indentation, adapt to it
859                         if (fLine < startLine) {
860                                 try {
861                                         int lineOffset = fDocument.getLineOffset(startLine);
862                                         int bound = Math.min(fDocument.getLength(),
863                                                         startPosition + 1);
864                                         fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(
865                                                         lineOffset, bound);
866                                 } catch (BadLocationException e) {
867                                         // ignore and return just the position
868                                 }
869                                 return startPosition;
870                         }
871
872                         switch (fToken) {
873                         // scopes: skip them
874                         case Symbols.TokenRPAREN:
875                         case Symbols.TokenRBRACKET:
876                         case Symbols.TokenRBRACE:
877                                 skipScope();
878                                 break;
879
880                         // scope introduction: special treat who special is
881                         case Symbols.TokenLPAREN:
882                         case Symbols.TokenLBRACE:
883                         case Symbols.TokenLBRACKET:
884                                 return handleScopeIntroduction(startPosition + 1);
885
886                         case Symbols.TokenSEMICOLON:
887                                 return fPosition;
888                         case Symbols.TokenQUESTIONMARK:
889                                 if (prefTernaryDeepAlign()) {
890                                         setFirstElementAlignment(fPosition - 1, fPosition + 1);
891                                 } else {
892                                         fIndent = prefTernaryIndent();
893                                 }
894                                 return fPosition;
895                         case Symbols.TokenEOF:
896                                 return 0;
897
898                         case Symbols.TokenEQUAL:
899                                 // indent assignments
900                                 fIndent= prefAssignmentIndent();
901                                 return fPosition;
902                         }
903                 }
904         }
905
906         /**
907          * Skips a scope and positions the cursor (<code>fPosition</code>) on
908          * the token that opens the scope. Returns <code>true</code> if a matching
909          * peer could be found, <code>false</code> otherwise. The current token
910          * when calling must be one out of <code>Symbols.TokenRPAREN</code>,
911          * <code>Symbols.TokenRBRACE</code>, and
912          * <code>Symbols.TokenRBRACKET</code>.
913          * 
914          * @return <code>true</code> if a matching peer was found,
915          *         <code>false</code> otherwise
916          */
917         private boolean skipScope() {
918                 switch (fToken) {
919                 case Symbols.TokenRPAREN:
920                         return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
921                 case Symbols.TokenRBRACKET:
922                         return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
923                 case Symbols.TokenRBRACE:
924                         return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
925                 default:
926                         Assert.isTrue(false);
927                         return false;
928                 }
929         }
930
931         /**
932          * Handles the introduction of a new scope. The current token must be one
933          * out of <code>Symbols.TokenLPAREN</code>,
934          * <code>Symbols.TokenLBRACE</code>, and
935          * <code>Symbols.TokenLBRACKET</code>. Returns as the reference position
936          * either the token introducing the scope or - if available - the first java
937          * token after that.
938          * 
939          * <p>
940          * Depending on the type of scope introduction, the indentation will align
941          * (deep indenting) with the reference position (<code>fAlign</code> will
942          * be set to the reference position) or <code>fIndent</code> will be set
943          * to the number of indentation units.
944          * </p>
945          * 
946          * @param bound
947          *            the bound for the search for the first token after the scope
948          *            introduction.
949          * @return
950          */
951         private int handleScopeIntroduction(int bound) {
952                 switch (fToken) {
953                 // scope introduction: special treat who special is
954                 case Symbols.TokenLPAREN:
955                         int pos = fPosition; // store
956
957                         // special: method declaration deep indentation
958                         if (looksLikeMethodDecl()) {
959                                 if (prefMethodDeclDeepIndent())
960                                         return setFirstElementAlignment(pos, bound);
961                                 else {
962                                         fIndent = prefMethodDeclIndent();
963                                         return pos;
964                                 }
965                         } else {
966                                 fPosition = pos;
967                                 if (looksLikeMethodCall()) {
968                                         if (prefMethodCallDeepIndent())
969                                                 return setFirstElementAlignment(pos, bound);
970                                         else {
971                                                 fIndent = prefMethodCallIndent();
972                                                 return pos;
973                                         }
974                                 } else if (prefParenthesisDeepIndent())
975                                         return setFirstElementAlignment(pos, bound);
976                         }
977
978                         // normal: return the parenthesis as reference
979                         fIndent = prefParenthesisIndent();
980                         return pos;
981
982                 case Symbols.TokenLBRACE:
983                         pos = fPosition; // store
984
985                         // special: array initializer
986                         if (looksLikeArrayInitializerIntro())
987                                 if (prefArrayDeepIndent())
988                                         return setFirstElementAlignment(pos, bound);
989                                 else
990                                         fIndent = prefArrayIndent();
991                         else
992                                 fIndent = prefBlockIndent();
993
994                         // normal: skip to the statement start before the scope introducer
995                         // opening braces are often on differently ending indents than e.g.
996                         // a method definition
997                         fPosition = pos; // restore
998                         return skipToStatementStart(true, true); // set to true to match
999                                                                                                                 // the first if
1000
1001                 case Symbols.TokenLBRACKET:
1002                         pos = fPosition; // store
1003
1004                         // special: method declaration deep indentation
1005                         if (prefArrayDimensionsDeepIndent()) {
1006                                 return setFirstElementAlignment(pos, bound);
1007                         }
1008
1009                         // normal: return the bracket as reference
1010                         fIndent = prefBracketIndent();
1011                         return pos; // restore
1012
1013                 default:
1014                         Assert.isTrue(false);
1015                         return -1; // dummy
1016                 }
1017         }
1018
1019         /**
1020          * Sets the deep indent offset (<code>fAlign</code>) to either the
1021          * offset right after <code>scopeIntroducerOffset</code> or - if available -
1022          * the first Java token after <code>scopeIntroducerOffset</code>, but
1023          * before <code>bound</code>.
1024          * 
1025          * @param scopeIntroducerOffset
1026          *            the offset of the scope introducer
1027          * @param bound
1028          *            the bound for the search for another element
1029          * @return the reference position
1030          */
1031         private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
1032                 int firstPossible = scopeIntroducerOffset + 1; // align with the first
1033                                                                                                                 // position after the
1034                                                                                                                 // scope intro
1035                 fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible,
1036                                 bound);
1037                 if (fAlign == JavaHeuristicScanner.NOT_FOUND)
1038                         fAlign = firstPossible;
1039                 return fAlign;
1040         }
1041
1042         /**
1043          * Returns <code>true</code> if the next token received after calling
1044          * <code>nextToken</code> is either an equal sign or an array designator
1045          * ('[]').
1046          * 
1047          * @return <code>true</code> if the next elements look like the start of
1048          *         an array definition
1049          */
1050         private boolean looksLikeArrayInitializerIntro() {
1051                 nextToken();
1052                 if (fToken == Symbols.TokenEQUAL || skipBrackets()) {
1053                         return true;
1054                 }
1055                 return false;
1056         }
1057
1058         /**
1059          * Skips over the next <code>if</code> keyword. The current token when
1060          * calling this method must be an <code>else</code> keyword. Returns
1061          * <code>true</code> if a matching <code>if</code> could be found,
1062          * <code>false</code> otherwise. The cursor (<code>fPosition</code>)
1063          * is set to the offset of the <code>if</code> token.
1064          * 
1065          * @return <code>true</code> if a matching <code>if</code> token was
1066          *         found, <code>false</code> otherwise
1067          */
1068         private boolean skipNextIF() {
1069                 Assert.isTrue(fToken == Symbols.TokenELSE);
1070
1071                 while (true) {
1072                         nextToken();
1073                         switch (fToken) {
1074                         // scopes: skip them
1075                         case Symbols.TokenRPAREN:
1076                         case Symbols.TokenRBRACKET:
1077                         case Symbols.TokenRBRACE:
1078                                 skipScope();
1079                                 break;
1080
1081                         case Symbols.TokenIF:
1082                                 // found it, return
1083                                 return true;
1084                         case Symbols.TokenELSE:
1085                                 // recursively skip else-if blocks
1086                                 skipNextIF();
1087                                 break;
1088
1089                         // shortcut scope starts
1090                         case Symbols.TokenLPAREN:
1091                         case Symbols.TokenLBRACE:
1092                         case Symbols.TokenLBRACKET:
1093                         case Symbols.TokenEOF:
1094                                 return false;
1095                         }
1096                 }
1097         }
1098
1099         /**
1100          * while(condition); is ambiguous when parsed backwardly, as it is a valid
1101          * statement by its own, so we have to check whether there is a matching do.
1102          * A <code>do</code> can either be separated from the while by a block, or
1103          * by a single statement, which limits our search distance.
1104          * 
1105          * @return <code>true</code> if the <code>while</code> currently in
1106          *         <code>fToken</code> has a matching <code>do</code>.
1107          */
1108         private boolean hasMatchingDo() {
1109                 Assert.isTrue(fToken == Symbols.TokenWHILE);
1110                 nextToken();
1111                 switch (fToken) {
1112                 case Symbols.TokenRBRACE:
1113                         skipScope(); // and fall thru
1114                 case Symbols.TokenSEMICOLON:
1115                         skipToStatementStart(false, false);
1116                         return fToken == Symbols.TokenDO;
1117                 }
1118                 return false;
1119         }
1120
1121         /**
1122          * Skips brackets if the current token is a RBRACKET. There can be nothing
1123          * but whitespace in between, this is only to be used for <code>[]</code>
1124          * elements.
1125          * 
1126          * @return <code>true</code> if a <code>[]</code> could be scanned, the
1127          *         current token is left at the LBRACKET.
1128          */
1129         private boolean skipBrackets() {
1130                 if (fToken == Symbols.TokenRBRACKET) {
1131                         nextToken();
1132                         if (fToken == Symbols.TokenLBRACKET) {
1133                                 return true;
1134                         }
1135                 }
1136                 return false;
1137         }
1138
1139         /**
1140          * Reads the next token in backward direction from the heuristic scanner and
1141          * sets the fields <code>fToken, fPreviousPosition</code> and
1142          * <code>fPosition</code> accordingly.
1143          */
1144         private void nextToken() {
1145                 nextToken(fPosition);
1146         }
1147
1148         /**
1149          * Reads the next token in backward direction of <code>start</code> from
1150          * the heuristic scanner and sets the fields
1151          * <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
1152          * accordingly.
1153          */
1154         private void nextToken(int start) {
1155                 fToken = fScanner
1156                                 .previousToken(start - 1, JavaHeuristicScanner.UNBOUND);
1157                 fPreviousPos = start;
1158                 fPosition = fScanner.getPosition() + 1;
1159                 try {
1160                         fLine = fDocument.getLineOfOffset(fPosition);
1161                 } catch (BadLocationException e) {
1162                         fLine = -1;
1163                 }
1164         }
1165
1166         /**
1167          * Returns <code>true</code> if the current tokens look like a method
1168          * declaration header (i.e. only the return type and method name). The
1169          * heuristic calls <code>nextToken</code> and expects an identifier
1170          * (method name) and a type declaration (an identifier with optional
1171          * brackets) which also covers the visibility modifier of constructors; it
1172          * does not recognize package visible constructors.
1173          * 
1174          * @return <code>true</code> if the current position looks like a method
1175          *         declaration header.
1176          */
1177         private boolean looksLikeMethodDecl() {
1178                 /*
1179                  * TODO This heuristic does not recognize package private constructors
1180                  * since those do have neither type nor visibility keywords. One option
1181                  * would be to go over the parameter list, but that might be empty as
1182                  * well - hard to do without an AST...
1183                  */
1184
1185                 nextToken();
1186                 if (fToken == Symbols.TokenIDENT) { // method name
1187                         do
1188                                 nextToken();
1189                         while (skipBrackets()); // optional brackets for array valued return
1190                                                                         // types
1191                         return fToken == Symbols.TokenIDENT; // type name
1192
1193                 }
1194                 return false;
1195         }
1196
1197         /**
1198          * Returns <code>true</code> if the current tokens look like a method call
1199          * header (i.e. an identifier as opposed to a keyword taking parenthesized
1200          * parameters such as <code>if</code>).
1201          * <p>
1202          * The heuristic calls <code>nextToken</code> and expects an identifier
1203          * (method name).
1204          * 
1205          * @return <code>true</code> if the current position looks like a method
1206          *         call header.
1207          */
1208         private boolean looksLikeMethodCall() {
1209                 nextToken();
1210                 return fToken == Symbols.TokenIDENT; // method name
1211         }
1212
1213         /**
1214          * Scans tokens for the matching opening peer. The internal cursor (<code>fPosition</code>)
1215          * is set to the offset of the opening peer if found.
1216          * 
1217          * @return <code>true</code> if a matching token was found,
1218          *         <code>false</code> otherwise
1219          */
1220         private boolean skipScope(int openToken, int closeToken) {
1221
1222                 int depth = 1;
1223
1224                 while (true) {
1225                         nextToken();
1226
1227                         if (fToken == closeToken) {
1228                                 depth++;
1229                         } else if (fToken == openToken) {
1230                                 depth--;
1231                                 if (depth == 0)
1232                                         return true;
1233                         } else if (fToken == Symbols.TokenEOF) {
1234                                 return false;
1235                         }
1236                 }
1237         }
1238
1239         // TODO adjust once there are per-project settings
1240
1241         private int prefTabLength() {
1242                 int tabLen;
1243                 // JavaCore core= JavaCore.getJavaCore();
1244                 WebUI plugin = WebUI.getDefault();
1245                 // if (core != null && plugin != null)
1246                 if (plugin != null)
1247                         if (JavaCore.SPACE
1248                                         .equals(JavaCore
1249                                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
1250                                 // if the formatter uses chars to mark indentation, then don't
1251                                 // substitute any chars
1252                                 tabLen = -1; // results in no tabs being substituted for
1253                                                                 // space runs
1254                         else
1255                                 // if the formatter uses tabs to mark indentations, use the
1256                                 // visual setting from the editor
1257                                 // to get nicely aligned indentations
1258                                 tabLen = plugin
1259                                                 .getPreferenceStore()
1260                                                 .getInt(
1261                                                                 AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH);
1262                 else
1263                         tabLen = 4; // sensible default for testing
1264
1265                 return tabLen;
1266         }
1267
1268         private boolean prefArrayDimensionsDeepIndent() {
1269                 return true; // sensible default
1270         }
1271
1272         private int prefArrayIndent() {
1273                 Plugin plugin = JavaCore.getPlugin();
1274                 if (plugin != null) {
1275                         String option = JavaCore
1276                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
1277                         try {
1278                                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
1279                                         return 1;
1280                         } catch (IllegalArgumentException e) {
1281                                 // ignore and return default
1282                         }
1283                 }
1284
1285                 return prefContinuationIndent(); // default
1286         }
1287
1288         private boolean prefArrayDeepIndent() {
1289                 Plugin plugin = JavaCore.getPlugin();
1290                 if (plugin != null) {
1291                         String option = JavaCore
1292                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
1293                         try {
1294                                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
1295                         } catch (IllegalArgumentException e) {
1296                                 // ignore and return default
1297                         }
1298                 }
1299
1300                 return true;
1301         }
1302
1303         private boolean prefTernaryDeepAlign() {
1304                 Plugin plugin = JavaCore.getPlugin();
1305                 if (plugin != null) {
1306                         String option = JavaCore
1307                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
1308                         try {
1309                                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
1310                         } catch (IllegalArgumentException e) {
1311                                 // ignore and return default
1312                         }
1313                 }
1314                 return false;
1315         }
1316
1317         private int prefTernaryIndent() {
1318                 Plugin plugin = JavaCore.getPlugin();
1319                 if (plugin != null) {
1320                         String option = JavaCore
1321                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
1322                         try {
1323                                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
1324                                         return 1;
1325                                 else
1326                                         return prefContinuationIndent();
1327                         } catch (IllegalArgumentException e) {
1328                                 // ignore and return default
1329                         }
1330                 }
1331
1332                 return prefContinuationIndent();
1333         }
1334
1335         private int prefCaseIndent() {
1336                 Plugin plugin = JavaCore.getPlugin();
1337                 if (plugin != null) {
1338                         if (DefaultCodeFormatterConstants.TRUE
1339                                         .equals(JavaCore
1340                                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
1341                                 return prefBlockIndent();
1342                         else
1343                                 return 0;
1344                 }
1345
1346                 return 0; // sun standard
1347         }
1348
1349         private int prefAssignmentIndent() {
1350                 return prefBlockIndent();
1351         }
1352
1353         private int prefCaseBlockIndent() {
1354                 if (true)
1355                         return prefBlockIndent();
1356
1357                 Plugin plugin = JavaCore.getPlugin();
1358                 if (plugin != null) {
1359                         if (DefaultCodeFormatterConstants.TRUE
1360                                         .equals(JavaCore
1361                                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
1362                                 return prefBlockIndent();
1363                         else
1364                                 return 0;
1365                 }
1366                 return prefBlockIndent(); // sun standard
1367         }
1368
1369         private int prefSimpleIndent() {
1370                 return prefBlockIndent();
1371         }
1372
1373         private int prefBracketIndent() {
1374                 return prefBlockIndent();
1375         }
1376
1377         private boolean prefMethodDeclDeepIndent() {
1378                 Plugin plugin = JavaCore.getPlugin();
1379                 if (plugin != null) {
1380                         String option = JavaCore
1381                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
1382                         try {
1383                                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
1384                         } catch (IllegalArgumentException e) {
1385                                 // ignore and return default
1386                         }
1387                 }
1388
1389                 return true;
1390         }
1391
1392         private int prefMethodDeclIndent() {
1393                 Plugin plugin = JavaCore.getPlugin();
1394                 if (plugin != null) {
1395                         String option = JavaCore
1396                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
1397                         try {
1398                                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
1399                                         return 1;
1400                                 else
1401                                         return prefContinuationIndent();
1402                         } catch (IllegalArgumentException e) {
1403                                 // ignore and return default
1404                         }
1405                 }
1406                 return 1;
1407         }
1408
1409         private boolean prefMethodCallDeepIndent() {
1410                 Plugin plugin = JavaCore.getPlugin();
1411                 if (plugin != null) {
1412                         String option = JavaCore
1413                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
1414                         try {
1415                                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
1416                         } catch (IllegalArgumentException e) {
1417                                 // ignore and return default
1418                         }
1419                 }
1420                 return false; // sensible default
1421         }
1422
1423         private int prefMethodCallIndent() {
1424                 Plugin plugin = JavaCore.getPlugin();
1425                 if (plugin != null) {
1426                         String option = JavaCore
1427                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
1428                         try {
1429                                 if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
1430                                         return 1;
1431                                 else
1432                                         return prefContinuationIndent();
1433                         } catch (IllegalArgumentException e) {
1434                                 // ignore and return default
1435                         }
1436                 }
1437
1438                 return 1; // sensible default
1439         }
1440
1441         private boolean prefParenthesisDeepIndent() {
1442
1443                 if (true) // don't do parenthesis deep indentation
1444                         return false;
1445
1446                 Plugin plugin = JavaCore.getPlugin();
1447                 if (plugin != null) {
1448                         String option = JavaCore
1449                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
1450                         try {
1451                                 return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
1452                         } catch (IllegalArgumentException e) {
1453                                 // ignore and return default
1454                         }
1455                 }
1456
1457                 return false; // sensible default
1458         }
1459
1460         private int prefParenthesisIndent() {
1461                 return prefContinuationIndent();
1462         }
1463
1464         private int prefBlockIndent() {
1465                 return 1; // sensible default
1466         }
1467
1468         private boolean prefIndentBracesForBlocks() {
1469                 Plugin plugin = JavaCore.getPlugin();
1470                 if (plugin != null) {
1471                         String option = JavaCore
1472                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK);
1473                         return option
1474                                         .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
1475                 }
1476
1477                 return false; // sensible default
1478         }
1479
1480         private boolean prefIndentBracesForArrays() {
1481                 Plugin plugin = JavaCore.getPlugin();
1482                 if (plugin != null) {
1483                         String option = JavaCore
1484                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER);
1485                         return option
1486                                         .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
1487                 }
1488
1489                 return false; // sensible default
1490         }
1491
1492         private boolean prefIndentBracesForMethods() {
1493                 Plugin plugin = JavaCore.getPlugin();
1494                 if (plugin != null) {
1495                         String option = JavaCore
1496                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION);
1497                         return option
1498                                         .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
1499                 }
1500
1501                 return false; // sensible default
1502         }
1503
1504         private int prefContinuationIndent() {
1505                 Plugin plugin = JavaCore.getPlugin();
1506                 if (plugin != null) {
1507                         String option = JavaCore
1508                                         .getOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
1509                         try {
1510                                 return Integer.parseInt(option);
1511                         } catch (NumberFormatException e) {
1512                                 // ignore and return default
1513                         }
1514                 }
1515
1516                 return 2; // sensible default
1517         }
1518
1519 }