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