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