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