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