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