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