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