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