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