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