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