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