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