misc parser changes
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / formatter / CodeFormatter.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.formatter;
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.StringReader;
15 import java.util.Hashtable;
16 import java.util.Locale;
17 import java.util.Map;
18 import net.sourceforge.phpdt.core.ICodeFormatter;
19 import net.sourceforge.phpdt.core.compiler.CharOperation;
20 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
21 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
22 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
23 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
24 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
25 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
26 /**
27  * <h2>How to format a piece of code ?</h2>
28  * <ul>
29  * <li>Create an instance of <code>CodeFormatter</code>
30  * <li>Use the method <code>void format(aString)</code> on this instance to
31  * format <code>aString</code>. It will return the formatted string.
32  * </ul>
33  */
34 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
35   public FormatterOptions options;
36   /**
37    * Represents a block in the <code>constructions</code> stack.
38    */
39   public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
40   /**
41    * Represents a block following a control statement in the <code>constructions</code>
42    * stack.
43    */
44   public static final int NONINDENT_BLOCK = -100;
45   /**
46    * Contains the formatted output.
47    */
48   StringBuffer formattedSource;
49   /**
50    * Contains the current line. <br>
51    * Will be dumped at the next "newline"
52    */
53   StringBuffer currentLineBuffer;
54   /**
55    * Used during the formatting to get each token.
56    */
57   Scanner scanner;
58   /**
59    * Contains the tokens responsible for the current indentation level and the
60    * blocks not closed yet.
61    */
62   private int[] constructions;
63   /**
64    * Index in the <code>constructions</code> array.
65    */
66   private int constructionsCount;
67   /**
68    * Level of indentation of the current token (number of tab char put in front
69    * of it).
70    */
71   private int indentationLevel;
72   /**
73    * Regular level of indentation of all the lines
74    */
75   private int initialIndentationLevel;
76   /**
77    * Used to split a line.
78    */
79   Scanner splitScanner;
80   /**
81    * To remember the offset between the beginning of the line and the beginning
82    * of the comment.
83    */
84   int currentCommentOffset;
85   int currentLineIndentationLevel;
86   int maxLineSize = 30;
87   private boolean containsOpenCloseBraces;
88   private int indentationLevelForOpenCloseBraces;
89   /**
90    * Collections of positions to map
91    */
92   private int[] positionsToMap;
93   /**
94    * Collections of mapped positions
95    */
96   private int[] mappedPositions;
97   private int indexToMap;
98   private int indexInMap;
99   private int globalDelta;
100   private int lineDelta;
101   private int splitDelta;
102   private int beginningOfLineIndex;
103   private int multipleLineCommentCounter;
104   /**
105    * Creates a new instance of Code Formatter using the given settings.
106    * 
107    * @deprecated backport 1.0 internal functionality
108    */
109   public CodeFormatter(ConfigurableOption[] settings) {
110     this(convertConfigurableOptions(settings));
111   }
112   /**
113    * Creates a new instance of Code Formatter using the FormattingOptions
114    * object given as argument
115    * 
116    * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
117    */
118   public CodeFormatter() {
119     this((Map) null);
120   }
121   /**
122    * Creates a new instance of Code Formatter using the given settings.
123    */
124   public CodeFormatter(Map settings) {
125     // initialize internal state
126     constructionsCount = 0;
127     constructions = new int[10];
128     currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
129     currentCommentOffset = -1;
130     // initialize primary and secondary scanners
131     scanner = new Scanner(true /* comment */
132     , true /* whitespace */
133     , false /* nls */
134     , false /* assert */
135     ); // regular scanner for forming lines
136     scanner.recordLineSeparator = true;
137     // to remind of the position of the beginning of the line.
138     splitScanner = new Scanner(true /* comment */
139     , true /* whitespace */
140     , false /* nls */
141     , false /* assert */
142     );
143     // secondary scanner to split long lines formed by primary scanning
144     // initialize current line buffer
145     currentLineBuffer = new StringBuffer();
146     this.options = new FormatterOptions(settings);
147   }
148   /**
149    * Returns true if a lineSeparator has to be inserted before <code>operator</code>
150    * false otherwise.
151    */
152   private static boolean breakLineBeforeOperator(int operator) {
153     switch (operator) {
154       case TokenNameCOMMA :
155       case TokenNameSEMICOLON :
156       case TokenNameEQUAL :
157         return false;
158       default :
159         return true;
160     }
161   }
162   /**
163    * @deprecated backport 1.0 internal functionality
164    */
165   private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
166     Hashtable options = new Hashtable(10);
167     for (int i = 0; i < settings.length; i++) {
168       if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
169         String optionName = settings[i].getOptionName();
170         int valueIndex = settings[i].getCurrentValueIndex();
171         if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
172           options.put(
173               "net.sourceforge.phpdt.core.formatter.newline.openingBrace",
174               valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
175         } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
176           options.put(
177               "net.sourceforge.phpdt.core.formatter.newline.controlStatement",
178               valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
179         } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
180           options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll",
181               valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
182         } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
183           options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf",
184               valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
185         } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
186           options.put(
187               "net.sourceforge.phpdt.core.formatter.newline.emptyBlock",
188               valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
189         } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
190           options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String
191               .valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
192         } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
193           options.put("net.sourceforge.phpdt.core.formatter.style.assignment",
194               valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
195         } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
196           options.put("net.sourceforge.phpdt.core.formatter.tabulation.char",
197               valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
198         } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
199           options.put("net.sourceforge.phpdt.core.formatter.tabulation.size",
200               String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
201         }
202       }
203     }
204     return options;
205   }
206   /**
207    * Returns the end of the source code.
208    */
209   private final String copyRemainingSource() {
210     char str[] = scanner.source;
211     int startPosition = scanner.startPosition;
212     int length = str.length - startPosition;
213     StringBuffer bufr = new StringBuffer(length);
214     if (startPosition < str.length) {
215       bufr.append(str, startPosition, length);
216     }
217     return (bufr.toString());
218   }
219   /**
220    * Inserts <code>tabCount</code> tab character or their equivalent number
221    * of spaces.
222    */
223   private void dumpTab(int tabCount) {
224     if (options.indentWithTab) {
225       for (int j = 0; j < tabCount; j++) {
226         formattedSource.append('\t');
227         increaseSplitDelta(1);
228       }
229     } else {
230       for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
231         formattedSource.append(' ');
232         increaseSplitDelta(1);
233       }
234     }
235   }
236   /**
237    * Dumps <code>currentLineBuffer</code> into the formatted string.
238    */
239   private void flushBuffer() {
240     String currentString = currentLineBuffer.toString();
241     splitDelta = 0;
242     beginningOfLineIndex = formattedSource.length();
243     if (containsOpenCloseBraces) {
244       containsOpenCloseBraces = false;
245       outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0,
246           -1, null, 0);
247       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
248     } else {
249       outputLine(currentString, false, currentLineIndentationLevel, 0, -1,
250           null, 0);
251     }
252     int scannerSourceLength = scanner.source.length;
253     if (scannerSourceLength > 2) {
254       if (scanner.source[scannerSourceLength - 1] == '\n'
255           && scanner.source[scannerSourceLength - 2] == '\r') {
256         formattedSource.append(options.lineSeparatorSequence);
257         increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
258       } else if (scanner.source[scannerSourceLength - 1] == '\n') {
259         formattedSource.append(options.lineSeparatorSequence);
260         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
261       } else if (scanner.source[scannerSourceLength - 1] == '\r') {
262         formattedSource.append(options.lineSeparatorSequence);
263         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
264       }
265     }
266     updateMappedPositions(scanner.startPosition);
267   }
268   /**
269    * Formats the input string.
270    */
271   private void format() {
272     int token = 0;
273     int previousToken = 0;
274     int previousCompilableToken = 0;
275     int indentationOffset = 0;
276     int newLinesInWhitespace = 0;
277     // number of new lines in the previous whitespace token
278     // (used to leave blank lines before comments)
279     int pendingNewLines = 0;
280     boolean expectingOpenBrace = false;
281     boolean clearNonBlockIndents = false;
282     // true if all indentations till the 1st { (usefull after } or ;)
283     boolean pendingSpace = true;
284     boolean pendingNewlineAfterParen = false;
285     // true when a cr is to be put after a ) (in conditional statements)
286     boolean inAssignment = false;
287     boolean inArrayAssignment = false;
288     boolean inThrowsClause = false;
289     boolean inClassOrInterfaceHeader = false;
290     int dollarBraceCount = 0;
291     // openBracketCount is used to count the number of open brackets not closed
292     // yet.
293     int openBracketCount = 0;
294     int unarySignModifier = 0;
295     // openParenthesis[0] is used to count the parenthesis not belonging to a
296     // condition
297     // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
298     int openParenthesisCount = 1;
299     int[] openParenthesis = new int[10];
300     // tokenBeforeColon is used to know what token goes along with the current
301     // :
302     // it can be case or ?
303     int tokenBeforeColonCount = 0;
304     int[] tokenBeforeColon = new int[10];
305     constructionsCount = 0; // initializes the constructions count.
306     // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
307     int nlicsToken = 0;
308     // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
309     boolean specialElse = false;
310     // OPTION (IndentationLevel): initial indentation level may be non-zero.
311     currentLineIndentationLevel += constructionsCount;
312     // An InvalidInputException exception might cause the termination of this
313     // loop.
314     try {
315       while (true) {
316         // Get the next token. Catch invalid input and output it
317         // with minimal formatting, also catch end of input and
318         // exit the loop.
319         try {
320           token = scanner.getNextToken();
321           if (Scanner.DEBUG) {
322             int currentEndPosition = scanner.getCurrentTokenEndPosition();
323             int currentStartPosition = scanner.getCurrentTokenStartPosition();
324             System.out.print(currentStartPosition + "," + currentEndPosition
325                 + ": ");
326             System.out.println(scanner.toStringAction(token));
327           }
328           // Patch for line comment
329           // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
330           if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
331             int length = scanner.currentPosition;
332             loop : for (int index = length - 1; index >= 0; index--) {
333               switch (scanner.source[index]) {
334                 case '\r' :
335                 case '\n' :
336                   scanner.currentPosition--;
337                   break;
338                 default :
339                   break loop;
340               }
341             }
342           }
343         } catch (InvalidInputException e) {
344           if (!handleInvalidToken(e)) {
345             throw e;
346           }
347           token = 0;
348         }
349         if (token == Scanner.TokenNameEOF)
350           break;
351         /*
352          * ## MODIFYING the indentation level before generating new lines and
353          * indentation in the output string
354          */
355         // Removes all the indentations made by statements not followed by a
356         // block
357         // except if the current token is ELSE, CATCH or if we are in a
358         // switch/case
359         if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
360           switch (token) {
361             case TokenNameelse :
362               if (constructionsCount > 0
363                   && constructions[constructionsCount - 1] == TokenNameelse) {
364                 pendingNewLines = 1;
365                 specialElse = true;
366               }
367               indentationLevel += popInclusiveUntil(TokenNameif);
368               break;
369             //                                          case TokenNamecatch :
370             //                                                  indentationLevel += popInclusiveUntil(TokenNamecatch);
371             //                                                  break;
372             //                                          case TokenNamefinally :
373             //                                                  indentationLevel += popInclusiveUntil(TokenNamecatch);
374             //                                                  break;
375             case TokenNamewhile :
376               if (nlicsToken == TokenNamedo) {
377                 indentationLevel += pop(TokenNamedo);
378                 break;
379               }
380             default :
381               indentationLevel += popExclusiveUntilBlockOrCase();
382           // clear until a CASE, DEFAULT or BLOCK is encountered.
383           // Thus, the indentationLevel is correctly cleared either
384           // in a switch/case statement or in any other situation.
385           }
386           clearNonBlockIndents = false;
387         }
388         // returns to the indentation level created by the SWITCH keyword
389         // if the current token is a CASE or a DEFAULT
390         if (token == TokenNamecase || token == TokenNamedefault) {
391           indentationLevel += pop(TokenNamecase);
392         }
393         //                              if (token == Scanner.TokenNamethrows) {
394         //                                      inThrowsClause = true;
395         //                              }
396         if ((token == Scanner.TokenNameclass // || token ==
397                                              // Scanner.TokenNameinterface
398             ) && previousToken != Scanner.TokenNameDOT) {
399           inClassOrInterfaceHeader = true;
400         }
401         /*
402          * ## APPEND newlines and indentations to the output string
403          */
404         // Do not add a new line between ELSE and IF, if the option
405         // elseIfOnSameLine is true.
406         // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
407         //        if (pendingNewlineAfterParen
408         //          && previousCompilableToken == TokenNameelse
409         //          && token == TokenNameif
410         //          && options.compactElseIfMode) {
411         //          pendingNewlineAfterParen = false;
412         //          pendingNewLines = 0;
413         //          indentationLevel += pop(TokenNameelse);
414         //          // because else if is now one single statement,
415         //          // the indentation level after it is increased by one and not by 2
416         //          // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
417         //        }
418         // Add a newline & indent to the formatted source string if
419         // a for/if-else/while statement was scanned and there is no block
420         // following it.
421         pendingNewlineAfterParen = pendingNewlineAfterParen
422             || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
423         if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
424           pendingNewlineAfterParen = false;
425           // Do to add a newline & indent sequence if the current token is an
426           // open brace or a period or if the current token is a semi-colon and
427           // the
428           // previous token is a close paren.
429           // add a new line if a parenthesis belonging to a for() statement
430           // has been closed and the current token is not an opening brace
431           if (token != TokenNameLBRACE
432               && !isComment(token)
433               // to avoid adding new line between else and a comment
434               && token != TokenNameDOT
435               && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
436             newLine(1);
437             currentLineIndentationLevel = indentationLevel;
438             pendingNewLines = 0;
439             pendingSpace = false;
440           } else {
441             if (token == TokenNameLBRACE
442                 && options.newLineBeforeOpeningBraceMode) {
443               newLine(1);
444               if (constructionsCount > 0
445                   && constructions[constructionsCount - 1] != BLOCK
446                   && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
447                 currentLineIndentationLevel = indentationLevel - 1;
448               } else {
449                 currentLineIndentationLevel = indentationLevel;
450               }
451               pendingNewLines = 0;
452               pendingSpace = false;
453             }
454           }
455         }
456         if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode
457             && constructionsCount > 0
458             && constructions[constructionsCount - 1] == TokenNamedo) {
459           newLine(1);
460           currentLineIndentationLevel = indentationLevel - 1;
461           pendingNewLines = 0;
462           pendingSpace = false;
463         }
464         // see PR 1G5G8EC
465         if (token == TokenNameLBRACE && inThrowsClause) {
466           inThrowsClause = false;
467           if (options.newLineBeforeOpeningBraceMode) {
468             newLine(1);
469             currentLineIndentationLevel = indentationLevel;
470             pendingNewLines = 0;
471             pendingSpace = false;
472           }
473         }
474         // see PR 1G5G82G
475         if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
476           inClassOrInterfaceHeader = false;
477           if (options.newLineBeforeOpeningBraceMode) {
478             newLine(1);
479             currentLineIndentationLevel = indentationLevel;
480             pendingNewLines = 0;
481             pendingSpace = false;
482           }
483         }
484         // Add pending new lines to the formatted source string.
485         // Note: pending new lines are not added if the current token
486         // is a single line comment or whitespace.
487         // if the comment is between parenthesis, there is no blank line
488         // preservation
489         // (if it's a one-line comment, a blank line is added after it).
490         if (((pendingNewLines > 0 && (!isComment(token)))
491             || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
492             && token != Scanner.TokenNameWHITESPACE) {
493           // Do not add newline & indent between an adjoining close brace and
494           // close paren. Anonymous inner classes may use this form.
495           boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
496               && token == TokenNameRPAREN;
497           // OPTION (NewLineInCompoundStatement): do not add newline & indent
498           // between close brace and else, (do) while, catch, and finally if
499           // newlineInCompoundStatement is true.
500           boolean nlicsOption = previousToken == TokenNameRBRACE
501               && !options.newlineInControlStatementMode
502               && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo));
503           //                                                            || token == TokenNamecatch
504           //                                                            || token == TokenNamefinally);
505           // Do not add a newline & indent between a close brace and
506           // semi-colon.
507           boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
508               && token == TokenNameSEMICOLON;
509           // Do not add a new line & indent between a multiline comment and a
510           // opening brace
511           boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
512               && token == TokenNameLBRACE;
513           // Do not add a newline & indent between a close brace and a colon
514           // (in array assignments, for example).
515           boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
516               && token == TokenNameCOMMA;
517           // Add a newline and indent, if appropriate.
518           if (specialElse
519               || (!commentAndOpenBrace && !closeBraceAndCloseParen
520                   && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
521             // if clearAllBlankLinesMode=false, leaves the blank lines
522             // inserted by the user
523             // if clearAllBlankLinesMode=true, removes all of then
524             // and insert only blank lines required by the formatting.
525             if (!options.clearAllBlankLinesMode) {
526               //  (isComment(token))
527               pendingNewLines = (pendingNewLines < newLinesInWhitespace)
528                   ? newLinesInWhitespace
529                   : pendingNewLines;
530               pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
531             }
532             if (previousCompilableToken == TokenNameLBRACE
533                 && token == TokenNameRBRACE) {
534               containsOpenCloseBraces = true;
535               indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
536               if (isComment(previousToken)) {
537                 newLine(pendingNewLines);
538               } else {
539                 /*
540                  * if (!(constructionsCount > 1 &&
541                  * constructions[constructionsCount-1] == NONINDENT_BLOCK &&
542                  * (constructions[constructionsCount-2] == TokenNamefor
543                  */
544                 if (options.newLineInEmptyBlockMode) {
545                   if (inArrayAssignment) {
546                     newLine(1); // array assigment with an empty block
547                   } else {
548                     newLine(pendingNewLines);
549                   }
550                 }
551                 // }
552               }
553             } else {
554               // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
555               // before the ';'
556               if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
557                 newLine(pendingNewLines);
558               }
559             }
560             if (((previousCompilableToken == TokenNameSEMICOLON)
561                 || (previousCompilableToken == TokenNameLBRACE)
562                 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
563                 && (token == TokenNameRBRACE)) {
564               indentationOffset = -1;
565               indentationLevel += popExclusiveUntilBlock();
566             }
567             if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
568               // PR 1FI5IPO
569               currentLineIndentationLevel++;
570             } else {
571               currentLineIndentationLevel = indentationLevel
572                   + indentationOffset;
573             }
574             pendingSpace = false;
575             indentationOffset = 0;
576           }
577           pendingNewLines = 0;
578           newLinesInWhitespace = 0;
579           specialElse = false;
580           if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
581             nlicsToken = 0;
582           }
583         }
584         switch (token) {
585           //          case TokenNameDOLLAR :
586           //            dollarBraceCount++;
587           //            break;
588           case TokenNameelse :
589             //                          case TokenNamefinally :
590             expectingOpenBrace = true;
591             pendingNewlineAfterParen = true;
592             indentationLevel += pushControlStatement(token);
593             break;
594           case TokenNamecase :
595           case TokenNamedefault :
596             if (tokenBeforeColonCount == tokenBeforeColon.length) {
597               System.arraycopy(tokenBeforeColon, 0,
598                   (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
599                   tokenBeforeColonCount);
600             }
601             tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
602             indentationLevel += pushControlStatement(TokenNamecase);
603             break;
604           case TokenNameQUESTION :
605             if (tokenBeforeColonCount == tokenBeforeColon.length) {
606               System.arraycopy(tokenBeforeColon, 0,
607                   (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
608                   tokenBeforeColonCount);
609             }
610             tokenBeforeColon[tokenBeforeColonCount++] = token;
611             break;
612           case TokenNameswitch :
613           case TokenNamefor :
614           case TokenNameif :
615           case TokenNamewhile :
616             if (openParenthesisCount == openParenthesis.length) {
617               System.arraycopy(openParenthesis, 0,
618                   (openParenthesis = new int[openParenthesisCount * 2]), 0,
619                   openParenthesisCount);
620             }
621             openParenthesis[openParenthesisCount++] = 0;
622             expectingOpenBrace = true;
623             indentationLevel += pushControlStatement(token);
624             break;
625           //                                    case TokenNametry :
626           //                                            pendingNewlineAfterParen = true;
627           //                                    case TokenNamecatch :
628           //                                            // several CATCH statements can be contiguous.
629           //                                            // a CATCH is encountered pop until first CATCH (if a CATCH
630           // follows a TRY it works the same way,
631           //                                            // as CATCH and TRY are the same token in the stack).
632           //                                            expectingOpenBrace = true;
633           //                                            indentationLevel += pushControlStatement(TokenNamecatch);
634           //                                            break;
635           case TokenNamedo :
636             expectingOpenBrace = true;
637             indentationLevel += pushControlStatement(token);
638             nlicsToken = token;
639             break;
640           case TokenNamenew :
641             break;
642           case TokenNameLPAREN :
643             //                                          if (previousToken == TokenNamesynchronized) {
644             //                                                  indentationLevel += pushControlStatement(previousToken);
645             //                                          } else {
646             // Put a space between the previous and current token if the
647             // previous token was not a keyword, open paren, logical
648             // compliment (eg: !), semi-colon, open brace, close brace,
649             // super, or this.
650             if (previousCompilableToken != TokenNameLBRACKET
651                 && previousToken != TokenNameIdentifier && previousToken != 0
652                 && previousToken != TokenNameNOT
653                 && previousToken != TokenNameLPAREN
654                 && previousToken != TokenNameTWIDDLE
655                 && previousToken != TokenNameSEMICOLON
656                 && previousToken != TokenNameLBRACE
657                 && previousToken != TokenNameRBRACE) {
658               //                                                                && previousToken != TokenNamesuper
659               //                                                                && previousToken != TokenNamethis) {
660               space();
661             }
662             // If in a for/if/while statement, increase the parenthesis count
663             // for the current openParenthesisCount
664             // else increase the count for stand alone parenthesis.
665             if (openParenthesisCount > 0)
666               openParenthesis[openParenthesisCount - 1]++;
667             else
668               openParenthesis[0]++;
669             pendingSpace = false;
670             //S }
671             break;
672           case TokenNameRPAREN :
673             // Decrease the parenthesis count
674             // if there is no more unclosed parenthesis,
675             // a new line and indent may be append (depending on the next
676             // token).
677             if ((openParenthesisCount > 1)
678                 && (openParenthesis[openParenthesisCount - 1] > 0)) {
679               openParenthesis[openParenthesisCount - 1]--;
680               if (openParenthesis[openParenthesisCount - 1] <= 0) {
681                 pendingNewlineAfterParen = true;
682                 inAssignment = false;
683                 openParenthesisCount--;
684               }
685             } else {
686               openParenthesis[0]--;
687             }
688             pendingSpace = false;
689             break;
690           case TokenNameLBRACE :
691             if (previousCompilableToken == TokenNameDOLLAR) {
692               dollarBraceCount++;
693             }
694             if ((previousCompilableToken == TokenNameRBRACKET)
695                 || (previousCompilableToken == TokenNameEQUAL)) {
696               //                  if (previousCompilableToken == TokenNameRBRACKET) {
697               inArrayAssignment = true;
698               inAssignment = false;
699             }
700             if (inArrayAssignment) {
701               indentationLevel += pushBlock();
702             } else {
703               // Add new line and increase indentation level after open brace.
704               pendingNewLines = 1;
705               indentationLevel += pushBlock();
706             }
707             break;
708           case TokenNameRBRACE :
709             if (dollarBraceCount > 0) {
710               dollarBraceCount--;
711               break;
712             }
713             if (previousCompilableToken == TokenNameRPAREN) {
714               pendingSpace = false;
715             }
716             if (inArrayAssignment) {
717               inArrayAssignment = false;
718               pendingNewLines = 1;
719               indentationLevel += popInclusiveUntilBlock();
720             } else {
721               pendingNewLines = 1;
722               indentationLevel += popInclusiveUntilBlock();
723               if (previousCompilableToken == TokenNameRPAREN) {
724                 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
725                 // expression
726                 currentLineBuffer.append(options.lineSeparatorSequence);
727                 increaseLineDelta(options.lineSeparatorSequence.length);
728               }
729               if (constructionsCount > 0) {
730                 switch (constructions[constructionsCount - 1]) {
731                   case TokenNamefor :
732                   //indentationLevel += popExclusiveUntilBlock();
733                   //break;
734                   case TokenNameswitch :
735                   case TokenNameif :
736                   case TokenNameelse :
737                   //                                                                    case TokenNametry :
738                   //                                                                    case TokenNamecatch :
739                   //                                                                    case TokenNamefinally :
740                   case TokenNamewhile :
741                   case TokenNamedo :
742                     //                                                                  case TokenNamesynchronized :
743                     clearNonBlockIndents = true;
744                   default :
745                     break;
746                 }
747               }
748             }
749             break;
750           case TokenNameLBRACKET :
751             openBracketCount++;
752             pendingSpace = false;
753             break;
754           case TokenNameRBRACKET :
755             openBracketCount -= (openBracketCount > 0) ? 1 : 0;
756             // if there is no left bracket to close, the right bracket is
757             // ignored.
758             pendingSpace = false;
759             break;
760           case TokenNameCOMMA :
761           case TokenNameDOT :
762             pendingSpace = false;
763             break;
764           case TokenNameSEMICOLON :
765             // Do not generate line terminators in the definition of
766             // the for statement.
767             // if not in this case, jump a line and reduce indentation after
768             // the brace
769             // if the block it closes belongs to a conditional statement (if,
770             // while, do...).
771             if (openParenthesisCount <= 1) {
772               pendingNewLines = 1;
773               if (expectingOpenBrace) {
774                 clearNonBlockIndents = true;
775                 expectingOpenBrace = false;
776               }
777             }
778             inAssignment = false;
779             pendingSpace = false;
780             break;
781           case TokenNamePLUS_PLUS :
782           case TokenNameMINUS_MINUS :
783             // Do not put a space between a post-increment/decrement
784             // and the identifier being modified.
785             if (previousToken == TokenNameIdentifier
786                 || previousToken == TokenNameRBRACKET) {
787               pendingSpace = false;
788             }
789             break;
790           case TokenNamePLUS :
791           // previously ADDITION
792           case TokenNameMINUS :
793             // Handle the unary operators plus and minus via a flag
794             if (!isLiteralToken(previousToken)
795                 && previousToken != TokenNameIdentifier
796                 && previousToken != TokenNameRPAREN
797                 && previousToken != TokenNameRBRACKET) {
798               unarySignModifier = 1;
799             }
800             break;
801           case TokenNameCOLON :
802             // In a switch/case statement, add a newline & indent
803             // when a colon is encountered.
804             if (tokenBeforeColonCount > 0) {
805               if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
806                 pendingNewLines = 1;
807               }
808               tokenBeforeColonCount--;
809             }
810             break;
811           case TokenNameEQUAL :
812             inAssignment = true;
813             break;
814           case Scanner.TokenNameCOMMENT_LINE :
815             pendingNewLines = 1;
816             if (inAssignment) {
817               currentLineIndentationLevel++;
818             }
819             break; // a line is always inserted after a one-line comment
820           case Scanner.TokenNameCOMMENT_PHPDOC :
821           case Scanner.TokenNameCOMMENT_BLOCK :
822             currentCommentOffset = getCurrentCommentOffset();
823             pendingNewLines = 1;
824             break;
825           case Scanner.TokenNameWHITESPACE :
826             // Count the number of line terminators in the whitespace so
827             // line spacing can be preserved near comments.
828             char[] source = scanner.source;
829             newLinesInWhitespace = 0;
830             for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
831               if (source[i] == '\r') {
832                 if (i < max - 1) {
833                   if (source[++i] == '\n') {
834                     newLinesInWhitespace++;
835                   } else {
836                     newLinesInWhitespace++;
837                   }
838                 } else {
839                   newLinesInWhitespace++;
840                 }
841               } else if (source[i] == '\n') {
842                 newLinesInWhitespace++;
843               }
844             }
845             increaseLineDelta(scanner.startPosition - scanner.currentPosition);
846             break;
847           //          case TokenNameHTML :
848           //            // Add the next token to the formatted source string.
849           //            // outputCurrentToken(token);
850           //            int startPosition = scanner.startPosition;
851           //            flushBuffer();
852           //            for (int i = startPosition, max = scanner.currentPosition; i <
853           // max; i++) {
854           //              char currentCharacter = scanner.source[i];
855           //              updateMappedPositions(i);
856           //              currentLineBuffer.append(currentCharacter);
857           //            }
858           //            break;
859           default :
860             if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
861               //                                                        || token == TokenNamesuper
862               //                                                        || token == TokenNamethis) {
863               // Do not put a space between a unary operator
864               // (eg: ++, --, +, -) and the identifier being modified.
865               if (previousToken == TokenNamePLUS_PLUS
866                   || previousToken == TokenNameMINUS_MINUS
867                   || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
868                   || (previousToken == TokenNamePLUS && unarySignModifier > 0)
869                   || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
870                 pendingSpace = false;
871               }
872               unarySignModifier = 0;
873             }
874             break;
875         }
876         // Do not output whitespace tokens.
877         if (token != Scanner.TokenNameWHITESPACE) {
878           /*
879            * Add pending space to the formatted source string. Do not output a
880            * space under the following circumstances: 1) this is the first pass 2)
881            * previous token is an open paren 3) previous token is a period 4)
882            * previous token is the logical compliment (eg: !) 5) previous token
883            * is the bitwise compliment (eg: ~) 6) previous token is the open
884            * bracket (eg: [) 7) in an assignment statement, if the previous
885            * token is an open brace or the current token is a close brace 8)
886            * previous token is a single line comment 9) current token is a '->'
887            */
888           if (token == TokenNameMINUS_GREATER
889               && options.compactDereferencingMode)
890             pendingSpace = false;
891           boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
892               && token == TokenNameRBRACE;
893           if (pendingSpace
894               && insertSpaceAfter(previousToken)
895               && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
896               && previousToken != Scanner.TokenNameCOMMENT_LINE) {
897             if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
898                 && !openAndCloseBrace)
899               space();
900           }
901           // Add the next token to the formatted source string.
902           outputCurrentToken(token);
903           if (token == Scanner.TokenNameCOMMENT_LINE
904               && openParenthesisCount > 1) {
905             pendingNewLines = 0;
906             currentLineBuffer.append(options.lineSeparatorSequence);
907             increaseLineDelta(options.lineSeparatorSequence.length);
908           }
909           pendingSpace = true;
910         }
911         // Whitespace tokens do not need to be remembered.
912         if (token != Scanner.TokenNameWHITESPACE) {
913           previousToken = token;
914           if (token != Scanner.TokenNameCOMMENT_BLOCK
915               && token != Scanner.TokenNameCOMMENT_LINE
916               && token != Scanner.TokenNameCOMMENT_PHPDOC) {
917             previousCompilableToken = token;
918           }
919         }
920       }
921       output(copyRemainingSource());
922       flushBuffer();
923       // dump the last token of the source in the formatted output.
924     } catch (InvalidInputException e) {
925       output(copyRemainingSource());
926       flushBuffer();
927       // dump the last token of the source in the formatted output.
928     }
929   }
930   /**
931    * Formats the char array <code>sourceString</code>, and returns a string
932    * containing the formatted version.
933    * 
934    * @return the formatted ouput.
935    */
936   public String formatSourceString(String sourceString) {
937     char[] sourceChars = sourceString.toCharArray();
938     formattedSource = new StringBuffer(sourceChars.length);
939     scanner.setSource(sourceChars);
940     format();
941     return formattedSource.toString();
942   }
943   /**
944    * Formats the char array <code>sourceString</code>, and returns a string
945    * containing the formatted version.
946    * 
947    * @param string
948    *            the string to format
949    * @param indentationLevel
950    *            the initial indentation level
951    * @return the formatted ouput.
952    */
953   public String format(String string, int indentationLevel) {
954     return format(string, indentationLevel, (int[]) null);
955   }
956   /**
957    * Formats the char array <code>sourceString</code>, and returns a string
958    * containing the formatted version. The positions array is modified to
959    * contain the mapped positions.
960    * 
961    * @param string
962    *            the string to format
963    * @param indentationLevel
964    *            the initial indentation level
965    * @param positions
966    *            the array of positions to map
967    * @return the formatted ouput.
968    */
969   public String format(String string, int indentationLevel, int[] positions) {
970     return this.format(string, indentationLevel, positions, null);
971   }
972   public String format(String string, int indentationLevel, int[] positions,
973       String lineSeparator) {
974     if (lineSeparator != null) {
975       this.options.setLineSeparator(lineSeparator);
976     }
977     if (positions != null) {
978       this.setPositionsToMap(positions);
979       this.setInitialIndentationLevel(indentationLevel);
980       String formattedString = this.formatSourceString(string);
981       int[] mappedPositions = this.getMappedPositions();
982       System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
983       return formattedString;
984     } else {
985       this.setInitialIndentationLevel(indentationLevel);
986       return this.formatSourceString(string);
987     }
988   }
989   /**
990    * Formats the char array <code>sourceString</code>, and returns a string
991    * containing the formatted version. The initial indentation level is 0.
992    * 
993    * @param string
994    *            the string to format
995    * @return the formatted ouput.
996    */
997   public String format(String string) {
998     return this.format(string, 0, (int[]) null);
999   }
1000   /**
1001    * Formats a given source string, starting indenting it at a particular depth
1002    * and using the given options
1003    * 
1004    * @deprecated backport 1.0 internal functionality
1005    */
1006   public static String format(String sourceString, int initialIndentationLevel,
1007       ConfigurableOption[] options) {
1008     CodeFormatter formatter = new CodeFormatter(options);
1009     formatter.setInitialIndentationLevel(initialIndentationLevel);
1010     return formatter.formatSourceString(sourceString);
1011   }
1012   /**
1013    * Returns the number of characters and tab char between the beginning of the
1014    * line and the beginning of the comment.
1015    */
1016   private int getCurrentCommentOffset() {
1017     int linePtr = scanner.linePtr;
1018     // if there is no beginning of line, return 0.
1019     if (linePtr < 0)
1020       return 0;
1021     int offset = 0;
1022     int beginningOfLine = scanner.lineEnds[linePtr];
1023     int currentStartPosition = scanner.startPosition;
1024     char[] source = scanner.source;
1025     // find the position of the beginning of the line containing the comment
1026     while (beginningOfLine > currentStartPosition) {
1027       if (linePtr > 0) {
1028         beginningOfLine = scanner.lineEnds[--linePtr];
1029       } else {
1030         beginningOfLine = 0;
1031         break;
1032       }
1033     }
1034     for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1035       char currentCharacter = source[i];
1036       switch (currentCharacter) {
1037         case '\t' :
1038           offset += options.tabSize;
1039           break;
1040         case ' ' :
1041           offset++;
1042           break;
1043         case '\r' :
1044         case '\n' :
1045           break;
1046         default :
1047           return offset;
1048       }
1049     }
1050     return offset;
1051   }
1052   /**
1053    * Returns an array of descriptions for the configurable options. The
1054    * descriptions may be changed and passed back to a different compiler.
1055    * 
1056    * @deprecated backport 1.0 internal functionality
1057    */
1058   public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1059     String componentName = CodeFormatter.class.getName();
1060     FormatterOptions options = new FormatterOptions();
1061     return new ConfigurableOption[]{
1062         new ConfigurableOption(componentName, "newline.openingBrace", locale,
1063             options.newLineBeforeOpeningBraceMode ? 0 : 1),
1064         //$NON-NLS-1$
1065         new ConfigurableOption(componentName, "newline.controlStatement",
1066             locale, options.newlineInControlStatementMode ? 0 : 1),
1067         //$NON-NLS-1$
1068         new ConfigurableOption(componentName, "newline.clearAll", locale,
1069             options.clearAllBlankLinesMode ? 0 : 1),
1070         //$NON-NLS-1$
1071         //      new ConfigurableOption(componentName, "newline.elseIf", locale,
1072         // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1073         new ConfigurableOption(componentName, "newline.emptyBlock", locale,
1074             options.newLineInEmptyBlockMode ? 0 : 1),
1075         //$NON-NLS-1$
1076         new ConfigurableOption(componentName, "line.split", locale,
1077             options.maxLineLength),
1078         //$NON-NLS-1$
1079         new ConfigurableOption(componentName, "style.compactAssignment",
1080             locale, options.compactAssignmentMode ? 0 : 1),
1081         //$NON-NLS-1$
1082         new ConfigurableOption(componentName, "tabulation.char", locale,
1083             options.indentWithTab ? 0 : 1),
1084         //$NON-NLS-1$
1085         new ConfigurableOption(componentName, "tabulation.size", locale,
1086             options.tabSize) //$NON-NLS-1$
1087     };
1088   }
1089   /**
1090    * Returns the array of mapped positions. Returns null is no positions have
1091    * been set.
1092    * 
1093    * @return int[]
1094    * @deprecated There is no need to retrieve the mapped positions anymore.
1095    */
1096   public int[] getMappedPositions() {
1097     return mappedPositions;
1098   }
1099   /**
1100    * Returns the priority of the token given as argument <br>
1101    * The most prioritary the token is, the smallest the return value is.
1102    * 
1103    * @return the priority of <code>token</code>
1104    * @param token
1105    *            the token of which the priority is requested
1106    */
1107   private static int getTokenPriority(int token) {
1108     switch (token) {
1109       case TokenNameextends :
1110         //                      case TokenNameimplements :
1111         //                      case TokenNamethrows :
1112         return 10;
1113       case TokenNameSEMICOLON :
1114         // ;
1115         return 20;
1116       case TokenNameCOMMA :
1117         // ,
1118         return 25;
1119       case TokenNameEQUAL :
1120         // =
1121         return 30;
1122       case TokenNameAND_AND :
1123       // &&
1124       case TokenNameOR_OR :
1125         // ||
1126         return 40;
1127       case TokenNameQUESTION :
1128       // ?
1129       case TokenNameCOLON :
1130         // :
1131         return 50; // it's better cutting on ?: than on ;
1132       case TokenNameEQUAL_EQUAL :
1133       // ==
1134       case TokenNameEQUAL_EQUAL_EQUAL :
1135       // ===
1136       case TokenNameNOT_EQUAL :
1137       // !=
1138       case TokenNameNOT_EQUAL_EQUAL :
1139         // !=
1140         return 60;
1141       case TokenNameLESS :
1142       // <
1143       case TokenNameLESS_EQUAL :
1144       // <=
1145       case TokenNameGREATER :
1146       // >
1147       case TokenNameGREATER_EQUAL :
1148         // >=
1149         //                      case TokenNameinstanceof : // instanceof
1150         return 70;
1151       case TokenNamePLUS :
1152       // +
1153       case TokenNameMINUS :
1154         // -
1155         return 80;
1156       case TokenNameMULTIPLY :
1157       // *
1158       case TokenNameDIVIDE :
1159       // /
1160       case TokenNameREMAINDER :
1161         // %
1162         return 90;
1163       case TokenNameLEFT_SHIFT :
1164       // <<
1165       case TokenNameRIGHT_SHIFT :
1166         // >>
1167         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1168         return 100;
1169       case TokenNameAND :
1170       // &
1171       case TokenNameOR :
1172       // |
1173       case TokenNameXOR :
1174         // ^
1175         return 110;
1176       case TokenNameMULTIPLY_EQUAL :
1177       // *=
1178       case TokenNameDIVIDE_EQUAL :
1179       // /=
1180       case TokenNameREMAINDER_EQUAL :
1181       // %=
1182       case TokenNamePLUS_EQUAL :
1183       // +=
1184       case TokenNameMINUS_EQUAL :
1185       // -=
1186       case TokenNameLEFT_SHIFT_EQUAL :
1187       // <<=
1188       case TokenNameRIGHT_SHIFT_EQUAL :
1189       // >>=
1190       //                        case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1191       case TokenNameAND_EQUAL :
1192       // &=
1193       case TokenNameXOR_EQUAL :
1194       // ^=
1195       case TokenNameOR_EQUAL :
1196         // |=
1197         return 120;
1198       case TokenNameDOT :
1199         // .
1200         return 130;
1201       default :
1202         return Integer.MAX_VALUE;
1203     }
1204   }
1205   /**
1206    * Handles the exception raised when an invalid token is encountered. Returns
1207    * true if the exception has been handled, false otherwise.
1208    */
1209   private boolean handleInvalidToken(Exception e) {
1210     if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1211         || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1212         || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1213       return true;
1214     }
1215     return false;
1216   }
1217   private final void increaseGlobalDelta(int offset) {
1218     globalDelta += offset;
1219   }
1220   private final void increaseLineDelta(int offset) {
1221     lineDelta += offset;
1222   }
1223   private final void increaseSplitDelta(int offset) {
1224     splitDelta += offset;
1225   }
1226   /**
1227    * Returns true if a space has to be inserted after <code>operator</code>
1228    * false otherwise.
1229    */
1230   private boolean insertSpaceAfter(int token) {
1231     switch (token) {
1232       case TokenNameLPAREN :
1233       case TokenNameNOT :
1234       case TokenNameTWIDDLE :
1235       case TokenNameDOT :
1236       case 0 :
1237       // no token
1238       case TokenNameLBRACKET :
1239       case Scanner.TokenNameCOMMENT_LINE :
1240         return false;
1241       default :
1242         return true;
1243     }
1244   }
1245   /**
1246    * Returns true if a space has to be inserted before <code>operator</code>
1247    * false otherwise. <br>
1248    * Cannot be static as it uses the code formatter options (to know if the
1249    * compact assignment mode is on).
1250    */
1251   private boolean insertSpaceBefore(int token) {
1252     switch (token) {
1253       case TokenNameEQUAL :
1254         return (!options.compactAssignmentMode);
1255       default :
1256         return false;
1257     }
1258   }
1259   private static boolean isComment(int token) {
1260     boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1261         || token == Scanner.TokenNameCOMMENT_LINE
1262         || token == Scanner.TokenNameCOMMENT_PHPDOC;
1263     return result;
1264   }
1265   private static boolean isLiteralToken(int token) {
1266     boolean result = token == TokenNameIntegerLiteral
1267     //                  || token == TokenNameLongLiteral
1268         //                      || token == TokenNameFloatingPointLiteral
1269         || token == TokenNameDoubleLiteral
1270         //                      || token == TokenNameCharacterLiteral
1271         || token == TokenNameStringLiteral;
1272     return result;
1273   }
1274   /**
1275    * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1276    * it is split and the result is dumped in <code>formattedSource</code>
1277    * 
1278    * @param newLineCount
1279    *            the number of new lines to append
1280    */
1281   private void newLine(int newLineCount) {
1282     // format current line
1283     splitDelta = 0;
1284     beginningOfLineIndex = formattedSource.length();
1285     String currentLine = currentLineBuffer.toString();
1286     if (containsOpenCloseBraces) {
1287       containsOpenCloseBraces = false;
1288       outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1,
1289           null, 0);
1290       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1291     } else {
1292       outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null,
1293           0);
1294     }
1295     // dump line break(s)
1296     for (int i = 0; i < newLineCount; i++) {
1297       formattedSource.append(options.lineSeparatorSequence);
1298       increaseSplitDelta(options.lineSeparatorSequence.length);
1299     }
1300     // reset formatter for next line
1301     int currentLength = currentLine.length();
1302     currentLineBuffer = new StringBuffer(currentLength > maxLineSize
1303         ? maxLineSize = currentLength
1304         : maxLineSize);
1305     increaseGlobalDelta(splitDelta);
1306     increaseGlobalDelta(lineDelta);
1307     lineDelta = 0;
1308     currentLineIndentationLevel = initialIndentationLevel;
1309   }
1310   private String operatorString(int operator) {
1311     switch (operator) {
1312       case TokenNameextends :
1313         return "extends"; //$NON-NLS-1$
1314       //                        case TokenNameimplements :
1315       //                                return "implements"; //$NON-NLS-1$
1316       //
1317       //                        case TokenNamethrows :
1318       //                                return "throws"; //$NON-NLS-1$
1319       case TokenNameSEMICOLON :
1320         // ;
1321         return ";"; //$NON-NLS-1$
1322       case TokenNameCOMMA :
1323         // ,
1324         return ","; //$NON-NLS-1$
1325       case TokenNameEQUAL :
1326         // =
1327         return "="; //$NON-NLS-1$
1328       case TokenNameAND_AND :
1329         // && (15.22)
1330         return "&&"; //$NON-NLS-1$
1331       case TokenNameOR_OR :
1332         // || (15.23)
1333         return "||"; //$NON-NLS-1$
1334       case TokenNameQUESTION :
1335         // ? (15.24)
1336         return "?"; //$NON-NLS-1$
1337       case TokenNameCOLON :
1338         // : (15.24)
1339         return ":"; //$NON-NLS-1$
1340       case TokenNamePAAMAYIM_NEKUDOTAYIM :
1341         // : (15.24)
1342         return "::"; //$NON-NLS-1$
1343       case TokenNameEQUAL_EQUAL :
1344         // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1345         return "=="; //$NON-NLS-1$
1346       case TokenNameEQUAL_EQUAL_EQUAL :
1347         // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1348         return "==="; //$NON-NLS-1$
1349       case TokenNameEQUAL_GREATER :
1350         // -= (15.25.2)
1351         return "=>"; //$NON-NLS-1$                              
1352       case TokenNameNOT_EQUAL :
1353         // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1354         return "!="; //$NON-NLS-1$
1355       case TokenNameNOT_EQUAL_EQUAL :
1356         // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1357         return "!=="; //$NON-NLS-1$
1358       case TokenNameLESS :
1359         // < (15.19.1)
1360         return "<"; //$NON-NLS-1$
1361       case TokenNameLESS_EQUAL :
1362         // <= (15.19.1)
1363         return "<="; //$NON-NLS-1$
1364       case TokenNameGREATER :
1365         // > (15.19.1)
1366         return ">"; //$NON-NLS-1$
1367       case TokenNameGREATER_EQUAL :
1368         // >= (15.19.1)
1369         return ">="; //$NON-NLS-1$
1370       //                        case TokenNameinstanceof : // instanceof
1371       //                                return "instanceof"; //$NON-NLS-1$
1372       case TokenNamePLUS :
1373         // + (15.17, 15.17.2)
1374         return "+"; //$NON-NLS-1$
1375       case TokenNameMINUS :
1376         // - (15.17.2)
1377         return "-"; //$NON-NLS-1$
1378       case TokenNameMULTIPLY :
1379         // * (15.16.1)
1380         return "*"; //$NON-NLS-1$
1381       case TokenNameDIVIDE :
1382         // / (15.16.2)
1383         return "/"; //$NON-NLS-1$
1384       case TokenNameREMAINDER :
1385         // % (15.16.3)
1386         return "%"; //$NON-NLS-1$
1387       case TokenNameLEFT_SHIFT :
1388         // << (15.18)
1389         return "<<"; //$NON-NLS-1$
1390       case TokenNameRIGHT_SHIFT :
1391         // >> (15.18)
1392         return ">>"; //$NON-NLS-1$
1393       //                        case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1394       //                                return ">>>"; //$NON-NLS-1$
1395       case TokenNameAND :
1396         // & (15.21, 15.21.1, 15.21.2)
1397         return "&"; //$NON-NLS-1$
1398       case TokenNameOR :
1399         // | (15.21, 15.21.1, 15.21.2)
1400         return "|"; //$NON-NLS-1$
1401       case TokenNameXOR :
1402         // ^ (15.21, 15.21.1, 15.21.2)
1403         return "^"; //$NON-NLS-1$
1404       case TokenNameMULTIPLY_EQUAL :
1405         // *= (15.25.2)
1406         return "*="; //$NON-NLS-1$
1407       case TokenNameDIVIDE_EQUAL :
1408         // /= (15.25.2)
1409         return "/="; //$NON-NLS-1$
1410       case TokenNameREMAINDER_EQUAL :
1411         // %= (15.25.2)
1412         return "%="; //$NON-NLS-1$
1413       case TokenNamePLUS_EQUAL :
1414         // += (15.25.2)
1415         return "+="; //$NON-NLS-1$
1416       case TokenNameMINUS_EQUAL :
1417         // -= (15.25.2)
1418         return "-="; //$NON-NLS-1$
1419       case TokenNameMINUS_GREATER :
1420         // -= (15.25.2)
1421         return "->"; //$NON-NLS-1$
1422       case TokenNameLEFT_SHIFT_EQUAL :
1423         // <<= (15.25.2)
1424         return "<<="; //$NON-NLS-1$
1425       case TokenNameRIGHT_SHIFT_EQUAL :
1426         // >>= (15.25.2)
1427         return ">>="; //$NON-NLS-1$
1428       //                        case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1429       //                                return ">>>="; //$NON-NLS-1$
1430       case TokenNameAND_EQUAL :
1431         // &= (15.25.2)
1432         return "&="; //$NON-NLS-1$
1433       case TokenNameXOR_EQUAL :
1434         // ^= (15.25.2)
1435         return "^="; //$NON-NLS-1$
1436       case TokenNameOR_EQUAL :
1437         // |= (15.25.2)
1438         return "|="; //$NON-NLS-1$
1439       case TokenNameDOT :
1440         // .
1441         return "."; //$NON-NLS-1$
1442       default :
1443         return ""; //$NON-NLS-1$
1444     }
1445   }
1446   /**
1447    * Appends <code>stringToOutput</code> to the formatted output. <br>
1448    * If it contains \n, append a LINE_SEPARATOR and indent after it.
1449    */
1450   private void output(String stringToOutput) {
1451     char currentCharacter;
1452     for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1453       currentCharacter = stringToOutput.charAt(i);
1454       if (currentCharacter != '\t') {
1455         currentLineBuffer.append(currentCharacter);
1456       }
1457     }
1458   }
1459   /**
1460    * Appends <code>token</code> to the formatted output. <br>
1461    * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1462    * after it.
1463    */
1464   private void outputCurrentToken(int token) {
1465     char[] source = scanner.source;
1466     int startPosition = scanner.startPosition;
1467     switch (token) {
1468       case Scanner.TokenNameCOMMENT_PHPDOC :
1469       case Scanner.TokenNameCOMMENT_BLOCK :
1470       case Scanner.TokenNameCOMMENT_LINE :
1471         boolean endOfLine = false;
1472         int currentCommentOffset = getCurrentCommentOffset();
1473         int beginningOfLineSpaces = 0;
1474         endOfLine = false;
1475         currentCommentOffset = getCurrentCommentOffset();
1476         beginningOfLineSpaces = 0;
1477         boolean pendingCarriageReturn = false;
1478         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1479           char currentCharacter = source[i];
1480           updateMappedPositions(i);
1481           switch (currentCharacter) {
1482             case '\r' :
1483               pendingCarriageReturn = true;
1484               endOfLine = true;
1485               break;
1486             case '\n' :
1487               if (pendingCarriageReturn) {
1488                 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1489               } else {
1490                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1491               }
1492               pendingCarriageReturn = false;
1493               currentLineBuffer.append(options.lineSeparatorSequence);
1494               beginningOfLineSpaces = 0;
1495               endOfLine = true;
1496               break;
1497             case '\t' :
1498               if (pendingCarriageReturn) {
1499                 pendingCarriageReturn = false;
1500                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1501                 currentLineBuffer.append(options.lineSeparatorSequence);
1502                 beginningOfLineSpaces = 0;
1503                 endOfLine = true;
1504               }
1505               if (endOfLine) {
1506                 // we remove a maximum of currentCommentOffset characters (tabs
1507                 // are converted to space numbers).
1508                 beginningOfLineSpaces += options.tabSize;
1509                 if (beginningOfLineSpaces > currentCommentOffset) {
1510                   currentLineBuffer.append(currentCharacter);
1511                 } else {
1512                   increaseGlobalDelta(-1);
1513                 }
1514               } else {
1515                 currentLineBuffer.append(currentCharacter);
1516               }
1517               break;
1518             case ' ' :
1519               if (pendingCarriageReturn) {
1520                 pendingCarriageReturn = false;
1521                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1522                 currentLineBuffer.append(options.lineSeparatorSequence);
1523                 beginningOfLineSpaces = 0;
1524                 endOfLine = true;
1525               }
1526               if (endOfLine) {
1527                 // we remove a maximum of currentCommentOffset characters (tabs
1528                 // are converted to space numbers).
1529                 beginningOfLineSpaces++;
1530                 if (beginningOfLineSpaces > currentCommentOffset) {
1531                   currentLineBuffer.append(currentCharacter);
1532                 } else {
1533                   increaseGlobalDelta(-1);
1534                 }
1535               } else {
1536                 currentLineBuffer.append(currentCharacter);
1537               }
1538               break;
1539             default :
1540               if (pendingCarriageReturn) {
1541                 pendingCarriageReturn = false;
1542                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1543                 currentLineBuffer.append(options.lineSeparatorSequence);
1544                 beginningOfLineSpaces = 0;
1545                 endOfLine = true;
1546               } else {
1547                 beginningOfLineSpaces = 0;
1548                 currentLineBuffer.append(currentCharacter);
1549                 endOfLine = false;
1550               }
1551           }
1552         }
1553         updateMappedPositions(scanner.currentPosition - 1);
1554         multipleLineCommentCounter++;
1555         break;
1556       default :
1557         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1558           char currentCharacter = source[i];
1559           updateMappedPositions(i);
1560           currentLineBuffer.append(currentCharacter);
1561         }
1562     }
1563   }
1564   /**
1565    * Outputs <code>currentString</code>:<br>
1566    * <ul>
1567    * <li>If its length is < maxLineLength, output
1568    * <li>Otherwise it is split.
1569    * </ul>
1570    * 
1571    * @param currentString
1572    *            string to output
1573    * @param preIndented
1574    *            whether the string to output was pre-indented
1575    * @param depth
1576    *            number of indentation to put in front of <code>currentString</code>
1577    * @param operator
1578    *            value of the operator belonging to <code>currentString</code>.
1579    */
1580   private void outputLine(String currentString, boolean preIndented, int depth,
1581       int operator, int substringIndex, int[] startSubstringIndexes,
1582       int offsetInGlobalLine) {
1583     boolean emptyFirstSubString = false;
1584     String operatorString = operatorString(operator);
1585     boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1586     boolean placeOperatorAhead = !placeOperatorBehind;
1587     // dump prefix operator?
1588     if (placeOperatorAhead) {
1589       if (!preIndented) {
1590         dumpTab(depth);
1591         preIndented = true;
1592       }
1593       if (operator != 0) {
1594         if (insertSpaceBefore(operator)) {
1595           formattedSource.append(' ');
1596           increaseSplitDelta(1);
1597         }
1598         formattedSource.append(operatorString);
1599         increaseSplitDelta(operatorString.length());
1600         if (insertSpaceAfter(operator) //                       && operator != TokenNameimplements
1601             && operator != TokenNameextends) {
1602           //                    && operator != TokenNamethrows) {
1603           formattedSource.append(' ');
1604           increaseSplitDelta(1);
1605         }
1606       }
1607     }
1608     SplitLine splitLine = null;
1609     if (options.maxLineLength == 0
1610         || getLength(currentString, depth) < options.maxLineLength
1611         || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1612       // depending on the type of operator, outputs new line before of after
1613       // dumping it
1614       // indent before postfix operator
1615       // indent also when the line cannot be split
1616       if (operator == TokenNameextends) {
1617         //                              || operator == TokenNameimplements
1618         //                              || operator == TokenNamethrows) {
1619         formattedSource.append(' ');
1620         increaseSplitDelta(1);
1621       }
1622       if (placeOperatorBehind) {
1623         if (!preIndented) {
1624           dumpTab(depth);
1625         }
1626       }
1627       int max = currentString.length();
1628       if (multipleLineCommentCounter != 0) {
1629         try {
1630           BufferedReader reader = new BufferedReader(new StringReader(
1631               currentString));
1632           String line = reader.readLine();
1633           while (line != null) {
1634             updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1635                 beginningOfLineIndex + line.length()
1636                     + options.lineSeparatorSequence.length);
1637             formattedSource.append(line);
1638             beginningOfLineIndex = beginningOfLineIndex + line.length();
1639             if ((line = reader.readLine()) != null) {
1640               formattedSource.append(options.lineSeparatorSequence);
1641               beginningOfLineIndex += options.lineSeparatorSequence.length;
1642               dumpTab(currentLineIndentationLevel);
1643             }
1644           }
1645           reader.close();
1646         } catch (IOException e) {
1647           e.printStackTrace();
1648         }
1649       } else {
1650         updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1651             beginningOfLineIndex + max);
1652         for (int i = 0; i < max; i++) {
1653           char currentChar = currentString.charAt(i);
1654           switch (currentChar) {
1655             case '\r' :
1656               break;
1657             case '\n' :
1658               if (i != max - 1) {
1659                 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1660                 // split with a comment inside a condition
1661                 // a substring cannot end with a lineSeparatorSequence,
1662                 // except if it has been added by format() after a one-line
1663                 // comment
1664                 formattedSource.append(options.lineSeparatorSequence);
1665                 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1666                 dumpTab(depth - 1);
1667               }
1668               break;
1669             default :
1670               formattedSource.append(currentChar);
1671           }
1672         }
1673       }
1674       // update positions inside the mappedPositions table
1675       if (substringIndex != -1) {
1676         if (multipleLineCommentCounter == 0) {
1677           int startPosition = beginningOfLineIndex
1678               + startSubstringIndexes[substringIndex];
1679           updateMappedPositionsWhileSplitting(startPosition, startPosition
1680               + max);
1681         }
1682         // compute the splitDelta resulting with the operator and blank removal
1683         if (substringIndex + 1 != startSubstringIndexes.length) {
1684           increaseSplitDelta(startSubstringIndexes[substringIndex] + max
1685               - startSubstringIndexes[substringIndex + 1]);
1686         }
1687       }
1688       // dump postfix operator?
1689       if (placeOperatorBehind) {
1690         if (insertSpaceBefore(operator)) {
1691           formattedSource.append(' ');
1692           if (operator != 0) {
1693             increaseSplitDelta(1);
1694           }
1695         }
1696         formattedSource.append(operatorString);
1697         if (operator != 0) {
1698           increaseSplitDelta(operatorString.length());
1699         }
1700       }
1701       return;
1702     }
1703     // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1704     // extends has to stand alone on a line when currentString has been split.
1705     if (options.maxLineLength != 0 && splitLine != null
1706         && (operator == TokenNameextends)) {
1707       //                                || operator == TokenNameimplements
1708       //                                || operator == TokenNamethrows)) {
1709       formattedSource.append(options.lineSeparatorSequence);
1710       increaseSplitDelta(options.lineSeparatorSequence.length);
1711       dumpTab(depth + 1);
1712     } else {
1713       if (operator == TokenNameextends) {
1714         //                              || operator == TokenNameimplements
1715         //                              || operator == TokenNamethrows) {
1716         formattedSource.append(' ');
1717         increaseSplitDelta(1);
1718       }
1719     }
1720     // perform actual splitting
1721     String result[] = splitLine.substrings;
1722     int[] splitOperators = splitLine.operators;
1723     if (result[0].length() == 0) {
1724       // when the substring 0 is null, the substring 1 is correctly indented.
1725       depth--;
1726       emptyFirstSubString = true;
1727     }
1728     // the operator going in front of the result[0] string is the operator
1729     // parameter
1730     for (int i = 0, max = result.length; i < max; i++) {
1731       // the new depth is the current one if this is the first substring,
1732       // the current one + 1 otherwise.
1733       // if the substring is a comment, use the current indentation Level
1734       // instead of the depth
1735       // (-1 because the ouputline increases depth).
1736       // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1737       // comments)
1738       String currentResult = result[i];
1739       if (currentResult.length() != 0 || splitOperators[i] != 0) {
1740         int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1741         || currentResult.startsWith("//")) //$NON-NLS-1$ 
1742             ? indentationLevel - 1 : depth;
1743         outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString)
1744             ? preIndented
1745             : false, i == 0 ? newDepth : newDepth + 1, splitOperators[i], i,
1746             splitLine.startSubstringsIndexes, currentString
1747                 .indexOf(currentResult));
1748         if (i != max - 1) {
1749           formattedSource.append(options.lineSeparatorSequence);
1750           increaseSplitDelta(options.lineSeparatorSequence.length);
1751         }
1752       }
1753     }
1754     if (result.length == splitOperators.length - 1) {
1755       int lastOperator = splitOperators[result.length];
1756       String lastOperatorString = operatorString(lastOperator);
1757       formattedSource.append(options.lineSeparatorSequence);
1758       increaseSplitDelta(options.lineSeparatorSequence.length);
1759       if (breakLineBeforeOperator(lastOperator)) {
1760         dumpTab(depth + 1);
1761         if (lastOperator != 0) {
1762           if (insertSpaceBefore(lastOperator)) {
1763             formattedSource.append(' ');
1764             increaseSplitDelta(1);
1765           }
1766           formattedSource.append(lastOperatorString);
1767           increaseSplitDelta(lastOperatorString.length());
1768           if (insertSpaceAfter(lastOperator) //                                 && lastOperator !=
1769                                              // TokenNameimplements
1770               && lastOperator != TokenNameextends) {
1771             //                                  && lastOperator != TokenNamethrows) {
1772             formattedSource.append(' ');
1773             increaseSplitDelta(1);
1774           }
1775         }
1776       }
1777     }
1778     if (placeOperatorBehind) {
1779       if (insertSpaceBefore(operator)) {
1780         formattedSource.append(' ');
1781         increaseSplitDelta(1);
1782       }
1783       formattedSource.append(operatorString);
1784       //increaseSplitDelta(operatorString.length());
1785     }
1786   }
1787   /**
1788    * Pops the top statement of the stack if it is <code>token</code>
1789    */
1790   private int pop(int token) {
1791     int delta = 0;
1792     if ((constructionsCount > 0)
1793         && (constructions[constructionsCount - 1] == token)) {
1794       delta--;
1795       constructionsCount--;
1796     }
1797     return delta;
1798   }
1799   /**
1800    * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
1801    * <code>NONINDENT_BLOCK</code>.
1802    */
1803   private int popBlock() {
1804     int delta = 0;
1805     if ((constructionsCount > 0)
1806         && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1807       if (constructions[constructionsCount - 1] == BLOCK)
1808         delta--;
1809       constructionsCount--;
1810     }
1811     return delta;
1812   }
1813   /**
1814    * Pops elements until the stack is empty or the top element is <code>token</code>.
1815    * <br>
1816    * Does not remove <code>token</code> from the stack.
1817    * 
1818    * @param token
1819    *            the token to be left as the top of the stack
1820    */
1821   private int popExclusiveUntil(int token) {
1822     int delta = 0;
1823     int startCount = constructionsCount;
1824     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1825       if (constructions[i] != NONINDENT_BLOCK)
1826         delta--;
1827       constructionsCount--;
1828     }
1829     return delta;
1830   }
1831   /**
1832    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1833    * or a <code>NONINDENT_BLOCK</code>.<br>
1834    * Does not remove it from the stack.
1835    */
1836   private int popExclusiveUntilBlock() {
1837     int startCount = constructionsCount;
1838     int delta = 0;
1839     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1840         && constructions[i] != NONINDENT_BLOCK; i--) {
1841       constructionsCount--;
1842       delta--;
1843     }
1844     return delta;
1845   }
1846   /**
1847    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>,
1848    * a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1849    * Does not remove it from the stack.
1850    */
1851   private int popExclusiveUntilBlockOrCase() {
1852     int startCount = constructionsCount;
1853     int delta = 0;
1854     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1855         && constructions[i] != NONINDENT_BLOCK
1856         && constructions[i] != TokenNamecase; i--) {
1857       constructionsCount--;
1858       delta--;
1859     }
1860     return delta;
1861   }
1862   /**
1863    * Pops elements until the stack is empty or the top element is <code>token</code>.
1864    * <br>
1865    * Removes <code>token</code> from the stack too.
1866    * 
1867    * @param token
1868    *            the token to remove from the stack
1869    */
1870   private int popInclusiveUntil(int token) {
1871     int startCount = constructionsCount;
1872     int delta = 0;
1873     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1874       if (constructions[i] != NONINDENT_BLOCK)
1875         delta--;
1876       constructionsCount--;
1877     }
1878     if (constructionsCount > 0) {
1879       if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1880         delta--;
1881       constructionsCount--;
1882     }
1883     return delta;
1884   }
1885   /**
1886    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1887    * or a <code>NONINDENT_BLOCK</code>.<br>
1888    * Does not remove it from the stack.
1889    */
1890   private int popInclusiveUntilBlock() {
1891     int startCount = constructionsCount;
1892     int delta = 0;
1893     for (int i = startCount - 1; i >= 0
1894         && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1895       delta--;
1896       constructionsCount--;
1897     }
1898     if (constructionsCount > 0) {
1899       if (constructions[constructionsCount - 1] == BLOCK)
1900         delta--;
1901       constructionsCount--;
1902     }
1903     return delta;
1904   }
1905   /**
1906    * Pushes a block in the stack. <br>
1907    * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
1908    * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
1909    * otherwise. Creates a new bigger array if the current one is full.
1910    */
1911   private int pushBlock() {
1912     int delta = 0;
1913     if (constructionsCount == constructions.length)
1914       System.arraycopy(constructions, 0,
1915           (constructions = new int[constructionsCount * 2]), 0,
1916           constructionsCount);
1917     if ((constructionsCount == 0)
1918         || (constructions[constructionsCount - 1] == BLOCK)
1919         || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1920         || (constructions[constructionsCount - 1] == TokenNamecase)) {
1921       delta++;
1922       constructions[constructionsCount++] = BLOCK;
1923     } else {
1924       constructions[constructionsCount++] = NONINDENT_BLOCK;
1925     }
1926     return delta;
1927   }
1928   /**
1929    * Pushes <code>token</code>.<br>
1930    * Creates a new bigger array if the current one is full.
1931    */
1932   private int pushControlStatement(int token) {
1933     if (constructionsCount == constructions.length)
1934       System.arraycopy(constructions, 0,
1935           (constructions = new int[constructionsCount * 2]), 0,
1936           constructionsCount);
1937     constructions[constructionsCount++] = token;
1938     return 1;
1939   }
1940   private static boolean separateFirstArgumentOn(int currentToken) {
1941     //return (currentToken == TokenNameCOMMA || currentToken ==
1942     // TokenNameSEMICOLON);
1943     return currentToken != TokenNameif && currentToken != TokenNameLPAREN
1944         && currentToken != TokenNameNOT && currentToken != TokenNamewhile
1945         && currentToken != TokenNamefor && currentToken != TokenNameswitch;
1946   }
1947   /**
1948    * Set the positions to map. The mapped positions should be retrieved using
1949    * the getMappedPositions() method.
1950    * 
1951    * @param positions
1952    *            int[]
1953    * @deprecated Set the positions to map using the format(String, int, int[])
1954    *             method.
1955    * 
1956    * @see #getMappedPositions()
1957    */
1958   public void setPositionsToMap(int[] positions) {
1959     positionsToMap = positions;
1960     lineDelta = 0;
1961     globalDelta = 0;
1962     mappedPositions = new int[positions.length];
1963   }
1964   /**
1965    * Appends a space character to the current line buffer.
1966    */
1967   private void space() {
1968     currentLineBuffer.append(' ');
1969     increaseLineDelta(1);
1970   }
1971   /**
1972    * Splits <code>stringToSplit</code> on the top level token <br>
1973    * If there are several identical token at the same level, the string is cut
1974    * into many pieces.
1975    * 
1976    * @return an object containing the operator and all the substrings or null
1977    *         if the string cannot be split
1978    */
1979   public SplitLine split(String stringToSplit) {
1980     return split(stringToSplit, 0);
1981   }
1982   /**
1983    * Splits <code>stringToSplit</code> on the top level token <br>
1984    * If there are several identical token at the same level, the string is cut
1985    * into many pieces.
1986    * 
1987    * @return an object containing the operator and all the substrings or null
1988    *         if the string cannot be split
1989    */
1990   public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1991     /*
1992      * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
1993      * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1994      */
1995     if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1996       return null;
1997     }
1998     // local variables
1999     int currentToken = 0;
2000     int splitTokenType = 0;
2001     int splitTokenDepth = Integer.MAX_VALUE;
2002     int splitTokenPriority = Integer.MAX_VALUE;
2003     int[] substringsStartPositions = new int[10];
2004     // contains the start position of substrings
2005     int[] substringsEndPositions = new int[10];
2006     // contains the start position of substrings
2007     int substringsCount = 1; // index in the substringsStartPosition array
2008     int[] splitOperators = new int[10];
2009     // contains the start position of substrings
2010     int splitOperatorsCount = 0; // index in the substringsStartPosition array
2011     int[] openParenthesisPosition = new int[10];
2012     int openParenthesisPositionCount = 0;
2013     int position = 0;
2014     int lastOpenParenthesisPosition = -1;
2015     // used to remember the position of the 1st open parenthesis
2016     // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2017     // setup the scanner with a new source
2018     int lastCommentStartPosition = -1;
2019     // to remember the start position of the last comment
2020     int firstTokenOnLine = -1;
2021     // to remember the first token of the line
2022     int previousToken = -1;
2023     // to remember the previous token.
2024     splitScanner.setSource(stringToSplit.toCharArray());
2025     try {
2026       // start the loop
2027       while (true) {
2028         // takes the next token
2029         try {
2030           if (currentToken != Scanner.TokenNameWHITESPACE)
2031             previousToken = currentToken;
2032           currentToken = splitScanner.getNextToken();
2033           if (Scanner.DEBUG) {
2034             int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2035             int currentStartPosition = splitScanner
2036                 .getCurrentTokenStartPosition();
2037             System.out.print(currentStartPosition + "," + currentEndPosition
2038                 + ": ");
2039             System.out.println(scanner.toStringAction(currentToken));
2040           }
2041         } catch (InvalidInputException e) {
2042           if (!handleInvalidToken(e))
2043             throw e;
2044           currentToken = 0;
2045           // this value is not modify when an exception is raised.
2046         }
2047         if (currentToken == TokenNameEOF)
2048           break;
2049         if (firstTokenOnLine == -1) {
2050           firstTokenOnLine = currentToken;
2051         }
2052         switch (currentToken) {
2053           case TokenNameRBRACE :
2054           case TokenNameRPAREN :
2055             if (openParenthesisPositionCount > 0) {
2056               if (openParenthesisPositionCount == 1
2057                   && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2058                 lastOpenParenthesisPosition = openParenthesisPosition[0];
2059               } else if ((splitTokenDepth == Integer.MAX_VALUE)
2060                   || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2061                 splitTokenType = 0;
2062                 splitTokenDepth = openParenthesisPositionCount;
2063                 splitTokenPriority = Integer.MAX_VALUE;
2064                 substringsStartPositions[0] = 0;
2065                 // better token means the whole line until now is the first
2066                 // substring
2067                 substringsCount = 1; // resets the count of substrings
2068                 substringsEndPositions[0] = openParenthesisPosition[0];
2069                 // substring ends on operator start
2070                 position = openParenthesisPosition[0];
2071                 // the string mustn't be cut before the closing parenthesis but
2072                 // after the opening one.
2073                 splitOperatorsCount = 1; // resets the count of split operators
2074                 splitOperators[0] = 0;
2075               }
2076               openParenthesisPositionCount--;
2077             }
2078             break;
2079           case TokenNameLBRACE :
2080           case TokenNameLPAREN :
2081             if (openParenthesisPositionCount == openParenthesisPosition.length) {
2082               System
2083                   .arraycopy(
2084                       openParenthesisPosition,
2085                       0,
2086                       (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2087                       0, openParenthesisPositionCount);
2088             }
2089             openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2090             if (currentToken == TokenNameLPAREN
2091                 && previousToken == TokenNameRPAREN) {
2092               openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2093             }
2094             break;
2095           case TokenNameSEMICOLON :
2096           // ;
2097           case TokenNameCOMMA :
2098           // ,
2099           case TokenNameEQUAL :
2100             // =
2101             if (openParenthesisPositionCount < splitTokenDepth
2102                 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2103               // the current token is better than the one we currently have
2104               // (in level or in priority if same level)
2105               // reset the substringsCount
2106               splitTokenDepth = openParenthesisPositionCount;
2107               splitTokenType = currentToken;
2108               splitTokenPriority = getTokenPriority(currentToken);
2109               substringsStartPositions[0] = 0;
2110               // better token means the whole line until now is the first
2111               // substring
2112               if (separateFirstArgumentOn(firstTokenOnLine)
2113                   && openParenthesisPositionCount > 0) {
2114                 substringsCount = 2; // resets the count of substrings
2115                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2116                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2117                 substringsEndPositions[1] = splitScanner.startPosition;
2118                 splitOperatorsCount = 2; // resets the count of split operators
2119                 splitOperators[0] = 0;
2120                 splitOperators[1] = currentToken;
2121                 position = splitScanner.currentPosition;
2122                 // next substring will start from operator end
2123               } else {
2124                 substringsCount = 1; // resets the count of substrings
2125                 substringsEndPositions[0] = splitScanner.startPosition;
2126                 // substring ends on operator start
2127                 position = splitScanner.currentPosition;
2128                 // next substring will start from operator end
2129                 splitOperatorsCount = 1; // resets the count of split operators
2130                 splitOperators[0] = currentToken;
2131               }
2132             } else {
2133               if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2134                   && splitTokenType != TokenNameEQUAL
2135                   && currentToken != TokenNameEQUAL) {
2136                 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2137                 // split
2138                 // take only the 1st = into account.
2139                 // if another token with the same priority is found,
2140                 // push the start position of the substring and
2141                 // push the token into the stack.
2142                 // create a new array object if the current one is full.
2143                 if (substringsCount == substringsStartPositions.length) {
2144                   System
2145                       .arraycopy(
2146                           substringsStartPositions,
2147                           0,
2148                           (substringsStartPositions = new int[substringsCount * 2]),
2149                           0, substringsCount);
2150                   System.arraycopy(substringsEndPositions, 0,
2151                       (substringsEndPositions = new int[substringsCount * 2]),
2152                       0, substringsCount);
2153                 }
2154                 if (splitOperatorsCount == splitOperators.length) {
2155                   System.arraycopy(splitOperators, 0,
2156                       (splitOperators = new int[splitOperatorsCount * 2]), 0,
2157                       splitOperatorsCount);
2158                 }
2159                 substringsStartPositions[substringsCount] = position;
2160                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2161                 // substring ends on operator start
2162                 position = splitScanner.currentPosition;
2163                 // next substring will start from operator end
2164                 splitOperators[splitOperatorsCount++] = currentToken;
2165               }
2166             }
2167             break;
2168           case TokenNameCOLON :
2169             // : (15.24)
2170             // see 1FK7C5R, we only split on a colon, when it is associated
2171             // with a question-mark.
2172             // indeed it might appear also behind a case statement, and we do
2173             // not to break at this point.
2174             if ((splitOperatorsCount == 0)
2175                 || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2176               break;
2177             }
2178           case TokenNameextends :
2179           //                    case TokenNameimplements :
2180           //                    case TokenNamethrows :
2181           case TokenNameDOT :
2182           // .
2183           case TokenNameMULTIPLY :
2184           // * (15.16.1)
2185           case TokenNameDIVIDE :
2186           // / (15.16.2)
2187           case TokenNameREMAINDER :
2188           // % (15.16.3)
2189           case TokenNamePLUS :
2190           // + (15.17, 15.17.2)
2191           case TokenNameMINUS :
2192           // - (15.17.2)
2193           case TokenNameLEFT_SHIFT :
2194           // << (15.18)
2195           case TokenNameRIGHT_SHIFT :
2196           // >> (15.18)
2197           //                            case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2198           case TokenNameLESS :
2199           // < (15.19.1)
2200           case TokenNameLESS_EQUAL :
2201           // <= (15.19.1)
2202           case TokenNameGREATER :
2203           // > (15.19.1)
2204           case TokenNameGREATER_EQUAL :
2205           // >= (15.19.1)
2206           //                            case TokenNameinstanceof : // instanceof
2207           case TokenNameEQUAL_EQUAL :
2208           // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2209           case TokenNameEQUAL_EQUAL_EQUAL :
2210           // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2211           case TokenNameNOT_EQUAL :
2212           // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2213           case TokenNameNOT_EQUAL_EQUAL :
2214           // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2215           case TokenNameAND :
2216           // & (15.21, 15.21.1, 15.21.2)
2217           case TokenNameOR :
2218           // | (15.21, 15.21.1, 15.21.2)
2219           case TokenNameXOR :
2220           // ^ (15.21, 15.21.1, 15.21.2)
2221           case TokenNameAND_AND :
2222           // && (15.22)
2223           case TokenNameOR_OR :
2224           // || (15.23)
2225           case TokenNameQUESTION :
2226           // ? (15.24)
2227           case TokenNameMULTIPLY_EQUAL :
2228           // *= (15.25.2)
2229           case TokenNameDIVIDE_EQUAL :
2230           // /= (15.25.2)
2231           case TokenNameREMAINDER_EQUAL :
2232           // %= (15.25.2)
2233           case TokenNamePLUS_EQUAL :
2234           // += (15.25.2)
2235           case TokenNameMINUS_EQUAL :
2236           // -= (15.25.2)
2237           case TokenNameLEFT_SHIFT_EQUAL :
2238           // <<= (15.25.2)
2239           case TokenNameRIGHT_SHIFT_EQUAL :
2240           // >>= (15.25.2)
2241           //                                    case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2242           case TokenNameAND_EQUAL :
2243           // &= (15.25.2)
2244           case TokenNameXOR_EQUAL :
2245           // ^= (15.25.2)
2246           case TokenNameOR_EQUAL :
2247             // |= (15.25.2)
2248             if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2249                 && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2250                     || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2251               // the current token is better than the one we currently have
2252               // (in level or in priority if same level)
2253               // reset the substringsCount
2254               splitTokenDepth = openParenthesisPositionCount;
2255               splitTokenType = currentToken;
2256               splitTokenPriority = getTokenPriority(currentToken);
2257               substringsStartPositions[0] = 0;
2258               // better token means the whole line until now is the first
2259               // substring
2260               if (separateFirstArgumentOn(firstTokenOnLine)
2261                   && openParenthesisPositionCount > 0) {
2262                 substringsCount = 2; // resets the count of substrings
2263                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2264                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2265                 substringsEndPositions[1] = splitScanner.startPosition;
2266                 splitOperatorsCount = 3; // resets the count of split operators
2267                 splitOperators[0] = 0;
2268                 splitOperators[1] = 0;
2269                 splitOperators[2] = currentToken;
2270                 position = splitScanner.currentPosition;
2271                 // next substring will start from operator end
2272               } else {
2273                 substringsCount = 1; // resets the count of substrings
2274                 substringsEndPositions[0] = splitScanner.startPosition;
2275                 // substring ends on operator start
2276                 position = splitScanner.currentPosition;
2277                 // next substring will start from operator end
2278                 splitOperatorsCount = 2; // resets the count of split operators
2279                 splitOperators[0] = 0;
2280                 // nothing for first operand since operator will be inserted in
2281                 // front of the second operand
2282                 splitOperators[1] = currentToken;
2283               }
2284             } else {
2285               if (openParenthesisPositionCount == splitTokenDepth
2286                   && splitTokenPriority == getTokenPriority(currentToken)) {
2287                 // if another token with the same priority is found,
2288                 // push the start position of the substring and
2289                 // push the token into the stack.
2290                 // create a new array object if the current one is full.
2291                 if (substringsCount == substringsStartPositions.length) {
2292                   System
2293                       .arraycopy(
2294                           substringsStartPositions,
2295                           0,
2296                           (substringsStartPositions = new int[substringsCount * 2]),
2297                           0, substringsCount);
2298                   System.arraycopy(substringsEndPositions, 0,
2299                       (substringsEndPositions = new int[substringsCount * 2]),
2300                       0, substringsCount);
2301                 }
2302                 if (splitOperatorsCount == splitOperators.length) {
2303                   System.arraycopy(splitOperators, 0,
2304                       (splitOperators = new int[splitOperatorsCount * 2]), 0,
2305                       splitOperatorsCount);
2306                 }
2307                 substringsStartPositions[substringsCount] = position;
2308                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2309                 // substring ends on operator start
2310                 position = splitScanner.currentPosition;
2311                 // next substring will start from operator end
2312                 splitOperators[splitOperatorsCount++] = currentToken;
2313               }
2314             }
2315           default :
2316             break;
2317         }
2318         if (isComment(currentToken)) {
2319           lastCommentStartPosition = splitScanner.startPosition;
2320         } else {
2321           lastCommentStartPosition = -1;
2322         }
2323       }
2324     } catch (InvalidInputException e) {
2325       return null;
2326     }
2327     // if the string cannot be split, return null.
2328     if (splitOperatorsCount == 0)
2329       return null;
2330     // ## SPECIAL CASES BEGIN
2331     if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2332         && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2333         || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2334             && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) || (separateFirstArgumentOn(firstTokenOnLine)
2335         && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2336         && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2337       // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2338       // not be broken on two lines
2339       // only one split on a top level .
2340       // or more than one split on . and substring before open parenthesis fits
2341       // one line.
2342       // or split inside parenthesis and first token is not a for/while/if
2343       SplitLine sl = split(
2344           stringToSplit.substring(lastOpenParenthesisPosition),
2345           lastOpenParenthesisPosition);
2346       if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2347         // trim() is used to remove the extra blanks at the end of the
2348         // substring. See PR 1FGYPI1
2349         return new SplitLine(new int[]{0, 0}, new String[]{
2350             stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2351             stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2352             offsetInGlobalLine,
2353             lastOpenParenthesisPosition + offsetInGlobalLine});
2354       } else {
2355         // right substring can be split and is split on comma
2356         // copy substrings and operators
2357         // except if the 1st string is empty.
2358         int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2359         int subStringsLength = sl.substrings.length + 1 - startIndex;
2360         String[] result = new String[subStringsLength];
2361         int[] startIndexes = new int[subStringsLength];
2362         int operatorsLength = sl.operators.length + 1 - startIndex;
2363         int[] operators = new int[operatorsLength];
2364         result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2365         operators[0] = 0;
2366         System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2367             1, subStringsLength - 1);
2368         for (int i = subStringsLength - 1; i >= 0; i--) {
2369           startIndexes[i] += offsetInGlobalLine;
2370         }
2371         System.arraycopy(sl.substrings, startIndex, result, 1,
2372             subStringsLength - 1);
2373         System.arraycopy(sl.operators, startIndex, operators, 1,
2374             operatorsLength - 1);
2375         return new SplitLine(operators, result, startIndexes);
2376       }
2377     }
2378     // if the last token is a comment and the substring before the comment fits
2379     // on a line,
2380     // split before the comment and return the result.
2381     if (lastCommentStartPosition > -1
2382         && lastCommentStartPosition < options.maxLineLength
2383         && splitTokenPriority > 50) {
2384       int end = lastCommentStartPosition;
2385       int start = lastCommentStartPosition;
2386       if (stringToSplit.charAt(end - 1) == ' ') {
2387         end--;
2388       }
2389       if (start != end && stringToSplit.charAt(start) == ' ') {
2390         start++;
2391       }
2392       return new SplitLine(new int[]{0, 0}, new String[]{
2393           stringToSplit.substring(0, end), stringToSplit.substring(start)},
2394           new int[]{0, start});
2395     }
2396     if (position != stringToSplit.length()) {
2397       if (substringsCount == substringsStartPositions.length) {
2398         System.arraycopy(substringsStartPositions, 0,
2399             (substringsStartPositions = new int[substringsCount * 2]), 0,
2400             substringsCount);
2401         System.arraycopy(substringsEndPositions, 0,
2402             (substringsEndPositions = new int[substringsCount * 2]), 0,
2403             substringsCount);
2404       }
2405       // avoid empty extra substring, e.g. line terminated with a semi-colon
2406       substringsStartPositions[substringsCount] = position;
2407       substringsEndPositions[substringsCount++] = stringToSplit.length();
2408     }
2409     if (splitOperatorsCount == splitOperators.length) {
2410       System.arraycopy(splitOperators, 0,
2411           (splitOperators = new int[splitOperatorsCount * 2]), 0,
2412           splitOperatorsCount);
2413     }
2414     splitOperators[splitOperatorsCount] = 0;
2415     // the last element of the stack is the position of the end of
2416     // StringToSPlit
2417     // +1 because the substring method excludes the last character
2418     String[] result = new String[substringsCount];
2419     for (int i = 0; i < substringsCount; i++) {
2420       int start = substringsStartPositions[i];
2421       int end = substringsEndPositions[i];
2422       if (stringToSplit.charAt(start) == ' ') {
2423         start++;
2424         substringsStartPositions[i]++;
2425       }
2426       if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2427         end--;
2428       }
2429       result[i] = stringToSplit.substring(start, end);
2430       substringsStartPositions[i] += offsetInGlobalLine;
2431     }
2432     if (splitOperatorsCount > substringsCount) {
2433       System.arraycopy(substringsStartPositions, 0,
2434           (substringsStartPositions = new int[splitOperatorsCount]), 0,
2435           substringsCount);
2436       System.arraycopy(substringsEndPositions, 0,
2437           (substringsEndPositions = new int[splitOperatorsCount]), 0,
2438           substringsCount);
2439       for (int i = substringsCount; i < splitOperatorsCount; i++) {
2440         substringsStartPositions[i] = position;
2441         substringsEndPositions[i] = position;
2442       }
2443       System.arraycopy(splitOperators, 0,
2444           (splitOperators = new int[splitOperatorsCount]), 0,
2445           splitOperatorsCount);
2446     } else {
2447       System.arraycopy(substringsStartPositions, 0,
2448           (substringsStartPositions = new int[substringsCount]), 0,
2449           substringsCount);
2450       System.arraycopy(substringsEndPositions, 0,
2451           (substringsEndPositions = new int[substringsCount]), 0,
2452           substringsCount);
2453       System.arraycopy(splitOperators, 0,
2454           (splitOperators = new int[substringsCount]), 0, substringsCount);
2455     }
2456     SplitLine splitLine = new SplitLine(splitOperators, result,
2457         substringsStartPositions);
2458     return splitLine;
2459   }
2460   private void updateMappedPositions(int startPosition) {
2461     if (positionsToMap == null) {
2462       return;
2463     }
2464     char[] source = scanner.source;
2465     int sourceLength = source.length;
2466     while (indexToMap < positionsToMap.length
2467         && positionsToMap[indexToMap] <= startPosition) {
2468       int posToMap = positionsToMap[indexToMap];
2469       if (posToMap < 0 || posToMap >= sourceLength) {
2470         // protection against out of bounds position
2471         if (posToMap == sourceLength) {
2472           mappedPositions[indexToMap] = formattedSource.length();
2473         }
2474         indexToMap = positionsToMap.length; // no more mapping
2475         return;
2476       }
2477       if (CharOperation.isWhitespace(source[posToMap])) {
2478         mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2479       } else {
2480         if (posToMap == sourceLength - 1) {
2481           mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2482         } else {
2483           mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2484         }
2485       }
2486       indexToMap++;
2487     }
2488   }
2489   private void updateMappedPositionsWhileSplitting(int startPosition,
2490       int endPosition) {
2491     if (mappedPositions == null || mappedPositions.length == indexInMap)
2492       return;
2493     while (indexInMap < mappedPositions.length
2494         && startPosition <= mappedPositions[indexInMap]
2495         && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2496       mappedPositions[indexInMap] += splitDelta;
2497       indexInMap++;
2498     }
2499   }
2500   private int getLength(String s, int tabDepth) {
2501     int length = 0;
2502     for (int i = 0; i < tabDepth; i++) {
2503       length += options.tabSize;
2504     }
2505     for (int i = 0, max = s.length(); i < max; i++) {
2506       char currentChar = s.charAt(i);
2507       switch (currentChar) {
2508         case '\t' :
2509           length += options.tabSize;
2510           break;
2511         default :
2512           length++;
2513       }
2514     }
2515     return length;
2516   }
2517   /**
2518    * Sets the initial indentation level
2519    * 
2520    * @param indentationLevel
2521    *            new indentation level
2522    * 
2523    * @deprecated
2524    */
2525   public void setInitialIndentationLevel(int newIndentationLevel) {
2526     this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;
2527   }
2528 }