compact the dereferencing operator in the formatter
[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 == TokenNameMINUS_GREATER &&
894                                                                         options.compactDereferencingMode) // ->
895                 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
896                 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
897                 pendingSpace = false;
898               }
899               unarySignModifier = 0;
900             }
901             break;
902         }
903         // Do not output whitespace tokens.
904         if (token != Scanner.TokenNameWHITESPACE) {
905
906           /* Add pending space to the formatted source string.
907           Do not output a space under the following circumstances:
908           1) this is the first pass
909           2) previous token is an open paren
910           3) previous token is a period
911           4) previous token is the logical compliment (eg: !)
912           5) previous token is the bitwise compliment (eg: ~)
913           6) previous token is the open bracket (eg: [)
914           7) in an assignment statement, if the previous token is an 
915           open brace or the current token is a close brace
916           8) previous token is a single line comment
917           9) current token is a '->'
918           */
919                   if (token == TokenNameMINUS_GREATER && 
920                                 options.compactDereferencingMode) pendingSpace = false;
921           
922           boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
923
924           if (pendingSpace
925             && insertSpaceAfter(previousToken)
926             && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
927             && previousToken != Scanner.TokenNameCOMMENT_LINE) {
928             if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
929               space();
930           }
931           // Add the next token to the formatted source string.
932           outputCurrentToken(token);
933           if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
934             pendingNewLines = 0;
935             currentLineBuffer.append(options.lineSeparatorSequence);
936             increaseLineDelta(options.lineSeparatorSequence.length);
937           }
938           pendingSpace = true;
939         }
940         // Whitespace tokens do not need to be remembered.
941         if (token != Scanner.TokenNameWHITESPACE) {
942           previousToken = token;
943           if (token != Scanner.TokenNameCOMMENT_BLOCK
944             && token != Scanner.TokenNameCOMMENT_LINE
945             && token != Scanner.TokenNameCOMMENT_PHPDOC) {
946             previousCompilableToken = token;
947           }
948         }
949       }
950       output(copyRemainingSource());
951       flushBuffer();
952       // dump the last token of the source in the formatted output.
953     } catch (InvalidInputException e) {
954       output(copyRemainingSource());
955       flushBuffer();
956       // dump the last token of the source in the formatted output.
957     }
958   }
959
960   /** 
961    * Formats the char array <code>sourceString</code>,
962    * and returns a string containing the formatted version.
963    * @return the formatted ouput.
964    */
965   public String formatSourceString(String sourceString) {
966     char[] sourceChars = sourceString.toCharArray();
967     formattedSource = new StringBuffer(sourceChars.length);
968     scanner.setSource(sourceChars);
969     format();
970     return formattedSource.toString();
971   }
972
973   /** 
974    * Formats the char array <code>sourceString</code>,
975    * and returns a string containing the formatted version.
976    * @param string the string to format
977    * @param indentationLevel the initial indentation level
978    * @return the formatted ouput.
979    */
980   public String format(String string, int indentationLevel) {
981     return format(string, indentationLevel, (int[]) null);
982   }
983
984   /** 
985    * Formats the char array <code>sourceString</code>,
986    * and returns a string containing the formatted version.
987    * The positions array is modified to contain the mapped positions.
988    * @param string the string to format
989    * @param indentationLevel the initial indentation level
990    * @param positions the array of positions to map
991    * @return the formatted ouput.
992    */
993   public String format(String string, int indentationLevel, int[] positions) {
994     return this.format(string, indentationLevel, positions, null);
995   }
996
997   public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
998     if (lineSeparator != null) {
999       this.options.setLineSeparator(lineSeparator);
1000     }
1001     if (positions != null) {
1002       this.setPositionsToMap(positions);
1003       this.setInitialIndentationLevel(indentationLevel);
1004       String formattedString = this.formatSourceString(string);
1005       int[] mappedPositions = this.getMappedPositions();
1006       System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1007       return formattedString;
1008     } else {
1009       this.setInitialIndentationLevel(indentationLevel);
1010       return this.formatSourceString(string);
1011     }
1012   }
1013   /** 
1014    * Formats the char array <code>sourceString</code>,
1015    * and returns a string containing the formatted version. The initial indentation level is 0.
1016    * @param string the string to format
1017    * @return the formatted ouput.
1018    */
1019   public String format(String string) {
1020     return this.format(string, 0, (int[]) null);
1021   }
1022
1023   /** 
1024    * Formats a given source string, starting indenting it at a particular 
1025    * depth and using the given options
1026    * 
1027    * @deprecated backport 1.0 internal functionality
1028    */
1029   public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1030     CodeFormatter formatter = new CodeFormatter(options);
1031     formatter.setInitialIndentationLevel(initialIndentationLevel);
1032     return formatter.formatSourceString(sourceString);
1033   }
1034
1035   /**
1036    * Returns the number of characters and tab char between the beginning of the line
1037    * and the beginning of the comment.
1038    */
1039   private int getCurrentCommentOffset() {
1040     int linePtr = scanner.linePtr;
1041     // if there is no beginning of line, return 0.
1042     if (linePtr < 0)
1043       return 0;
1044     int offset = 0;
1045     int beginningOfLine = scanner.lineEnds[linePtr];
1046     int currentStartPosition = scanner.startPosition;
1047     char[] source = scanner.source;
1048
1049     // find the position of the beginning of the line containing the comment
1050     while (beginningOfLine > currentStartPosition) {
1051       if (linePtr > 0) {
1052         beginningOfLine = scanner.lineEnds[--linePtr];
1053       } else {
1054         beginningOfLine = 0;
1055         break;
1056       }
1057     }
1058     for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1059       char currentCharacter = source[i];
1060       switch (currentCharacter) {
1061         case '\t' :
1062           offset += options.tabSize;
1063           break;
1064         case ' ' :
1065           offset++;
1066           break;
1067         case '\r' :
1068         case '\n' :
1069           break;
1070         default :
1071           return offset;
1072       }
1073     }
1074     return offset;
1075   }
1076
1077   /**
1078    * Returns an array of descriptions for the configurable options.
1079    * The descriptions may be changed and passed back to a different
1080    * compiler.
1081    * 
1082    * @deprecated backport 1.0 internal functionality
1083    */
1084   public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1085     String componentName = CodeFormatter.class.getName();
1086     FormatterOptions options = new FormatterOptions();
1087     return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
1088       new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
1089       new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
1090       //      new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1091       new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
1092       new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
1093       new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
1094       new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
1095       new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1096     };
1097   }
1098
1099   /**
1100    * Returns the array of mapped positions.
1101    * Returns null is no positions have been set.
1102    * @return int[]
1103    * @deprecated There is no need to retrieve the mapped positions anymore.
1104    */
1105   public int[] getMappedPositions() {
1106     return mappedPositions;
1107   }
1108
1109   /**
1110    * Returns the priority of the token given as argument<br>
1111    * The most prioritary the token is, the smallest the return value is.
1112    * @return the priority of <code>token</code>
1113    * @param token the token of which the priority is requested
1114    */
1115   private static int getTokenPriority(int token) {
1116     switch (token) {
1117       case TokenNameextends :
1118         //                      case TokenNameimplements :
1119         //                      case TokenNamethrows :
1120         return 10;
1121       case TokenNameSEMICOLON : // ;
1122         return 20;
1123       case TokenNameCOMMA : // ,
1124         return 25;
1125       case TokenNameEQUAL : // =
1126         return 30;
1127       case TokenNameAND_AND : // && 
1128       case TokenNameOR_OR : // || 
1129         return 40;
1130       case TokenNameQUESTION : // ? 
1131       case TokenNameCOLON : // :
1132         return 50; // it's better cutting on ?: than on ;
1133       case TokenNameEQUAL_EQUAL : // == 
1134       case TokenNameEQUAL_EQUAL_EQUAL : // === 
1135       case TokenNameNOT_EQUAL : // != 
1136                         case TokenNameNOT_EQUAL_EQUAL : // != 
1137         return 60;
1138       case TokenNameLESS : // < 
1139       case TokenNameLESS_EQUAL : // <= 
1140       case TokenNameGREATER : // > 
1141       case TokenNameGREATER_EQUAL : // >= 
1142         //                      case TokenNameinstanceof : // instanceof
1143         return 70;
1144       case TokenNamePLUS : // + 
1145       case TokenNameMINUS : // - 
1146         return 80;
1147       case TokenNameMULTIPLY : // * 
1148       case TokenNameDIVIDE : // / 
1149       case TokenNameREMAINDER : // % 
1150         return 90;
1151       case TokenNameLEFT_SHIFT : // << 
1152       case TokenNameRIGHT_SHIFT : // >> 
1153         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> 
1154         return 100;
1155       case TokenNameAND : // &
1156       case TokenNameOR : // | 
1157       case TokenNameXOR : // ^ 
1158         return 110;
1159       case TokenNameMULTIPLY_EQUAL : // *= 
1160       case TokenNameDIVIDE_EQUAL : // /= 
1161       case TokenNameREMAINDER_EQUAL : // %= 
1162       case TokenNamePLUS_EQUAL : // += 
1163       case TokenNameMINUS_EQUAL : // -= 
1164       case TokenNameLEFT_SHIFT_EQUAL : // <<= 
1165       case TokenNameRIGHT_SHIFT_EQUAL : // >>= 
1166         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1167       case TokenNameAND_EQUAL : // &= 
1168       case TokenNameXOR_EQUAL : // ^= 
1169       case TokenNameOR_EQUAL : // |= 
1170         return 120;
1171       case TokenNameDOT : // .
1172         return 130;
1173       default :
1174         return Integer.MAX_VALUE;
1175     }
1176   }
1177
1178   /**
1179    * Handles the exception raised when an invalid token is encountered.
1180    * Returns true if the exception has been handled, false otherwise.
1181    */
1182   private boolean handleInvalidToken(Exception e) {
1183     if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1184       || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1185       || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1186       return true;
1187     }
1188     return false;
1189   }
1190
1191   private final void increaseGlobalDelta(int offset) {
1192     globalDelta += offset;
1193   }
1194
1195   private final void increaseLineDelta(int offset) {
1196     lineDelta += offset;
1197   }
1198
1199   private final void increaseSplitDelta(int offset) {
1200     splitDelta += offset;
1201   }
1202
1203   /**
1204    * Returns true if a space has to be inserted after <code>operator</code>
1205    * false otherwise.
1206    */
1207   private boolean insertSpaceAfter(int token) {
1208     switch (token) {
1209       case TokenNameLPAREN :
1210       case TokenNameNOT :
1211       case TokenNameTWIDDLE :
1212       case TokenNameDOT :
1213       case 0 : // no token
1214       case TokenNameLBRACKET :
1215       case Scanner.TokenNameCOMMENT_LINE :
1216         return false;
1217       default :
1218         return true;
1219     }
1220   }
1221
1222   /**
1223    * Returns true if a space has to be inserted before <code>operator</code>
1224    * false otherwise.<br>
1225    * Cannot be static as it uses the code formatter options
1226    * (to know if the compact assignment mode is on).
1227    */
1228   private boolean insertSpaceBefore(int token) {
1229     switch (token) {
1230       case TokenNameEQUAL :
1231         return (!options.compactAssignmentMode);
1232       default :
1233         return false;
1234     }
1235   }
1236
1237   private static boolean isComment(int token) {
1238     boolean result =
1239       token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE || token == Scanner.TokenNameCOMMENT_PHPDOC;
1240     return result;
1241   }
1242
1243   private static boolean isLiteralToken(int token) {
1244     boolean result = token == TokenNameIntegerLiteral
1245       //                        || token == TokenNameLongLiteral
1246     //                  || token == TokenNameFloatingPointLiteral
1247   || token == TokenNameDoubleLiteral
1248       //                        || token == TokenNameCharacterLiteral
1249   || token == TokenNameStringLiteral;
1250     return result;
1251   }
1252
1253   /**
1254    * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1255    * it is split and the result is dumped in <code>formattedSource</code>
1256    * @param newLineCount the number of new lines to append
1257    */
1258   private void newLine(int newLineCount) {
1259
1260     // format current line
1261     splitDelta = 0;
1262     beginningOfLineIndex = formattedSource.length();
1263     String currentLine = currentLineBuffer.toString();
1264     if (containsOpenCloseBraces) {
1265       containsOpenCloseBraces = false;
1266       outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1267       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1268     } else {
1269       outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1270     }
1271     // dump line break(s)
1272     for (int i = 0; i < newLineCount; i++) {
1273       formattedSource.append(options.lineSeparatorSequence);
1274       increaseSplitDelta(options.lineSeparatorSequence.length);
1275     }
1276     // reset formatter for next line
1277     int currentLength = currentLine.length();
1278     currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1279
1280     increaseGlobalDelta(splitDelta);
1281     increaseGlobalDelta(lineDelta);
1282     lineDelta = 0;
1283     currentLineIndentationLevel = initialIndentationLevel;
1284   }
1285
1286   private String operatorString(int operator) {
1287     switch (operator) {
1288       case TokenNameextends :
1289         return "extends"; //$NON-NLS-1$
1290
1291         //                      case TokenNameimplements :
1292         //                              return "implements"; //$NON-NLS-1$
1293         //
1294         //                      case TokenNamethrows :
1295         //                              return "throws"; //$NON-NLS-1$
1296
1297       case TokenNameSEMICOLON : // ;
1298         return ";"; //$NON-NLS-1$
1299
1300       case TokenNameCOMMA : // ,
1301         return ","; //$NON-NLS-1$
1302
1303       case TokenNameEQUAL : // =
1304         return "="; //$NON-NLS-1$
1305
1306       case TokenNameAND_AND : // && (15.22)
1307         return "&&"; //$NON-NLS-1$
1308
1309       case TokenNameOR_OR : // || (15.23)
1310         return "||"; //$NON-NLS-1$
1311
1312       case TokenNameQUESTION : // ? (15.24)
1313         return "?"; //$NON-NLS-1$
1314
1315       case TokenNameCOLON : // : (15.24)
1316         return ":"; //$NON-NLS-1$
1317
1318       case TokenNameCOLON_COLON : // : (15.24)
1319         return "::"; //$NON-NLS-1$
1320
1321       case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1322         return "=="; //$NON-NLS-1$
1323
1324       case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1325         return "==="; //$NON-NLS-1$
1326
1327       case TokenNameEQUAL_GREATER : // -= (15.25.2)
1328         return "=>"; //$NON-NLS-1$                              
1329
1330       case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1331         return "!="; //$NON-NLS-1$
1332
1333                         case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1334                                 return "!=="; //$NON-NLS-1$
1335                                                         
1336       case TokenNameLESS : // < (15.19.1)
1337         return "<"; //$NON-NLS-1$
1338
1339       case TokenNameLESS_EQUAL : // <= (15.19.1)
1340         return "<="; //$NON-NLS-1$
1341
1342       case TokenNameGREATER : // > (15.19.1)
1343         return ">"; //$NON-NLS-1$
1344
1345       case TokenNameGREATER_EQUAL : // >= (15.19.1)
1346         return ">="; //$NON-NLS-1$
1347
1348         //                      case TokenNameinstanceof : // instanceof
1349         //                              return "instanceof"; //$NON-NLS-1$
1350
1351       case TokenNamePLUS : // + (15.17, 15.17.2)
1352         return "+"; //$NON-NLS-1$
1353
1354       case TokenNameMINUS : // - (15.17.2)
1355         return "-"; //$NON-NLS-1$
1356
1357       case TokenNameMULTIPLY : // * (15.16.1)
1358         return "*"; //$NON-NLS-1$
1359
1360       case TokenNameDIVIDE : // / (15.16.2)
1361         return "/"; //$NON-NLS-1$
1362
1363       case TokenNameREMAINDER : // % (15.16.3)
1364         return "%"; //$NON-NLS-1$
1365
1366       case TokenNameLEFT_SHIFT : // << (15.18)
1367         return "<<"; //$NON-NLS-1$
1368
1369       case TokenNameRIGHT_SHIFT : // >> (15.18)
1370         return ">>"; //$NON-NLS-1$
1371
1372         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1373         //                              return ">>>"; //$NON-NLS-1$
1374
1375       case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
1376         return "&"; //$NON-NLS-1$
1377
1378       case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
1379         return "|"; //$NON-NLS-1$
1380
1381       case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
1382         return "^"; //$NON-NLS-1$
1383
1384       case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
1385         return "*="; //$NON-NLS-1$
1386
1387       case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
1388         return "/="; //$NON-NLS-1$
1389
1390       case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
1391         return "%="; //$NON-NLS-1$
1392
1393       case TokenNamePLUS_EQUAL : // += (15.25.2)
1394         return "+="; //$NON-NLS-1$
1395
1396       case TokenNameMINUS_EQUAL : // -= (15.25.2)
1397         return "-="; //$NON-NLS-1$
1398
1399       case TokenNameMINUS_GREATER : // -= (15.25.2)
1400         return "->"; //$NON-NLS-1$
1401
1402       case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
1403         return "<<="; //$NON-NLS-1$
1404
1405       case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
1406         return ">>="; //$NON-NLS-1$
1407
1408         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1409         //                              return ">>>="; //$NON-NLS-1$
1410
1411       case TokenNameAND_EQUAL : // &= (15.25.2)
1412         return "&="; //$NON-NLS-1$
1413
1414       case TokenNameXOR_EQUAL : // ^= (15.25.2)
1415         return "^="; //$NON-NLS-1$
1416
1417       case TokenNameOR_EQUAL : // |= (15.25.2)
1418         return "|="; //$NON-NLS-1$
1419
1420       case TokenNameDOT : // .
1421         return "."; //$NON-NLS-1$
1422
1423       default :
1424         return ""; //$NON-NLS-1$
1425     }
1426   }
1427
1428   /** 
1429    * Appends <code>stringToOutput</code> to the formatted output.<br>
1430    * If it contains \n, append a LINE_SEPARATOR and indent after it.
1431    */
1432   private void output(String stringToOutput) {
1433     char currentCharacter;
1434     for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1435       currentCharacter = stringToOutput.charAt(i);
1436       if (currentCharacter != '\t') {
1437         currentLineBuffer.append(currentCharacter);
1438       }
1439     }
1440   }
1441
1442   /** 
1443    * Appends <code>token</code> to the formatted output.<br>
1444    * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1445    */
1446   private void outputCurrentToken(int token) {
1447     char[] source = scanner.source;
1448     int startPosition = scanner.startPosition;
1449
1450     switch (token) {
1451       case Scanner.TokenNameCOMMENT_PHPDOC :
1452       case Scanner.TokenNameCOMMENT_BLOCK :
1453       case Scanner.TokenNameCOMMENT_LINE :
1454         boolean endOfLine = false;
1455         int currentCommentOffset = getCurrentCommentOffset();
1456         int beginningOfLineSpaces = 0;
1457         endOfLine = false;
1458         currentCommentOffset = getCurrentCommentOffset();
1459         beginningOfLineSpaces = 0;
1460         boolean pendingCarriageReturn = false;
1461         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1462           char currentCharacter = source[i];
1463           updateMappedPositions(i);
1464           switch (currentCharacter) {
1465             case '\r' :
1466               pendingCarriageReturn = true;
1467               endOfLine = true;
1468               break;
1469             case '\n' :
1470               if (pendingCarriageReturn) {
1471                 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1472               } else {
1473                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1474               }
1475               pendingCarriageReturn = false;
1476               currentLineBuffer.append(options.lineSeparatorSequence);
1477               beginningOfLineSpaces = 0;
1478               endOfLine = true;
1479               break;
1480             case '\t' :
1481               if (pendingCarriageReturn) {
1482                 pendingCarriageReturn = false;
1483                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1484                 currentLineBuffer.append(options.lineSeparatorSequence);
1485                 beginningOfLineSpaces = 0;
1486                 endOfLine = true;
1487               }
1488               if (endOfLine) {
1489                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1490                 beginningOfLineSpaces += options.tabSize;
1491                 if (beginningOfLineSpaces > currentCommentOffset) {
1492                   currentLineBuffer.append(currentCharacter);
1493                 } else {
1494                   increaseGlobalDelta(-1);
1495                 }
1496               } else {
1497                 currentLineBuffer.append(currentCharacter);
1498               }
1499               break;
1500             case ' ' :
1501               if (pendingCarriageReturn) {
1502                 pendingCarriageReturn = false;
1503                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1504                 currentLineBuffer.append(options.lineSeparatorSequence);
1505                 beginningOfLineSpaces = 0;
1506                 endOfLine = true;
1507               }
1508               if (endOfLine) {
1509                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1510                 beginningOfLineSpaces++;
1511                 if (beginningOfLineSpaces > currentCommentOffset) {
1512                   currentLineBuffer.append(currentCharacter);
1513                 } else {
1514                   increaseGlobalDelta(-1);
1515                 }
1516               } else {
1517                 currentLineBuffer.append(currentCharacter);
1518               }
1519               break;
1520             default :
1521               if (pendingCarriageReturn) {
1522                 pendingCarriageReturn = false;
1523                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1524                 currentLineBuffer.append(options.lineSeparatorSequence);
1525                 beginningOfLineSpaces = 0;
1526                 endOfLine = true;
1527               } else {
1528                 beginningOfLineSpaces = 0;
1529                 currentLineBuffer.append(currentCharacter);
1530                 endOfLine = false;
1531               }
1532           }
1533         }
1534         updateMappedPositions(scanner.currentPosition - 1);
1535         multipleLineCommentCounter++;
1536         break;
1537       default :
1538         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1539           char currentCharacter = source[i];
1540           updateMappedPositions(i);
1541           currentLineBuffer.append(currentCharacter);
1542         }
1543     }
1544   }
1545
1546   /**
1547    * Outputs <code>currentString</code>:<br>
1548    * <ul><li>If its length is < maxLineLength, output
1549    * <li>Otherwise it is split.</ul>
1550    * @param currentString string to output
1551    * @param preIndented whether the string to output was pre-indented
1552    * @param depth number of indentation to put in front of <code>currentString</code>
1553    * @param operator value of the operator belonging to <code>currentString</code>.
1554    */
1555   private void outputLine(
1556     String currentString,
1557     boolean preIndented,
1558     int depth,
1559     int operator,
1560     int substringIndex,
1561     int[] startSubstringIndexes,
1562     int offsetInGlobalLine) {
1563
1564     boolean emptyFirstSubString = false;
1565     String operatorString = operatorString(operator);
1566     boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1567     boolean placeOperatorAhead = !placeOperatorBehind;
1568
1569     // dump prefix operator?
1570     if (placeOperatorAhead) {
1571       if (!preIndented) {
1572         dumpTab(depth);
1573         preIndented = true;
1574       }
1575       if (operator != 0) {
1576         if (insertSpaceBefore(operator)) {
1577           formattedSource.append(' ');
1578           increaseSplitDelta(1);
1579         }
1580         formattedSource.append(operatorString);
1581         increaseSplitDelta(operatorString.length());
1582
1583         if (insertSpaceAfter(operator) //                       && operator != TokenNameimplements
1584         && operator != TokenNameextends) {
1585           //                    && operator != TokenNamethrows) {
1586           formattedSource.append(' ');
1587           increaseSplitDelta(1);
1588         }
1589       }
1590     }
1591     SplitLine splitLine = null;
1592     if (options.maxLineLength == 0
1593       || getLength(currentString, depth) < options.maxLineLength
1594       || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1595
1596       // depending on the type of operator, outputs new line before of after dumping it
1597       // indent before postfix operator
1598       // indent also when the line cannot be split
1599       if (operator == TokenNameextends) {
1600         //                              || operator == TokenNameimplements
1601         //                              || operator == TokenNamethrows) {
1602         formattedSource.append(' ');
1603         increaseSplitDelta(1);
1604       }
1605       if (placeOperatorBehind) {
1606         if (!preIndented) {
1607           dumpTab(depth);
1608         }
1609       }
1610       int max = currentString.length();
1611       if (multipleLineCommentCounter != 0) {
1612         try {
1613           BufferedReader reader = new BufferedReader(new StringReader(currentString));
1614           String line = reader.readLine();
1615           while (line != null) {
1616             updateMappedPositionsWhileSplitting(
1617               beginningOfLineIndex,
1618               beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length);
1619             formattedSource.append(line);
1620             beginningOfLineIndex = beginningOfLineIndex + line.length();
1621             if ((line = reader.readLine()) != null) {
1622               formattedSource.append(options.lineSeparatorSequence);
1623               beginningOfLineIndex += options.lineSeparatorSequence.length;
1624               dumpTab(currentLineIndentationLevel);
1625             }
1626           }
1627           reader.close();
1628         } catch (IOException e) {
1629           e.printStackTrace();
1630         }
1631       } else {
1632         updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1633         for (int i = 0; i < max; i++) {
1634           char currentChar = currentString.charAt(i);
1635           switch (currentChar) {
1636             case '\r' :
1637               break;
1638             case '\n' :
1639               if (i != max - 1) {
1640                 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
1641                 // a substring cannot end with a lineSeparatorSequence,
1642                 // except if it has been added by format() after a one-line comment
1643                 formattedSource.append(options.lineSeparatorSequence);
1644
1645                 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1646                 dumpTab(depth - 1);
1647               }
1648               break;
1649             default :
1650               formattedSource.append(currentChar);
1651           }
1652         }
1653       }
1654       // update positions inside the mappedPositions table
1655       if (substringIndex != -1) {
1656         if (multipleLineCommentCounter == 0) {
1657           int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1658           updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1659         }
1660
1661         // compute the splitDelta resulting with the operator and blank removal
1662         if (substringIndex + 1 != startSubstringIndexes.length) {
1663           increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1664         }
1665       }
1666       // dump postfix operator?
1667       if (placeOperatorBehind) {
1668         if (insertSpaceBefore(operator)) {
1669           formattedSource.append(' ');
1670           if (operator != 0) {
1671             increaseSplitDelta(1);
1672           }
1673         }
1674         formattedSource.append(operatorString);
1675         if (operator != 0) {
1676           increaseSplitDelta(operatorString.length());
1677         }
1678       }
1679       return;
1680     }
1681     // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1682     // extends has to stand alone on a line when currentString has been split.
1683     if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1684       //                                || operator == TokenNameimplements
1685       //                                || operator == TokenNamethrows)) {
1686       formattedSource.append(options.lineSeparatorSequence);
1687       increaseSplitDelta(options.lineSeparatorSequence.length);
1688       dumpTab(depth + 1);
1689     } else {
1690       if (operator == TokenNameextends) {
1691         //                              || operator == TokenNameimplements
1692         //                              || operator == TokenNamethrows) {
1693         formattedSource.append(' ');
1694         increaseSplitDelta(1);
1695       }
1696     }
1697     // perform actual splitting
1698     String result[] = splitLine.substrings;
1699     int[] splitOperators = splitLine.operators;
1700
1701     if (result[0].length() == 0) {
1702       // when the substring 0 is null, the substring 1 is correctly indented.
1703       depth--;
1704       emptyFirstSubString = true;
1705     }
1706     // the operator going in front of the result[0] string is the operator parameter
1707     for (int i = 0, max = result.length; i < max; i++) {
1708       // the new depth is the current one if this is the first substring,
1709       // the current one + 1 otherwise.
1710       // if the substring is a comment, use the current indentation Level instead of the depth
1711       // (-1 because the ouputline increases depth).
1712       // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
1713       String currentResult = result[i];
1714
1715       if (currentResult.length() != 0 || splitOperators[i] != 0) {
1716           int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1717     || currentResult.startsWith("//")) //$NON-NLS-1$ 
1718   ? indentationLevel - 1 : depth;
1719         outputLine(
1720           currentResult,
1721           i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1722           i == 0 ? newDepth : newDepth + 1,
1723           splitOperators[i],
1724           i,
1725           splitLine.startSubstringsIndexes,
1726           currentString.indexOf(currentResult));
1727         if (i != max - 1) {
1728           formattedSource.append(options.lineSeparatorSequence);
1729           increaseSplitDelta(options.lineSeparatorSequence.length);
1730         }
1731       }
1732     }
1733     if (result.length == splitOperators.length - 1) {
1734       int lastOperator = splitOperators[result.length];
1735       String lastOperatorString = operatorString(lastOperator);
1736       formattedSource.append(options.lineSeparatorSequence);
1737       increaseSplitDelta(options.lineSeparatorSequence.length);
1738
1739       if (breakLineBeforeOperator(lastOperator)) {
1740         dumpTab(depth + 1);
1741         if (lastOperator != 0) {
1742           if (insertSpaceBefore(lastOperator)) {
1743             formattedSource.append(' ');
1744             increaseSplitDelta(1);
1745           }
1746           formattedSource.append(lastOperatorString);
1747           increaseSplitDelta(lastOperatorString.length());
1748
1749           if (insertSpaceAfter(lastOperator) //                                 && lastOperator != TokenNameimplements
1750             && lastOperator != TokenNameextends) {
1751             //                                  && lastOperator != TokenNamethrows) {
1752             formattedSource.append(' ');
1753             increaseSplitDelta(1);
1754           }
1755         }
1756       }
1757     }
1758     if (placeOperatorBehind) {
1759       if (insertSpaceBefore(operator)) {
1760         formattedSource.append(' ');
1761         increaseSplitDelta(1);
1762       }
1763       formattedSource.append(operatorString);
1764       //increaseSplitDelta(operatorString.length());
1765     }
1766   }
1767
1768   /**
1769    * Pops the top statement of the stack if it is <code>token</code>
1770    */
1771   private int pop(int token) {
1772     int delta = 0;
1773     if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1774       delta--;
1775       constructionsCount--;
1776     }
1777     return delta;
1778   }
1779
1780   /**
1781    * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1782    */
1783   private int popBlock() {
1784     int delta = 0;
1785     if ((constructionsCount > 0)
1786       && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1787       if (constructions[constructionsCount - 1] == BLOCK)
1788         delta--;
1789       constructionsCount--;
1790     }
1791     return delta;
1792   }
1793
1794   /**
1795    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1796    * Does not remove <code>token</code> from the stack.
1797    * @param token the token to be left as the top of the stack
1798    */
1799   private int popExclusiveUntil(int token) {
1800     int delta = 0;
1801     int startCount = constructionsCount;
1802     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1803       if (constructions[i] != NONINDENT_BLOCK)
1804         delta--;
1805       constructionsCount--;
1806     }
1807     return delta;
1808   }
1809
1810   /**
1811    * Pops elements until the stack is empty or the top element is
1812    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1813    * Does not remove it from the stack.
1814    */
1815   private int popExclusiveUntilBlock() {
1816     int startCount = constructionsCount;
1817     int delta = 0;
1818     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1819       constructionsCount--;
1820       delta--;
1821     }
1822     return delta;
1823   }
1824
1825   /**
1826    * Pops elements until the stack is empty or the top element is
1827    * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1828    * Does not remove it from the stack.
1829    */
1830   private int popExclusiveUntilBlockOrCase() {
1831     int startCount = constructionsCount;
1832     int delta = 0;
1833     for (int i = startCount - 1;
1834       i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK && constructions[i] != TokenNamecase;
1835       i--) {
1836       constructionsCount--;
1837       delta--;
1838     }
1839     return delta;
1840   }
1841
1842   /**
1843    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1844    * Removes <code>token</code> from the stack too.
1845    * @param token the token to remove from the stack
1846    */
1847   private int popInclusiveUntil(int token) {
1848     int startCount = constructionsCount;
1849     int delta = 0;
1850     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1851       if (constructions[i] != NONINDENT_BLOCK)
1852         delta--;
1853       constructionsCount--;
1854     }
1855     if (constructionsCount > 0) {
1856       if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1857         delta--;
1858       constructionsCount--;
1859     }
1860     return delta;
1861   }
1862
1863   /**
1864    * Pops elements until the stack is empty or the top element is
1865    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1866    * Does not remove it from the stack.
1867    */
1868   private int popInclusiveUntilBlock() {
1869     int startCount = constructionsCount;
1870     int delta = 0;
1871     for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1872       delta--;
1873       constructionsCount--;
1874     }
1875     if (constructionsCount > 0) {
1876       if (constructions[constructionsCount - 1] == BLOCK)
1877         delta--;
1878       constructionsCount--;
1879     }
1880     return delta;
1881   }
1882
1883   /** 
1884    * Pushes a block in the stack.<br>
1885    * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
1886    * pushes <code>NONINDENT_BLOCK</code> otherwise.
1887    * Creates a new bigger array if the current one is full.
1888    */
1889   private int pushBlock() {
1890     int delta = 0;
1891     if (constructionsCount == constructions.length)
1892       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1893
1894     if ((constructionsCount == 0)
1895       || (constructions[constructionsCount - 1] == BLOCK)
1896       || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1897       || (constructions[constructionsCount - 1] == TokenNamecase)) {
1898       delta++;
1899       constructions[constructionsCount++] = BLOCK;
1900     } else {
1901       constructions[constructionsCount++] = NONINDENT_BLOCK;
1902     }
1903     return delta;
1904   }
1905
1906   /** 
1907    * Pushes <code>token</code>.<br>
1908    * Creates a new bigger array if the current one is full.
1909    */
1910   private int pushControlStatement(int token) {
1911     if (constructionsCount == constructions.length)
1912       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1913     constructions[constructionsCount++] = token;
1914     return 1;
1915   }
1916
1917   private static boolean separateFirstArgumentOn(int currentToken) {
1918     //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
1919     return currentToken != TokenNameif
1920       && currentToken != TokenNameLPAREN
1921       && currentToken != TokenNameNOT
1922       && currentToken != TokenNamewhile
1923       && currentToken != TokenNamefor
1924       && currentToken != TokenNameswitch;
1925   }
1926
1927   /**
1928    * Set the positions to map. The mapped positions should be retrieved using the
1929    * getMappedPositions() method.
1930    * @param positions int[]
1931    * @deprecated Set the positions to map using the format(String, int, int[]) method.
1932    * 
1933    * @see #getMappedPositions()
1934    */
1935   public void setPositionsToMap(int[] positions) {
1936     positionsToMap = positions;
1937     lineDelta = 0;
1938     globalDelta = 0;
1939     mappedPositions = new int[positions.length];
1940   }
1941
1942   /** 
1943    * Appends a space character to the current line buffer.
1944    */
1945   private void space() {
1946     currentLineBuffer.append(' ');
1947     increaseLineDelta(1);
1948   }
1949
1950   /**
1951    * Splits <code>stringToSplit</code> on the top level token<br>
1952    * If there are several identical token at the same level,
1953    * the string is cut into many pieces.
1954    * @return an object containing the operator and all the substrings
1955    * or null if the string cannot be split
1956    */
1957   public SplitLine split(String stringToSplit) {
1958     return split(stringToSplit, 0);
1959   }
1960
1961   /**
1962    * Splits <code>stringToSplit</code> on the top level token<br>
1963    * If there are several identical token at the same level,
1964    * the string is cut into many pieces.
1965    * @return an object containing the operator and all the substrings
1966    * or null if the string cannot be split
1967    */
1968   public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1969     /*
1970      * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
1971      * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387 
1972      */
1973     if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1974       return null;
1975     }
1976     // local variables
1977     int currentToken = 0;
1978     int splitTokenType = 0;
1979     int splitTokenDepth = Integer.MAX_VALUE;
1980     int splitTokenPriority = Integer.MAX_VALUE;
1981
1982     int[] substringsStartPositions = new int[10];
1983     // contains the start position of substrings
1984     int[] substringsEndPositions = new int[10];
1985     // contains the start position of substrings
1986     int substringsCount = 1; // index in the substringsStartPosition array
1987     int[] splitOperators = new int[10];
1988     // contains the start position of substrings
1989     int splitOperatorsCount = 0; // index in the substringsStartPosition array
1990     int[] openParenthesisPosition = new int[10];
1991     int openParenthesisPositionCount = 0;
1992     int position = 0;
1993     int lastOpenParenthesisPosition = -1;
1994     // used to remember the position of the 1st open parenthesis
1995     // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
1996     // setup the scanner with a new source
1997     int lastCommentStartPosition = -1;
1998     // to remember the start position of the last comment
1999     int firstTokenOnLine = -1;
2000     // to remember the first token of the line
2001     int previousToken = -1;
2002     // to remember the previous token.
2003     splitScanner.setSource(stringToSplit.toCharArray());
2004
2005     try {
2006       // start the loop
2007       while (true) {
2008         // takes the next token
2009         try {
2010           if (currentToken != Scanner.TokenNameWHITESPACE)
2011             previousToken = currentToken;
2012           currentToken = splitScanner.getNextToken();
2013           if (Scanner.DEBUG) {
2014             int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2015             int currentStartPosition = splitScanner.getCurrentTokenStartPosition();
2016
2017             System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
2018             System.out.println(scanner.toStringAction(currentToken));
2019           }
2020         } catch (InvalidInputException e) {
2021           if (!handleInvalidToken(e))
2022             throw e;
2023           currentToken = 0;
2024           // this value is not modify when an exception is raised.
2025         }
2026         if (currentToken == TokenNameEOF)
2027           break;
2028
2029         if (firstTokenOnLine == -1) {
2030           firstTokenOnLine = currentToken;
2031         }
2032         switch (currentToken) {
2033           case TokenNameRBRACE :
2034           case TokenNameRPAREN :
2035             if (openParenthesisPositionCount > 0) {
2036               if (openParenthesisPositionCount == 1 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2037                 lastOpenParenthesisPosition = openParenthesisPosition[0];
2038               } else if (
2039                 (splitTokenDepth == Integer.MAX_VALUE)
2040                   || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2041                 splitTokenType = 0;
2042                 splitTokenDepth = openParenthesisPositionCount;
2043                 splitTokenPriority = Integer.MAX_VALUE;
2044                 substringsStartPositions[0] = 0;
2045                 // better token means the whole line until now is the first substring
2046                 substringsCount = 1; // resets the count of substrings
2047                 substringsEndPositions[0] = openParenthesisPosition[0];
2048                 // substring ends on operator start
2049                 position = openParenthesisPosition[0];
2050                 // the string mustn't be cut before the closing parenthesis but after the opening one.
2051                 splitOperatorsCount = 1; // resets the count of split operators
2052                 splitOperators[0] = 0;
2053               }
2054               openParenthesisPositionCount--;
2055             }
2056             break;
2057           case TokenNameLBRACE :
2058           case TokenNameLPAREN :
2059             if (openParenthesisPositionCount == openParenthesisPosition.length) {
2060               System.arraycopy(
2061                 openParenthesisPosition,
2062                 0,
2063                 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2064                 0,
2065                 openParenthesisPositionCount);
2066             }
2067             openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2068             if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) {
2069               openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2070             }
2071             break;
2072           case TokenNameSEMICOLON : // ;
2073           case TokenNameCOMMA : // ,
2074           case TokenNameEQUAL : // =
2075             if (openParenthesisPositionCount < splitTokenDepth
2076               || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2077               // the current token is better than the one we currently have
2078               // (in level or in priority if same level)
2079               // reset the substringsCount
2080               splitTokenDepth = openParenthesisPositionCount;
2081               splitTokenType = currentToken;
2082               splitTokenPriority = getTokenPriority(currentToken);
2083               substringsStartPositions[0] = 0;
2084               // better token means the whole line until now is the first substring
2085
2086               if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2087                 substringsCount = 2; // resets the count of substrings
2088
2089                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2090                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2091                 substringsEndPositions[1] = splitScanner.startPosition;
2092                 splitOperatorsCount = 2; // resets the count of split operators
2093                 splitOperators[0] = 0;
2094                 splitOperators[1] = currentToken;
2095                 position = splitScanner.currentPosition;
2096                 // next substring will start from operator end
2097               } else {
2098                 substringsCount = 1; // resets the count of substrings
2099
2100                 substringsEndPositions[0] = splitScanner.startPosition;
2101                 // substring ends on operator start
2102                 position = splitScanner.currentPosition;
2103                 // next substring will start from operator end
2104                 splitOperatorsCount = 1; // resets the count of split operators
2105                 splitOperators[0] = currentToken;
2106               }
2107             } else {
2108               if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2109                 && splitTokenType != TokenNameEQUAL
2110                 && currentToken != TokenNameEQUAL) {
2111                 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
2112                 // take only the 1st = into account.
2113                 // if another token with the same priority is found,
2114                 // push the start position of the substring and
2115                 // push the token into the stack.
2116                 // create a new array object if the current one is full.
2117                 if (substringsCount == substringsStartPositions.length) {
2118                   System.arraycopy(
2119                     substringsStartPositions,
2120                     0,
2121                     (substringsStartPositions = new int[substringsCount * 2]),
2122                     0,
2123                     substringsCount);
2124                   System.arraycopy(
2125                     substringsEndPositions,
2126                     0,
2127                     (substringsEndPositions = new int[substringsCount * 2]),
2128                     0,
2129                     substringsCount);
2130                 }
2131                 if (splitOperatorsCount == splitOperators.length) {
2132                   System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2133                 }
2134                 substringsStartPositions[substringsCount] = position;
2135                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2136                 // substring ends on operator start
2137                 position = splitScanner.currentPosition;
2138                 // next substring will start from operator end
2139                 splitOperators[splitOperatorsCount++] = currentToken;
2140               }
2141             }
2142             break;
2143
2144           case TokenNameCOLON : // : (15.24)
2145             // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
2146             // indeed it might appear also behind a case statement, and we do not to break at this point.
2147             if ((splitOperatorsCount == 0) || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2148               break;
2149             }
2150           case TokenNameextends :
2151             //                  case TokenNameimplements :
2152             //                  case TokenNamethrows :
2153
2154           case TokenNameDOT : // .
2155           case TokenNameMULTIPLY : // * (15.16.1)
2156           case TokenNameDIVIDE : // / (15.16.2)
2157           case TokenNameREMAINDER : // % (15.16.3)
2158           case TokenNamePLUS : // + (15.17, 15.17.2)
2159           case TokenNameMINUS : // - (15.17.2)
2160           case TokenNameLEFT_SHIFT : // << (15.18)
2161           case TokenNameRIGHT_SHIFT : // >> (15.18)
2162             //                          case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2163           case TokenNameLESS : // < (15.19.1)
2164           case TokenNameLESS_EQUAL : // <= (15.19.1)
2165           case TokenNameGREATER : // > (15.19.1)
2166           case TokenNameGREATER_EQUAL : // >= (15.19.1)
2167             //                          case TokenNameinstanceof : // instanceof
2168           case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2169           case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2170           case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2171                                         case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2172           case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
2173           case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
2174           case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
2175           case TokenNameAND_AND : // && (15.22)
2176           case TokenNameOR_OR : // || (15.23)
2177           case TokenNameQUESTION : // ? (15.24)
2178           case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
2179           case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
2180           case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
2181           case TokenNamePLUS_EQUAL : // += (15.25.2)
2182           case TokenNameMINUS_EQUAL : // -= (15.25.2)
2183           case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
2184           case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
2185             //                                  case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2186           case TokenNameAND_EQUAL : // &= (15.25.2)
2187           case TokenNameXOR_EQUAL : // ^= (15.25.2)
2188           case TokenNameOR_EQUAL : // |= (15.25.2)
2189
2190             if ((openParenthesisPositionCount < splitTokenDepth
2191               || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2192               && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS)
2193                 && (previousToken == TokenNameLBRACE || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2194               // the current token is better than the one we currently have
2195               // (in level or in priority if same level)
2196               // reset the substringsCount
2197               splitTokenDepth = openParenthesisPositionCount;
2198               splitTokenType = currentToken;
2199               splitTokenPriority = getTokenPriority(currentToken);
2200               substringsStartPositions[0] = 0;
2201               // better token means the whole line until now is the first substring
2202
2203               if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2204                 substringsCount = 2; // resets the count of substrings
2205
2206                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2207                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2208                 substringsEndPositions[1] = splitScanner.startPosition;
2209                 splitOperatorsCount = 3; // resets the count of split operators
2210                 splitOperators[0] = 0;
2211                 splitOperators[1] = 0;
2212                 splitOperators[2] = currentToken;
2213                 position = splitScanner.currentPosition;
2214                 // next substring will start from operator end
2215               } else {
2216                 substringsCount = 1; // resets the count of substrings
2217
2218                 substringsEndPositions[0] = splitScanner.startPosition;
2219                 // substring ends on operator start
2220                 position = splitScanner.currentPosition;
2221                 // next substring will start from operator end
2222                 splitOperatorsCount = 2; // resets the count of split operators
2223                 splitOperators[0] = 0;
2224                 // nothing for first operand since operator will be inserted in front of the second operand
2225                 splitOperators[1] = currentToken;
2226
2227               }
2228             } else {
2229               if (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken)) {
2230                 // if another token with the same priority is found,
2231                 // push the start position of the substring and
2232                 // push the token into the stack.
2233                 // create a new array object if the current one is full.
2234                 if (substringsCount == substringsStartPositions.length) {
2235                   System.arraycopy(
2236                     substringsStartPositions,
2237                     0,
2238                     (substringsStartPositions = new int[substringsCount * 2]),
2239                     0,
2240                     substringsCount);
2241                   System.arraycopy(
2242                     substringsEndPositions,
2243                     0,
2244                     (substringsEndPositions = new int[substringsCount * 2]),
2245                     0,
2246                     substringsCount);
2247                 }
2248                 if (splitOperatorsCount == splitOperators.length) {
2249                   System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2250                 }
2251                 substringsStartPositions[substringsCount] = position;
2252                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2253                 // substring ends on operator start
2254                 position = splitScanner.currentPosition;
2255                 // next substring will start from operator end
2256                 splitOperators[splitOperatorsCount++] = currentToken;
2257               }
2258             }
2259           default :
2260             break;
2261         }
2262         if (isComment(currentToken)) {
2263           lastCommentStartPosition = splitScanner.startPosition;
2264         } else {
2265           lastCommentStartPosition = -1;
2266         }
2267       }
2268     } catch (InvalidInputException e) {
2269       return null;
2270     }
2271     // if the string cannot be split, return null.
2272     if (splitOperatorsCount == 0)
2273       return null;
2274
2275     // ## SPECIAL CASES BEGIN
2276     if (((splitOperatorsCount == 2
2277       && splitOperators[1] == TokenNameDOT
2278       && splitTokenDepth == 0
2279       && lastOpenParenthesisPosition > -1)
2280       || (splitOperatorsCount > 2
2281         && splitOperators[1] == TokenNameDOT
2282         && splitTokenDepth == 0
2283         && lastOpenParenthesisPosition > -1
2284         && lastOpenParenthesisPosition <= options.maxLineLength)
2285       || (separateFirstArgumentOn(firstTokenOnLine) && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2286       && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2287       // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
2288       // only one split on a top level .
2289       // or more than one split on . and substring before open parenthesis fits one line.
2290       // or split inside parenthesis and first token is not a for/while/if
2291       SplitLine sl = split(stringToSplit.substring(lastOpenParenthesisPosition), lastOpenParenthesisPosition);
2292       if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2293         // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
2294         return new SplitLine(
2295           new int[] { 0, 0 },
2296           new String[] {
2297             stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2298             stringToSplit.substring(lastOpenParenthesisPosition)},
2299           new int[] { offsetInGlobalLine, lastOpenParenthesisPosition + offsetInGlobalLine });
2300       } else {
2301         // right substring can be split and is split on comma
2302         // copy substrings and operators
2303         // except if the 1st string is empty.
2304         int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2305         int subStringsLength = sl.substrings.length + 1 - startIndex;
2306         String[] result = new String[subStringsLength];
2307         int[] startIndexes = new int[subStringsLength];
2308         int operatorsLength = sl.operators.length + 1 - startIndex;
2309         int[] operators = new int[operatorsLength];
2310
2311         result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2312         operators[0] = 0;
2313
2314         System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes, 1, subStringsLength - 1);
2315         for (int i = subStringsLength - 1; i >= 0; i--) {
2316           startIndexes[i] += offsetInGlobalLine;
2317         }
2318         System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1);
2319         System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1);
2320
2321         return new SplitLine(operators, result, startIndexes);
2322       }
2323     }
2324     // if the last token is a comment and the substring before the comment fits on a line,
2325     // split before the comment and return the result.
2326     if (lastCommentStartPosition > -1 && lastCommentStartPosition < options.maxLineLength && splitTokenPriority > 50) {
2327       int end = lastCommentStartPosition;
2328       int start = lastCommentStartPosition;
2329       if (stringToSplit.charAt(end - 1) == ' ') {
2330         end--;
2331       }
2332       if (start != end && stringToSplit.charAt(start) == ' ') {
2333         start++;
2334       }
2335       return new SplitLine(
2336         new int[] { 0, 0 },
2337         new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)},
2338         new int[] { 0, start });
2339     }
2340     if (position != stringToSplit.length()) {
2341       if (substringsCount == substringsStartPositions.length) {
2342         System.arraycopy(
2343           substringsStartPositions,
2344           0,
2345           (substringsStartPositions = new int[substringsCount * 2]),
2346           0,
2347           substringsCount);
2348         System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount * 2]), 0, substringsCount);
2349       }
2350       // avoid empty extra substring, e.g. line terminated with a semi-colon
2351       substringsStartPositions[substringsCount] = position;
2352       substringsEndPositions[substringsCount++] = stringToSplit.length();
2353     }
2354     if (splitOperatorsCount == splitOperators.length) {
2355       System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2356     }
2357     splitOperators[splitOperatorsCount] = 0;
2358
2359     // the last element of the stack is the position of the end of StringToSPlit
2360     // +1 because the substring method excludes the last character
2361     String[] result = new String[substringsCount];
2362     for (int i = 0; i < substringsCount; i++) {
2363       int start = substringsStartPositions[i];
2364       int end = substringsEndPositions[i];
2365       if (stringToSplit.charAt(start) == ' ') {
2366         start++;
2367         substringsStartPositions[i]++;
2368       }
2369       if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2370         end--;
2371       }
2372       result[i] = stringToSplit.substring(start, end);
2373       substringsStartPositions[i] += offsetInGlobalLine;
2374     }
2375     if (splitOperatorsCount > substringsCount) {
2376       System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[splitOperatorsCount]), 0, substringsCount);
2377       System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[splitOperatorsCount]), 0, substringsCount);
2378       for (int i = substringsCount; i < splitOperatorsCount; i++) {
2379         substringsStartPositions[i] = position;
2380         substringsEndPositions[i] = position;
2381       }
2382       System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount]), 0, splitOperatorsCount);
2383     } else {
2384       System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[substringsCount]), 0, substringsCount);
2385       System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount]), 0, substringsCount);
2386       System.arraycopy(splitOperators, 0, (splitOperators = new int[substringsCount]), 0, substringsCount);
2387     }
2388     SplitLine splitLine = new SplitLine(splitOperators, result, substringsStartPositions);
2389     return splitLine;
2390   }
2391
2392   private void updateMappedPositions(int startPosition) {
2393     if (positionsToMap == null) {
2394       return;
2395     }
2396     char[] source = scanner.source;
2397     int sourceLength = source.length;
2398     while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2399       int posToMap = positionsToMap[indexToMap];
2400       if (posToMap < 0 || posToMap >= sourceLength) {
2401         // protection against out of bounds position
2402         if (posToMap == sourceLength) {
2403           mappedPositions[indexToMap] = formattedSource.length();
2404         }
2405         indexToMap = positionsToMap.length; // no more mapping
2406         return;
2407       }
2408       if (CharOperation.isWhitespace(source[posToMap])) {
2409         mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2410       } else {
2411         if (posToMap == sourceLength - 1) {
2412           mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2413         } else {
2414           mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2415         }
2416       }
2417       indexToMap++;
2418     }
2419   }
2420
2421   private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2422     if (mappedPositions == null || mappedPositions.length == indexInMap)
2423       return;
2424
2425     while (indexInMap < mappedPositions.length
2426       && startPosition <= mappedPositions[indexInMap]
2427       && mappedPositions[indexInMap] < endPosition
2428       && indexInMap < indexToMap) {
2429       mappedPositions[indexInMap] += splitDelta;
2430       indexInMap++;
2431     }
2432   }
2433
2434   private int getLength(String s, int tabDepth) {
2435     int length = 0;
2436     for (int i = 0; i < tabDepth; i++) {
2437       length += options.tabSize;
2438     }
2439     for (int i = 0, max = s.length(); i < max; i++) {
2440       char currentChar = s.charAt(i);
2441       switch (currentChar) {
2442         case '\t' :
2443           length += options.tabSize;
2444           break;
2445         default :
2446           length++;
2447       }
2448     }
2449     return length;
2450   }
2451
2452   /** 
2453   * Sets the initial indentation level
2454   * @param indentationLevel new indentation level
2455   * 
2456   * @deprecated
2457   */
2458   public void setInitialIndentationLevel(int newIndentationLevel) {
2459     this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;
2460   }
2461 }