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