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