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 
 
   6  * which accompanies this distribution, and is available at
 
   7  * http://www.eclipse.org/legal/cpl-v05.html
 
  10  *     IBM Corporation - initial API and implementation
 
  11  ******************************************************************************/
 
  12 package net.sourceforge.phpdt.internal.formatter;
 
  14 import java.io.BufferedReader;
 
  15 import java.io.IOException;
 
  16 import java.io.StringReader;
 
  17 import java.util.Hashtable;
 
  18 import java.util.Locale;
 
  21 import net.sourceforge.phpdt.core.ICodeFormatter;
 
  22 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  23 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
 
  24 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
 
  25 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
 
  26 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
 
  27 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
 
  28 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
 
  31  * <h2>How to format a piece of code ?</h2>
 
  33  * <li>Create an instance of <code>CodeFormatter</code>
 
  34  * <li>Use the method <code>void format(aString)</code> on this instance to format <code>aString</code>. It will return the
 
  38 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
 
  39   // IContentFormatterExtension {
 
  40   public FormatterOptions options;
 
  43    * Represents a block in the <code>constructions</code> stack.
 
  45   public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
 
  48    * Represents a block following a control statement in the <code>constructions</code> stack.
 
  50   public static final int NONINDENT_BLOCK = -100;
 
  53    * Contains the formatted output.
 
  55   StringBuffer formattedSource;
 
  58    * Contains the current line. <br>
 
  59    * Will be dumped at the next "newline"
 
  61   StringBuffer currentLineBuffer;
 
  64    * Used during the formatting to get each token.
 
  69    * Contains the tokens responsible for the current indentation level and the blocks not closed yet.
 
  71   private int[] constructions;
 
  74    * Index in the <code>constructions</code> array.
 
  76   private int constructionsCount;
 
  79    * Level of indentation of the current token (number of tab char put in front of it).
 
  81   private int indentationLevel;
 
  84    * Regular level of indentation of all the lines
 
  86   private int initialIndentationLevel;
 
  89    * Used to split a line.
 
  94    * To remember the offset between the beginning of the line and the beginning of the comment.
 
  96   int currentCommentOffset;
 
  98   int currentLineIndentationLevel;
 
 100   int maxLineSize = 30;
 
 102   private boolean containsOpenCloseBraces;
 
 104   private int indentationLevelForOpenCloseBraces;
 
 107    * Collections of positions to map
 
 109   private int[] positionsToMap;
 
 112    * Collections of mapped positions
 
 114   private int[] mappedPositions;
 
 116   private int indexToMap;
 
 118   private int indexInMap;
 
 120   private int globalDelta;
 
 122   private int lineDelta;
 
 124   private int splitDelta;
 
 126   private int beginningOfLineIndex;
 
 128   private int multipleLineCommentCounter;
 
 131    * Creates a new instance of Code Formatter using the given settings.
 
 133    * @deprecated backport 1.0 internal functionality
 
 135   public CodeFormatter(ConfigurableOption[] settings) {
 
 136     this(convertConfigurableOptions(settings));
 
 140    * Creates a new instance of Code Formatter using the FormattingOptions object given as argument
 
 142    * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
 
 144   public CodeFormatter() {
 
 149    * Creates a new instance of Code Formatter using the given settings.
 
 151   public CodeFormatter(Map settings) {
 
 152     // initialize internal state
 
 153     constructionsCount = 0;
 
 154     constructions = new int[10];
 
 155     currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
 
 156     currentCommentOffset = -1;
 
 157     // initialize primary and secondary scanners
 
 158     scanner = new Scanner(true /* comment */
 
 159     , true /* whitespace */
 
 162     , true, /* tokenizeStrings */
 
 163     null, null, true /*taskCaseSensitive*/); // regular scanner for forming lines
 
 164     scanner.recordLineSeparator = true;
 
 165     scanner.ignorePHPOneLiner = true;
 
 166     // to remind of the position of the beginning of the line.
 
 167     splitScanner = new Scanner(true /* comment */
 
 168     , true /* whitespace */
 
 171     , true, /* tokenizeStrings */
 
 172     null, null, true /*taskCaseSensitive*/);
 
 173     splitScanner.ignorePHPOneLiner = true;
 
 174     // secondary scanner to split long lines formed by primary scanning
 
 175     // initialize current line buffer
 
 176     currentLineBuffer = new StringBuffer();
 
 177     this.options = new FormatterOptions(settings);
 
 181    * Returns true if a lineSeparator has to be inserted before <code>operator</code> false otherwise.
 
 183   private static boolean breakLineBeforeOperator(int operator) {
 
 186     case TokenNameSEMICOLON:
 
 195    * @deprecated backport 1.0 internal functionality
 
 197   private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
 
 198     Hashtable options = new Hashtable(10);
 
 199     for (int i = 0; i < settings.length; i++) {
 
 200       if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
 
 201         String optionName = settings[i].getOptionName();
 
 202         int valueIndex = settings[i].getCurrentValueIndex();
 
 203         if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
 
 204           options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 205         } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
 
 207               .put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 208         } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
 
 209           options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 210         } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
 
 211           options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 212         } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
 
 213           options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 214         } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
 
 215           options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 216         } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
 
 217           options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 218         } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
 
 219           options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 220         } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
 
 221           options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 229    * Returns the end of the source code.
 
 231   private final String copyRemainingSource() {
 
 232     char str[] = scanner.source;
 
 233     int startPosition = scanner.startPosition;
 
 234     int length = str.length - startPosition;
 
 235     StringBuffer bufr = new StringBuffer(length);
 
 236     if (startPosition < str.length) {
 
 237       bufr.append(str, startPosition, length);
 
 239     return (bufr.toString());
 
 243    * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
 
 245   private void dumpTab(int tabCount) {
 
 246     if (options.indentWithTab) {
 
 247       for (int j = 0; j < tabCount; j++) {
 
 248         formattedSource.append('\t');
 
 249         increaseSplitDelta(1);
 
 252       for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
 
 253         formattedSource.append(' ');
 
 254         increaseSplitDelta(1);
 
 260    * Dumps <code>currentLineBuffer</code> into the formatted string.
 
 262   private void flushBuffer() {
 
 263     String currentString = currentLineBuffer.toString();
 
 265     beginningOfLineIndex = formattedSource.length();
 
 266     if (containsOpenCloseBraces) {
 
 267       containsOpenCloseBraces = false;
 
 268       outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
 
 269       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 271       outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
 
 273     int scannerSourceLength = scanner.source.length;
 
 274     if ((scannerSourceLength > 2) && (scanner.startPosition < scannerSourceLength)) {
 
 275       if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
 
 276         formattedSource.append(options.lineSeparatorSequence);
 
 277         increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
 278       } else if (scanner.source[scannerSourceLength - 1] == '\n') {
 
 279         formattedSource.append(options.lineSeparatorSequence);
 
 280         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 281       } else if (scanner.source[scannerSourceLength - 1] == '\r') {
 
 282         formattedSource.append(options.lineSeparatorSequence);
 
 283         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 286     updateMappedPositions(scanner.startPosition);
 
 290    * Formats the input string.
 
 292   private void format() {
 
 294     int previousToken = 0;
 
 295     int previousCompilableToken = 0;
 
 296     int indentationOffset = 0;
 
 297     int newLinesInWhitespace = 0;
 
 298     // number of new lines in the previous whitespace token
 
 299     // (used to leave blank lines before comments)
 
 300     int pendingNewLines = 0;
 
 301     boolean expectingOpenBrace = false;
 
 302     boolean clearNonBlockIndents = false;
 
 303     // true if all indentations till the 1st { (usefull after } or ;)
 
 304     boolean pendingSpace = true;
 
 305     boolean pendingNewlineAfterParen = false;
 
 306     // true when a cr is to be put after a ) (in conditional statements)
 
 307     boolean inAssignment = false;
 
 308     boolean inArrayAssignment = false;
 
 309     boolean inThrowsClause = false;
 
 310     boolean inClassOrInterfaceHeader = false;
 
 311     int dollarBraceCount = 0;
 
 312     // openBracketCount is used to count the number of open brackets not closed
 
 314     int openBracketCount = 0;
 
 315     int unarySignModifier = 0;
 
 316     // openParenthesis[0] is used to count the parenthesis not belonging to a
 
 318     // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
 
 319     int openParenthesisCount = 1;
 
 320     int[] openParenthesis = new int[10];
 
 321     // tokenBeforeColon is used to know what token goes along with the current
 
 323     // it can be case or ?
 
 324     int tokenBeforeColonCount = 0;
 
 325     int[] tokenBeforeColon = new int[10];
 
 326     constructionsCount = 0; // initializes the constructions count.
 
 327     // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
 
 329     // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
 
 330     boolean specialElse = false;
 
 331     // OPTION (IndentationLevel): initial indentation level may be non-zero.
 
 332     currentLineIndentationLevel += constructionsCount;
 
 333     // An InvalidInputException exception might cause the termination of this
 
 335     int arrayDeclarationCount=0;
 
 336         int[] arrayDeclarationParenthesis=new int[10];
 
 339         // Get the next token. Catch invalid input and output it
 
 340         // with minimal formatting, also catch end of input and
 
 343           token = scanner.getNextToken();
 
 345             int currentEndPosition = scanner.getCurrentTokenEndPosition();
 
 346             int currentStartPosition = scanner.getCurrentTokenStartPosition();
 
 347             System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
 
 348             System.out.println(scanner.toStringAction(token));
 
 350           // Patch for line comment
 
 351           // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
 
 352           if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
 
 353             int length = scanner.currentPosition;
 
 354             loop: for (int index = length - 1; index >= 0; index--) {
 
 355               switch (scanner.source[index]) {
 
 358                 scanner.currentPosition--;
 
 365         } catch (InvalidInputException e) {
 
 366           if (!handleInvalidToken(e)) {
 
 371         if (token == Scanner.TokenNameEOF) {
 
 373         } else if (token == Scanner.TokenNameHEREDOC) {
 
 374           // no indentation for heredocs and HTML !
 
 375           outputCurrentTokenWithoutIndent(Scanner.TokenNameHEREDOC, 0);
 
 377         } else if (token == Scanner.TokenNameINLINE_HTML) {
 
 378           // no indentation for heredocs and HTML !
 
 379           int newLineCount = 1;
 
 380           if (scanner.startPosition==0) {
 
 383           outputCurrentTokenWithoutIndent(Scanner.TokenNameINLINE_HTML, newLineCount);
 
 384           int srcLen = scanner.source.length;
 
 385           if (scanner.currentPosition < srcLen-1) {
 
 391          * ## MODIFYING the indentation level before generating new lines and indentation in the output string
 
 393         // Removes all the indentations made by statements not followed by a
 
 395         // except if the current token is ELSE, CATCH or if we are in a
 
 397         if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
 
 400             if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
 
 404             indentationLevel += popInclusiveUntil(TokenNameif);
 
 406           //                                            case TokenNamecatch :
 
 407           //                                                    indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 409           //                                            case TokenNamefinally :
 
 410           //                                                    indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 413             if (nlicsToken == TokenNamedo) {
 
 414               indentationLevel += pop(TokenNamedo);
 
 418             indentationLevel += popExclusiveUntilBlockOrCase();
 
 419           // clear until a CASE, DEFAULT or BLOCK is encountered.
 
 420           // Thus, the indentationLevel is correctly cleared either
 
 421           // in a switch/case statement or in any other situation.
 
 423           clearNonBlockIndents = false;
 
 425         // returns to the indentation level created by the SWITCH keyword
 
 426         // if the current token is a CASE or a DEFAULT
 
 427         if (token == TokenNamecase || token == TokenNamedefault) {
 
 428           indentationLevel += pop(TokenNamecase);
 
 430         //                              if (token == Scanner.TokenNamethrows) {
 
 431         //                                      inThrowsClause = true;
 
 433         if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface) && previousToken != Scanner.TokenNameDOT) {
 
 434           inClassOrInterfaceHeader = true;
 
 437          * ## APPEND newlines and indentations to the output string
 
 439         // Do not add a new line between ELSE and IF, if the option
 
 440         // elseIfOnSameLine is true.
 
 441         // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
 
 442         //        if (pendingNewlineAfterParen
 
 443         //          && previousCompilableToken == TokenNameelse
 
 444         //          && token == TokenNameif
 
 445         //          && options.compactElseIfMode) {
 
 446         //          pendingNewlineAfterParen = false;
 
 447         //          pendingNewLines = 0;
 
 448         //          indentationLevel += pop(TokenNameelse);
 
 449         //          // because else if is now one single statement,
 
 450         //          // the indentation level after it is increased by one and not by 2
 
 451         //          // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
 
 453         // Add a newline & indent to the formatted source string if
 
 454         // a for/if-else/while statement was scanned and there is no block
 
 456         pendingNewlineAfterParen = pendingNewlineAfterParen
 
 457             || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
 
 458         if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
 
 459           pendingNewlineAfterParen = false;
 
 460           // Do to add a newline & indent sequence if the current token is an
 
 461           // open brace or a period or if the current token is a semi-colon and
 
 463           // previous token is a close paren.
 
 464           // add a new line if a parenthesis belonging to a for() statement
 
 465           // has been closed and the current token is not an opening brace
 
 466           if (token != TokenNameLBRACE && !isComment(token)
 
 467           // to avoid adding new line between else and a comment
 
 468               && token != TokenNameDOT && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
 
 470             currentLineIndentationLevel = indentationLevel;
 
 472             pendingSpace = false;
 
 474             if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
 
 476               if (constructionsCount > 0 && constructions[constructionsCount - 1] != BLOCK
 
 477                   && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
 
 478                 currentLineIndentationLevel = indentationLevel - 1;
 
 480                 currentLineIndentationLevel = indentationLevel;
 
 483               pendingSpace = false;
 
 487         if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode && constructionsCount > 0
 
 488             && constructions[constructionsCount - 1] == TokenNamedo) {
 
 490           currentLineIndentationLevel = indentationLevel - 1;
 
 492           pendingSpace = false;
 
 495         if (token == TokenNameLBRACE && inThrowsClause) {
 
 496           inThrowsClause = false;
 
 497           if (options.newLineBeforeOpeningBraceMode) {
 
 499             currentLineIndentationLevel = indentationLevel;
 
 501             pendingSpace = false;
 
 505         if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
 
 506           inClassOrInterfaceHeader = false;
 
 507           if (options.newLineBeforeOpeningBraceMode) {
 
 509             currentLineIndentationLevel = indentationLevel;
 
 511             pendingSpace = false;
 
 514         // Add pending new lines to the formatted source string.
 
 515         // Note: pending new lines are not added if the current token
 
 516         // is a single line comment or whitespace.
 
 517         // if the comment is between parenthesis, there is no blank line
 
 519         // (if it's a one-line comment, a blank line is added after it).
 
 520         if (((pendingNewLines > 0 && (!isComment(token)))
 
 521             || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
 
 522             && token != Scanner.TokenNameWHITESPACE) {
 
 523           // Do not add newline & indent between an adjoining close brace and
 
 524           // close paren. Anonymous inner classes may use this form.
 
 525           boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
 
 526           // OPTION (NewLineInCompoundStatement): do not add newline & indent
 
 527           // between close brace and else, (do) while, catch, and finally if
 
 528           // newlineInCompoundStatement is true.
 
 529           boolean nlicsOption = previousToken == TokenNameRBRACE
 
 530               && !options.newlineInControlStatementMode
 
 531               && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo) || token == TokenNamecatch || token == TokenNamefinally);
 
 532           // Do not add a newline & indent between a close brace and
 
 534           boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
 
 535           // Do not add a new line & indent between a multiline comment and a
 
 537           boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
 
 538           // Do not add a newline & indent between a close brace and a colon
 
 539           // (in array assignments, for example).
 
 540           boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
 
 541           // Add a newline and indent, if appropriate.
 
 543               || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
 
 544             // if clearAllBlankLinesMode=false, leaves the blank lines
 
 545             // inserted by the user
 
 546             // if clearAllBlankLinesMode=true, removes all of then
 
 547             // and insert only blank lines required by the formatting.
 
 548             if (!options.clearAllBlankLinesMode) {
 
 549               //  (isComment(token))
 
 550               pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
 
 551               pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
 
 553             if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
 
 554               containsOpenCloseBraces = true;
 
 555               indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 556               if (isComment(previousToken)) {
 
 557                 newLine(pendingNewLines);
 
 560                  * if (!(constructionsCount > 1 && constructions[constructionsCount-1] == NONINDENT_BLOCK &&
 
 561                  * (constructions[constructionsCount-2] == TokenNamefor
 
 563                 if (options.newLineInEmptyBlockMode) {
 
 564                   if (inArrayAssignment) {
 
 565                     newLine(1); // array assigment with an empty block
 
 567                     newLine(pendingNewLines);
 
 573               // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
 
 575               if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
 
 576                 newLine(pendingNewLines);
 
 579             if (((previousCompilableToken == TokenNameSEMICOLON) || (previousCompilableToken == TokenNameLBRACE)
 
 580                 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
 
 581                 && (token == TokenNameRBRACE)) {
 
 582               indentationOffset = -1;
 
 583               indentationLevel += popExclusiveUntilBlock();
 
 585             if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
 
 587               currentLineIndentationLevel++;
 
 589               currentLineIndentationLevel = indentationLevel + indentationOffset;
 
 591             pendingSpace = false;
 
 592             indentationOffset = 0;
 
 595           newLinesInWhitespace = 0;
 
 597           if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
 
 601         boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
 
 603         //          case TokenNameDOLLAR :
 
 604         //            dollarBraceCount++;
 
 607           //                            case TokenNamefinally :
 
 608           expectingOpenBrace = true;
 
 609           pendingNewlineAfterParen = true;
 
 610           indentationLevel += pushControlStatement(token);
 
 613         case TokenNamedefault:
 
 614           if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 616                 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
 
 618           tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
 
 619           indentationLevel += pushControlStatement(TokenNamecase);
 
 621         case TokenNameQUESTION:
 
 622           if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 624                 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
 
 626           tokenBeforeColon[tokenBeforeColonCount++] = token;
 
 628         case TokenNameswitch:
 
 630         case TokenNameforeach:
 
 633           if (openParenthesisCount == openParenthesis.length) {
 
 634             System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
 
 636           openParenthesis[openParenthesisCount++] = 0;
 
 637           expectingOpenBrace = true;
 
 638           indentationLevel += pushControlStatement(token);
 
 641           pendingNewlineAfterParen = true;
 
 643           // several CATCH statements can be contiguous.
 
 644           // a CATCH is encountered pop until first CATCH (if a CATCH
 
 645           // follows a TRY it works the same way,
 
 646           // as CATCH and TRY are the same token in the stack).
 
 647           expectingOpenBrace = true;
 
 648           indentationLevel += pushControlStatement(TokenNamecatch);
 
 651           expectingOpenBrace = true;
 
 652           indentationLevel += pushControlStatement(token);
 
 657         case TokenNameLPAREN:
 
 658           //                                            if (previousToken == TokenNamesynchronized) {
 
 659           //                                                    indentationLevel += pushControlStatement(previousToken);
 
 661           // Put a space between the previous and current token if the
 
 662           // previous token was not a keyword, open paren, logical
 
 663           // compliment (eg: !), semi-colon, open brace, close brace,
 
 665           if (previousCompilableToken != TokenNameLBRACKET && previousToken != TokenNameIdentifier && previousToken != 0
 
 666               && previousToken != TokenNameNOT && previousToken != TokenNameLPAREN && previousToken != TokenNameTWIDDLE
 
 667               && previousToken != TokenNameSEMICOLON && previousToken != TokenNameLBRACE && previousToken != TokenNameRBRACE
 
 668               && previousToken != TokenNamesuper) {
 
 669             //  && previousToken != TokenNamethis) {
 
 672           // If in a for/if/while statement, increase the parenthesis count
 
 673           // for the current openParenthesisCount
 
 674           // else increase the count for stand alone parenthesis.
 
 675           if (openParenthesisCount > 0)
 
 676             openParenthesis[openParenthesisCount - 1]++;
 
 678             openParenthesis[0]++;
 
 679           pendingSpace = false;
 
 680           // recognize array declaration for nice output
 
 681           if (previousCompilableToken == TokenNamearray) {
 
 682                         arrayDeclarationCount++;
 
 683                         arrayDeclarationParenthesis[arrayDeclarationCount]=openParenthesis[openParenthesisCount];
 
 689         case TokenNameRPAREN:
 
 690             // check for closing array declaration
 
 691             if (arrayDeclarationCount>0) {
 
 692                 if (arrayDeclarationParenthesis[arrayDeclarationCount]==openParenthesis[openParenthesisCount]) {
 
 695                         currentLineIndentationLevel = indentationLevel;
 
 697                         arrayDeclarationCount--;
 
 700           // Decrease the parenthesis count
 
 701           // if there is no more unclosed parenthesis,
 
 702           // a new line and indent may be append (depending on the next
 
 704           if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
 
 705             openParenthesis[openParenthesisCount - 1]--;
 
 706             if (openParenthesis[openParenthesisCount - 1] <= 0) {
 
 707               pendingNewlineAfterParen = true;
 
 708               inAssignment = false;
 
 709               openParenthesisCount--;
 
 712             openParenthesis[0]--;
 
 714           pendingSpace = false;
 
 716         case TokenNameLBRACE:
 
 717           if (previousCompilableToken == TokenNameDOLLAR) {
 
 720             if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
 
 721               //                  if (previousCompilableToken == TokenNameRBRACKET) {
 
 722               inArrayAssignment = true;
 
 723               inAssignment = false;
 
 725             if (inArrayAssignment) {
 
 726               indentationLevel += pushBlock();
 
 728               // Add new line and increase indentation level after open brace.
 
 730               indentationLevel += pushBlock();
 
 734         case TokenNameRBRACE:
 
 735           if (dollarBraceCount > 0) {
 
 739           if (previousCompilableToken == TokenNameRPAREN) {
 
 740             pendingSpace = false;
 
 742           if (inArrayAssignment) {
 
 743             inArrayAssignment = false;
 
 745             indentationLevel += popInclusiveUntilBlock();
 
 748             indentationLevel += popInclusiveUntilBlock();
 
 749             if (previousCompilableToken == TokenNameRPAREN) {
 
 750               // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
 
 752               currentLineBuffer.append(options.lineSeparatorSequence);
 
 753               increaseLineDelta(options.lineSeparatorSequence.length);
 
 755             if (constructionsCount > 0) {
 
 756               switch (constructions[constructionsCount - 1]) {
 
 758               case TokenNameforeach:
 
 759               //indentationLevel += popExclusiveUntilBlock();
 
 761               case TokenNameswitch:
 
 766               case TokenNamefinally:
 
 769                 //                                                                      case TokenNamesynchronized :
 
 770                 clearNonBlockIndents = true;
 
 777         case TokenNameLBRACKET:
 
 779           pendingSpace = false;
 
 781         case TokenNameRBRACKET:
 
 782           openBracketCount -= (openBracketCount > 0) ? 1 : 0;
 
 783           // if there is no left bracket to close, the right bracket is
 
 785           pendingSpace = false;
 
 788           pendingSpace = false;
 
 789           if (arrayDeclarationCount>0) {
 
 794           pendingSpace = false;
 
 796         case TokenNameSEMICOLON:
 
 797           // Do not generate line terminators in the definition of
 
 798           // the for statement.
 
 799           // if not in this case, jump a line and reduce indentation after
 
 801           // if the block it closes belongs to a conditional statement (if,
 
 803           if (openParenthesisCount <= 1) {
 
 805             if (expectingOpenBrace) {
 
 806               clearNonBlockIndents = true;
 
 807               expectingOpenBrace = false;
 
 810           inAssignment = false;
 
 811           pendingSpace = false;
 
 813         case TokenNamePLUS_PLUS:
 
 814         case TokenNameMINUS_MINUS:
 
 815           // Do not put a space between a post-increment/decrement
 
 816           // and the identifier being modified.
 
 817           if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET || previousToken == TokenNameVariable) {
 
 818             pendingSpace = false;
 
 822         // previously ADDITION
 
 824           // Handle the unary operators plus and minus via a flag
 
 825           if (!isLiteralToken(previousToken) && previousToken != TokenNameIdentifier && previousToken != TokenNameRPAREN
 
 826               && previousToken != TokenNameRBRACKET) {
 
 827             unarySignModifier = 1;
 
 831           // In a switch/case statement, add a newline & indent
 
 832           // when a colon is encountered.
 
 833           if (tokenBeforeColonCount > 0) {
 
 834             if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
 
 837             tokenBeforeColonCount--;
 
 843         case Scanner.TokenNameCOMMENT_LINE:
 
 846             currentLineIndentationLevel++;
 
 848           break; // a line is always inserted after a one-line comment
 
 849         case Scanner.TokenNameCOMMENT_PHPDOC:
 
 850         case Scanner.TokenNameCOMMENT_BLOCK:
 
 851           currentCommentOffset = getCurrentCommentOffset();
 
 854         case Scanner.TokenNameWHITESPACE:
 
 855           if (!phpTagAndWhitespace) {
 
 856             // Count the number of line terminators in the whitespace so
 
 857             // line spacing can be preserved near comments.
 
 858             char[] source = scanner.source;
 
 859             newLinesInWhitespace = 0;
 
 860             for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
 
 861               if (source[i] == '\r') {
 
 863                   if (source[++i] == '\n') {
 
 864                     newLinesInWhitespace++;
 
 866                     newLinesInWhitespace++;
 
 869                   newLinesInWhitespace++;
 
 871               } else if (source[i] == '\n') {
 
 872                 newLinesInWhitespace++;
 
 875             increaseLineDelta(scanner.startPosition - scanner.currentPosition);
 
 878         //          case TokenNameHTML :
 
 879         //            // Add the next token to the formatted source string.
 
 880         //            // outputCurrentToken(token);
 
 881         //            int startPosition = scanner.startPosition;
 
 883         //            for (int i = startPosition, max = scanner.currentPosition; i <
 
 885         //              char currentCharacter = scanner.source[i];
 
 886         //              updateMappedPositions(i);
 
 887         //              currentLineBuffer.append(currentCharacter);
 
 891           if ((token == TokenNameIdentifier) || isLiteralToken(token) || token == TokenNamesuper) {
 
 892             //                                                  || token == TokenNamethis) {
 
 893             // Do not put a space between a unary operator
 
 894             // (eg: ++, --, +, -) and the identifier being modified.
 
 895             if (previousToken == TokenNamePLUS_PLUS || previousToken == TokenNameMINUS_MINUS
 
 896                 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
 
 897                 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
 
 898                 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
 
 899               pendingSpace = false;
 
 901             unarySignModifier = 0;
 
 905         // Do not output whitespace tokens.
 
 906         if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
 
 908            * Add pending space to the formatted source string. Do not output a space under the following circumstances: 1) this is
 
 909            * the first pass 2) previous token is an open paren 3) previous token is a period 4) previous token is the logical
 
 910            * compliment (eg: !) 5) previous token is the bitwise compliment (eg: ~) 6) previous token is the open bracket (eg: [) 7)
 
 911            * in an assignment statement, if the previous token is an open brace or the current token is a close brace 8) previous
 
 912            * token is a single line comment 9) current token is a '->'
 
 914           if (token == TokenNameMINUS_GREATER && options.compactDereferencingMode)
 
 915             pendingSpace = false;
 
 917           boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
 
 918           if (pendingSpace && insertSpaceAfter(previousToken)
 
 919               && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
 
 920               && previousToken != Scanner.TokenNameCOMMENT_LINE) {
 
 921             if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
 
 924           // Add the next token to the formatted source string.
 
 925           outputCurrentToken(token);
 
 926           if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
 
 928             currentLineBuffer.append(options.lineSeparatorSequence);
 
 929             increaseLineDelta(options.lineSeparatorSequence.length);
 
 933         // Whitespace tokens do not need to be remembered.
 
 934         if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
 
 935           previousToken = token;
 
 936           if (token != Scanner.TokenNameCOMMENT_BLOCK && token != Scanner.TokenNameCOMMENT_LINE
 
 937               && token != Scanner.TokenNameCOMMENT_PHPDOC) {
 
 938             previousCompilableToken = token;
 
 942       output(copyRemainingSource());
 
 944       // dump the last token of the source in the formatted output.
 
 945     } catch (InvalidInputException e) {
 
 946       output(copyRemainingSource());
 
 948       // dump the last token of the source in the formatted output.
 
 953    * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
 
 955    * @return the formatted ouput.
 
 957   public String formatSourceString(String sourceString) {
 
 958     char[] sourceChars = sourceString.toCharArray();
 
 959     formattedSource = new StringBuffer(sourceChars.length);
 
 960     scanner.setSource(sourceChars);
 
 962     return formattedSource.toString();
 
 966    * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
 
 969    *          the string to format
 
 970    * @param indentationLevel
 
 971    *          the initial indentation level
 
 972    * @return the formatted ouput.
 
 974   public String format(String string, int indentationLevel) {
 
 975     return format(string, indentationLevel, (int[]) null);
 
 979    * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The positions array
 
 980    * is modified to contain the mapped positions.
 
 983    *          the string to format
 
 984    * @param indentationLevel
 
 985    *          the initial indentation level
 
 987    *          the array of positions to map
 
 988    * @return the formatted ouput.
 
 990   public String format(String string, int indentationLevel, int[] positions) {
 
 991     return this.format(string, indentationLevel, positions, null);
 
 994   public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
 
 995     if (lineSeparator != null) {
 
 996       this.options.setLineSeparator(lineSeparator);
 
 998     if (positions != null) {
 
 999       this.setPositionsToMap(positions);
 
1000       this.setInitialIndentationLevel(indentationLevel);
 
1001       String formattedString = this.formatSourceString(string);
 
1002       int[] mappedPositions = this.getMappedPositions();
 
1003       System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
 
1004       return formattedString;
 
1006       this.setInitialIndentationLevel(indentationLevel);
 
1007       return this.formatSourceString(string);
 
1012    * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The initial
 
1013    * indentation level is 0.
 
1016    *          the string to format
 
1017    * @return the formatted ouput.
 
1019   public String format(String string) {
 
1020     return this.format(string, 0, (int[]) null);
 
1024    * Formats a given source string, starting indenting it at a particular depth and using the given options
 
1026    * @deprecated backport 1.0 internal functionality
 
1028   public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
 
1029     CodeFormatter formatter = new CodeFormatter(options);
 
1030     formatter.setInitialIndentationLevel(initialIndentationLevel);
 
1031     return formatter.formatSourceString(sourceString);
 
1035    * Returns the number of characters and tab char between the beginning of the line and the beginning of the comment.
 
1037   private int getCurrentCommentOffset() {
 
1038     int linePtr = scanner.linePtr;
 
1039     // if there is no beginning of line, return 0.
 
1043     int beginningOfLine = scanner.lineEnds[linePtr];
 
1044     int currentStartPosition = scanner.startPosition;
 
1045     char[] source = scanner.source;
 
1046     // find the position of the beginning of the line containing the comment
 
1047     while (beginningOfLine > currentStartPosition) {
 
1049         beginningOfLine = scanner.lineEnds[--linePtr];
 
1051         beginningOfLine = 0;
 
1055     for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
 
1056       char currentCharacter = source[i];
 
1057       switch (currentCharacter) {
 
1059         offset += options.tabSize;
 
1075    * Returns an array of descriptions for the configurable options. The descriptions may be changed and passed back to a different
 
1078    * @deprecated backport 1.0 internal functionality
 
1080   public static ConfigurableOption[] getDefaultOptions(Locale locale) {
 
1081     String componentName = CodeFormatter.class.getName();
 
1082     FormatterOptions options = new FormatterOptions();
 
1083     return new ConfigurableOption[] {
 
1084         new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
 
1086         new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1),
 
1088         new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1),
 
1090         //      new ConfigurableOption(componentName, "newline.elseIf", locale,
 
1091         // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
 
1092         new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1),
 
1094         new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),
 
1096         new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1),
 
1098         new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1),
 
1100         new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
 
1105    * Returns the array of mapped positions. Returns null is no positions have been set.
 
1108    * @deprecated There is no need to retrieve the mapped positions anymore.
 
1110   public int[] getMappedPositions() {
 
1111     return mappedPositions;
 
1115    * Returns the priority of the token given as argument <br>
 
1116    * The most prioritary the token is, the smallest the return value is.
 
1118    * @return the priority of <code>token</code>
 
1120    *          the token of which the priority is requested
 
1122   private static int getTokenPriority(int token) {
 
1124     case TokenNameextends:
 
1125       //                        case TokenNameimplements :
 
1126       //                        case TokenNamethrows :
 
1128     case TokenNameSEMICOLON:
 
1131     case TokenNameCOMMA:
 
1134     case TokenNameEQUAL:
 
1137     case TokenNameAND_AND:
 
1139     case TokenNameOR_OR:
 
1142     case TokenNameQUESTION:
 
1144     case TokenNameCOLON:
 
1146       return 50; // it's better cutting on ?: than on ;
 
1147     case TokenNameEQUAL_EQUAL:
 
1149     case TokenNameEQUAL_EQUAL_EQUAL:
 
1151     case TokenNameNOT_EQUAL:
 
1153     case TokenNameNOT_EQUAL_EQUAL:
 
1158     case TokenNameLESS_EQUAL:
 
1160     case TokenNameGREATER:
 
1162     case TokenNameGREATER_EQUAL:
 
1164       //                        case TokenNameinstanceof : // instanceof
 
1168     case TokenNameMINUS:
 
1171     case TokenNameMULTIPLY:
 
1173     case TokenNameDIVIDE:
 
1175     case TokenNameREMAINDER:
 
1178     case TokenNameLEFT_SHIFT:
 
1180     case TokenNameRIGHT_SHIFT:
 
1182       //                        case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
 
1191     case TokenNameMULTIPLY_EQUAL:
 
1193     case TokenNameDIVIDE_EQUAL:
 
1195     case TokenNameREMAINDER_EQUAL:
 
1197     case TokenNamePLUS_EQUAL:
 
1199     case TokenNameMINUS_EQUAL:
 
1201     case TokenNameLEFT_SHIFT_EQUAL:
 
1203     case TokenNameRIGHT_SHIFT_EQUAL:
 
1205     //                  case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
 
1206     case TokenNameAND_EQUAL:
 
1208     case TokenNameXOR_EQUAL:
 
1210     case TokenNameOR_EQUAL:
 
1212     case TokenNameDOT_EQUAL:
 
1219       return Integer.MAX_VALUE;
 
1224    * Handles the exception raised when an invalid token is encountered. Returns true if the exception has been handled, false
 
1227   private boolean handleInvalidToken(Exception e) {
 
1228     if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT) || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
 
1229         || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
 
1235   private final void increaseGlobalDelta(int offset) {
 
1236     globalDelta += offset;
 
1239   private final void increaseLineDelta(int offset) {
 
1240     lineDelta += offset;
 
1243   private final void increaseSplitDelta(int offset) {
 
1244     splitDelta += offset;
 
1248    * Returns true if a space has to be inserted after <code>operator</code> false otherwise.
 
1250   private boolean insertSpaceAfter(int token) {
 
1252     case TokenNameLPAREN:
 
1254     case TokenNameTWIDDLE:
 
1258     case TokenNameWHITESPACE:
 
1259     case TokenNameLBRACKET:
 
1260     case TokenNameDOLLAR:
 
1261     case Scanner.TokenNameCOMMENT_LINE:
 
1269    * Returns true if a space has to be inserted before <code>operator</code> false otherwise. <br>
 
1270    * Cannot be static as it uses the code formatter options (to know if the compact assignment mode is on).
 
1272   private boolean insertSpaceBefore(int token) {
 
1274     case TokenNameEQUAL:
 
1275       return (!options.compactAssignmentMode);
 
1281   private static boolean isComment(int token) {
 
1282     boolean result = token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE
 
1283         || token == Scanner.TokenNameCOMMENT_PHPDOC;
 
1287   private static boolean isLiteralToken(int token) {
 
1288     boolean result = token == TokenNameIntegerLiteral
 
1289     //                  || token == TokenNameLongLiteral
 
1290         //                      || token == TokenNameFloatingPointLiteral
 
1291         || token == TokenNameDoubleLiteral
 
1292         //                      || token == TokenNameCharacterLiteral
 
1293         || token == TokenNameStringDoubleQuote;
 
1298    * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>, it is split and the result is dumped in
 
1299    * <code>formattedSource</code>
 
1301    * @param newLineCount
 
1302    *          the number of new lines to append
 
1304   private void newLine(int newLineCount) {
 
1305     // format current line
 
1307     beginningOfLineIndex = formattedSource.length();
 
1308     String currentLine = currentLineBuffer.toString();
 
1309     if (containsOpenCloseBraces) {
 
1310       containsOpenCloseBraces = false;
 
1311       outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
 
1312       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
1314       outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
 
1316     // dump line break(s)
 
1317     for (int i = 0; i < newLineCount; i++) {
 
1318       formattedSource.append(options.lineSeparatorSequence);
 
1319       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1321     // reset formatter for next line
 
1322     int currentLength = currentLine.length();
 
1323     currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
 
1324     increaseGlobalDelta(splitDelta);
 
1325     increaseGlobalDelta(lineDelta);
 
1327     currentLineIndentationLevel = initialIndentationLevel;
 
1330   private String operatorString(int operator) {
 
1332     case TokenNameextends:
 
1333       return "extends"; //$NON-NLS-1$
 
1334     //                  case TokenNameimplements :
 
1335     //                          return "implements"; //$NON-NLS-1$
 
1337     //                  case TokenNamethrows :
 
1338     //                          return "throws"; //$NON-NLS-1$
 
1339     case TokenNameSEMICOLON:
 
1341       return ";"; //$NON-NLS-1$
 
1342     case TokenNameCOMMA:
 
1344       return ","; //$NON-NLS-1$
 
1345     case TokenNameEQUAL:
 
1347       return "="; //$NON-NLS-1$
 
1348     case TokenNameAND_AND:
 
1350       return "&&"; //$NON-NLS-1$
 
1351     case TokenNameOR_OR:
 
1353       return "||"; //$NON-NLS-1$
 
1354     case TokenNameQUESTION:
 
1356       return "?"; //$NON-NLS-1$
 
1357     case TokenNameCOLON:
 
1359       return ":"; //$NON-NLS-1$
 
1360     case TokenNamePAAMAYIM_NEKUDOTAYIM:
 
1362       return "::"; //$NON-NLS-1$
 
1363     case TokenNameEQUAL_EQUAL:
 
1364       // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1365       return "=="; //$NON-NLS-1$
 
1366     case TokenNameEQUAL_EQUAL_EQUAL:
 
1367       // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1368       return "==="; //$NON-NLS-1$
 
1369     case TokenNameEQUAL_GREATER:
 
1371       return "=>"; //$NON-NLS-1$                                
 
1372     case TokenNameNOT_EQUAL:
 
1373       // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1374       return "!="; //$NON-NLS-1$
 
1375     case TokenNameNOT_EQUAL_EQUAL:
 
1376       // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1377       return "!=="; //$NON-NLS-1$
 
1380       return "<"; //$NON-NLS-1$
 
1381     case TokenNameLESS_EQUAL:
 
1383       return "<="; //$NON-NLS-1$
 
1384     case TokenNameGREATER:
 
1386       return ">"; //$NON-NLS-1$
 
1387     case TokenNameGREATER_EQUAL:
 
1389       return ">="; //$NON-NLS-1$
 
1390     //                  case TokenNameinstanceof : // instanceof
 
1391     //                          return "instanceof"; //$NON-NLS-1$
 
1393       // + (15.17, 15.17.2)
 
1394       return "+"; //$NON-NLS-1$
 
1395     case TokenNameMINUS:
 
1397       return "-"; //$NON-NLS-1$
 
1398     case TokenNameMULTIPLY:
 
1400       return "*"; //$NON-NLS-1$
 
1401     case TokenNameDIVIDE:
 
1403       return "/"; //$NON-NLS-1$
 
1404     case TokenNameREMAINDER:
 
1406       return "%"; //$NON-NLS-1$
 
1407     case TokenNameLEFT_SHIFT:
 
1409       return "<<"; //$NON-NLS-1$
 
1410     case TokenNameRIGHT_SHIFT:
 
1412       return ">>"; //$NON-NLS-1$
 
1413     //                  case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
1414     //                          return ">>>"; //$NON-NLS-1$
 
1416       // & (15.21, 15.21.1, 15.21.2)
 
1417       return "&"; //$NON-NLS-1$
 
1419       // | (15.21, 15.21.1, 15.21.2)
 
1420       return "|"; //$NON-NLS-1$
 
1422       // ^ (15.21, 15.21.1, 15.21.2)
 
1423       return "^"; //$NON-NLS-1$
 
1424     case TokenNameMULTIPLY_EQUAL:
 
1426       return "*="; //$NON-NLS-1$
 
1427     case TokenNameDIVIDE_EQUAL:
 
1429       return "/="; //$NON-NLS-1$
 
1430     case TokenNameREMAINDER_EQUAL:
 
1432       return "%="; //$NON-NLS-1$
 
1433     case TokenNamePLUS_EQUAL:
 
1435       return "+="; //$NON-NLS-1$
 
1436     case TokenNameMINUS_EQUAL:
 
1438       return "-="; //$NON-NLS-1$
 
1439     case TokenNameMINUS_GREATER:
 
1441       return "->"; //$NON-NLS-1$
 
1442     case TokenNameLEFT_SHIFT_EQUAL:
 
1444       return "<<="; //$NON-NLS-1$
 
1445     case TokenNameRIGHT_SHIFT_EQUAL:
 
1447       return ">>="; //$NON-NLS-1$
 
1448     //                  case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
1449     //                          return ">>>="; //$NON-NLS-1$
 
1450     case TokenNameAND_EQUAL:
 
1452       return "&="; //$NON-NLS-1$
 
1453     case TokenNameXOR_EQUAL:
 
1455       return "^="; //$NON-NLS-1$
 
1456     case TokenNameOR_EQUAL:
 
1458       return "|="; //$NON-NLS-1$
 
1459     case TokenNameDOT_EQUAL:
 
1461       return ".="; //$NON-NLS-1$
 
1464       return "."; //$NON-NLS-1$
 
1466       return ""; //$NON-NLS-1$
 
1471    * Appends <code>stringToOutput</code> to the formatted output. <br>
 
1472    * If it contains \n, append a LINE_SEPARATOR and indent after it.
 
1474   private void output(String stringToOutput) {
 
1475     char currentCharacter;
 
1476     for (int i = 0, max = stringToOutput.length(); i < max; i++) {
 
1477       currentCharacter = stringToOutput.charAt(i);
 
1478       if (currentCharacter != '\t') {
 
1479         currentLineBuffer.append(currentCharacter);
 
1484   private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
 
1485     newLine(newLineCount);
 
1486     formattedSource.append(scanner.source, scanner.startPosition, scanner.currentPosition - scanner.startPosition);
 
1490    * Appends <code>token</code> to the formatted output. <br>
 
1491    * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
 
1493   private void outputCurrentToken(int token) {
 
1494     char[] source = scanner.source;
 
1495     int startPosition = scanner.startPosition;
 
1497     case Scanner.TokenNameCOMMENT_PHPDOC:
 
1498     case Scanner.TokenNameCOMMENT_BLOCK:
 
1499     case Scanner.TokenNameCOMMENT_LINE:
 
1500       boolean endOfLine = false;
 
1501       int currentCommentOffset = getCurrentCommentOffset();
 
1502       int beginningOfLineSpaces = 0;
 
1504       currentCommentOffset = getCurrentCommentOffset();
 
1505       beginningOfLineSpaces = 0;
 
1506       boolean pendingCarriageReturn = false;
 
1507       for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
 
1508         char currentCharacter = source[i];
 
1509         updateMappedPositions(i);
 
1510         switch (currentCharacter) {
 
1512           pendingCarriageReturn = true;
 
1516           if (pendingCarriageReturn) {
 
1517             increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
1519             increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1521           pendingCarriageReturn = false;
 
1522           currentLineBuffer.append(options.lineSeparatorSequence);
 
1523           beginningOfLineSpaces = 0;
 
1527           if (pendingCarriageReturn) {
 
1528             pendingCarriageReturn = false;
 
1529             increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1530             currentLineBuffer.append(options.lineSeparatorSequence);
 
1531             beginningOfLineSpaces = 0;
 
1535             // we remove a maximum of currentCommentOffset characters (tabs
 
1536             // are converted to space numbers).
 
1537             beginningOfLineSpaces += options.tabSize;
 
1538             if (beginningOfLineSpaces > currentCommentOffset) {
 
1539               currentLineBuffer.append(currentCharacter);
 
1541               increaseGlobalDelta(-1);
 
1544             currentLineBuffer.append(currentCharacter);
 
1548           if (pendingCarriageReturn) {
 
1549             pendingCarriageReturn = false;
 
1550             increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1551             currentLineBuffer.append(options.lineSeparatorSequence);
 
1552             beginningOfLineSpaces = 0;
 
1556             // we remove a maximum of currentCommentOffset characters (tabs
 
1557             // are converted to space numbers).
 
1558             beginningOfLineSpaces++;
 
1559             if (beginningOfLineSpaces > currentCommentOffset) {
 
1560               currentLineBuffer.append(currentCharacter);
 
1562               increaseGlobalDelta(-1);
 
1565             currentLineBuffer.append(currentCharacter);
 
1569           if (pendingCarriageReturn) {
 
1570             pendingCarriageReturn = false;
 
1571             increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1572             currentLineBuffer.append(options.lineSeparatorSequence);
 
1573             beginningOfLineSpaces = 0;
 
1576             beginningOfLineSpaces = 0;
 
1577             currentLineBuffer.append(currentCharacter);
 
1582       updateMappedPositions(scanner.currentPosition - 1);
 
1583       multipleLineCommentCounter++;
 
1586       for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
 
1587         char currentCharacter = source[i];
 
1588         updateMappedPositions(i);
 
1589         currentLineBuffer.append(currentCharacter);
 
1595    * Outputs <code>currentString</code>:<br>
 
1597    * <li>If its length is < maxLineLength, output
 
1598    * <li>Otherwise it is split.
 
1601    * @param currentString
 
1603    * @param preIndented
 
1604    *          whether the string to output was pre-indented
 
1606    *          number of indentation to put in front of <code>currentString</code>
 
1608    *          value of the operator belonging to <code>currentString</code>.
 
1610   private void outputLine(String currentString, boolean preIndented, int depth, int operator, int substringIndex,
 
1611       int[] startSubstringIndexes, int offsetInGlobalLine) {
 
1612     boolean emptyFirstSubString = false;
 
1613     String operatorString = operatorString(operator);
 
1614     boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
 
1615     boolean placeOperatorAhead = !placeOperatorBehind;
 
1616     // dump prefix operator?
 
1617     if (placeOperatorAhead) {
 
1622       if (operator != 0) {
 
1623         if (insertSpaceBefore(operator)) {
 
1624           formattedSource.append(' ');
 
1625           increaseSplitDelta(1);
 
1627         formattedSource.append(operatorString);
 
1628         increaseSplitDelta(operatorString.length());
 
1629         if (insertSpaceAfter(operator) && operator != TokenNameimplements && operator != TokenNameextends) {
 
1630           //                    && operator != TokenNamethrows) {
 
1631           formattedSource.append(' ');
 
1632           increaseSplitDelta(1);
 
1636     SplitLine splitLine = null;
 
1637     if (options.maxLineLength == 0 || getLength(currentString, depth) < options.maxLineLength
 
1638         || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
 
1639       // depending on the type of operator, outputs new line before of after
 
1641       // indent before postfix operator
 
1642       // indent also when the line cannot be split
 
1643       if (operator == TokenNameextends || operator == TokenNameimplements) {
 
1644         //                              || operator == TokenNamethrows) {
 
1645         formattedSource.append(' ');
 
1646         increaseSplitDelta(1);
 
1648       if (placeOperatorBehind) {
 
1653       int max = currentString.length();
 
1654       if (multipleLineCommentCounter != 0) {
 
1656           BufferedReader reader = new BufferedReader(new StringReader(currentString));
 
1657           String line = reader.readLine();
 
1658           while (line != null) {
 
1659             updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + line.length()
 
1660                 + options.lineSeparatorSequence.length);
 
1661             formattedSource.append(line);
 
1662             beginningOfLineIndex = beginningOfLineIndex + line.length();
 
1663             if ((line = reader.readLine()) != null) {
 
1664               formattedSource.append(options.lineSeparatorSequence);
 
1665               beginningOfLineIndex += options.lineSeparatorSequence.length;
 
1666               dumpTab(currentLineIndentationLevel);
 
1670         } catch (IOException e) {
 
1671           e.printStackTrace();
 
1674         updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
 
1675         for (int i = 0; i < max; i++) {
 
1676           char currentChar = currentString.charAt(i);
 
1677           switch (currentChar) {
 
1682               // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
 
1683               // split with a comment inside a condition
 
1684               // a substring cannot end with a lineSeparatorSequence,
 
1685               // except if it has been added by format() after a one-line
 
1687               formattedSource.append(options.lineSeparatorSequence);
 
1688               // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
 
1693             formattedSource.append(currentChar);
 
1697       // update positions inside the mappedPositions table
 
1698       if (substringIndex != -1) {
 
1699         if (multipleLineCommentCounter == 0) {
 
1700           int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
 
1701           updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
 
1703         // compute the splitDelta resulting with the operator and blank removal
 
1704         if (substringIndex + 1 != startSubstringIndexes.length) {
 
1705           increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
 
1708       // dump postfix operator?
 
1709       if (placeOperatorBehind) {
 
1710         if (insertSpaceBefore(operator)) {
 
1711           formattedSource.append(' ');
 
1712           if (operator != 0) {
 
1713             increaseSplitDelta(1);
 
1716         formattedSource.append(operatorString);
 
1717         if (operator != 0) {
 
1718           increaseSplitDelta(operatorString.length());
 
1723     // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
 
1724     // extends has to stand alone on a line when currentString has been split.
 
1725     if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
 
1726       //                                || operator == TokenNameimplements
 
1727       //                                || operator == TokenNamethrows)) {
 
1728       formattedSource.append(options.lineSeparatorSequence);
 
1729       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1732       if (operator == TokenNameextends) {
 
1733         //                              || operator == TokenNameimplements
 
1734         //                              || operator == TokenNamethrows) {
 
1735         formattedSource.append(' ');
 
1736         increaseSplitDelta(1);
 
1739     // perform actual splitting
 
1740     String result[] = splitLine.substrings;
 
1741     int[] splitOperators = splitLine.operators;
 
1742     if (result[0].length() == 0) {
 
1743       // when the substring 0 is null, the substring 1 is correctly indented.
 
1745       emptyFirstSubString = true;
 
1747     // the operator going in front of the result[0] string is the operator
 
1749     for (int i = 0, max = result.length; i < max; i++) {
 
1750       // the new depth is the current one if this is the first substring,
 
1751       // the current one + 1 otherwise.
 
1752       // if the substring is a comment, use the current indentation Level
 
1753       // instead of the depth
 
1754       // (-1 because the ouputline increases depth).
 
1755       // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
 
1757       String currentResult = result[i];
 
1758       if (currentResult.length() != 0 || splitOperators[i] != 0) {
 
1759         int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
 
1760         || currentResult.startsWith("//")) //$NON-NLS-1$ 
 
1761             ? indentationLevel - 1 : depth;
 
1762         outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
 
1763             i == 0 ? newDepth : newDepth + 1, splitOperators[i], i, splitLine.startSubstringsIndexes, currentString
 
1764                 .indexOf(currentResult));
 
1766           formattedSource.append(options.lineSeparatorSequence);
 
1767           increaseSplitDelta(options.lineSeparatorSequence.length);
 
1771     if (result.length == splitOperators.length - 1) {
 
1772       int lastOperator = splitOperators[result.length];
 
1773       String lastOperatorString = operatorString(lastOperator);
 
1774       formattedSource.append(options.lineSeparatorSequence);
 
1775       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1776       if (breakLineBeforeOperator(lastOperator)) {
 
1778         if (lastOperator != 0) {
 
1779           if (insertSpaceBefore(lastOperator)) {
 
1780             formattedSource.append(' ');
 
1781             increaseSplitDelta(1);
 
1783           formattedSource.append(lastOperatorString);
 
1784           increaseSplitDelta(lastOperatorString.length());
 
1785           if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements && lastOperator != TokenNameextends) {
 
1786             //                                  && lastOperator != TokenNamethrows) {
 
1787             formattedSource.append(' ');
 
1788             increaseSplitDelta(1);
 
1793     if (placeOperatorBehind) {
 
1794       if (insertSpaceBefore(operator)) {
 
1795         formattedSource.append(' ');
 
1796         increaseSplitDelta(1);
 
1798       formattedSource.append(operatorString);
 
1799       //increaseSplitDelta(operatorString.length());
 
1804    * Pops the top statement of the stack if it is <code>token</code>
 
1806   private int pop(int token) {
 
1808     if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
 
1810       constructionsCount--;
 
1816    * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
 
1818   private int popBlock() {
 
1820     if ((constructionsCount > 0)
 
1821         && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
 
1822       if (constructions[constructionsCount - 1] == BLOCK)
 
1824       constructionsCount--;
 
1830    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1831    * Does not remove <code>token</code> from the stack.
 
1834    *          the token to be left as the top of the stack
 
1836   private int popExclusiveUntil(int token) {
 
1838     int startCount = constructionsCount;
 
1839     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1840       if (constructions[i] != NONINDENT_BLOCK)
 
1842       constructionsCount--;
 
1848    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1849    * Does not remove it from the stack.
 
1851   private int popExclusiveUntilBlock() {
 
1852     int startCount = constructionsCount;
 
1854     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
 
1855       constructionsCount--;
 
1862    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
 
1863    * <code>CASE</code>.<br>
 
1864    * Does not remove it from the stack.
 
1866   private int popExclusiveUntilBlockOrCase() {
 
1867     int startCount = constructionsCount;
 
1869     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK
 
1870         && constructions[i] != TokenNamecase; i--) {
 
1871       constructionsCount--;
 
1878    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1879    * Removes <code>token</code> from the stack too.
 
1882    *          the token to remove from the stack
 
1884   private int popInclusiveUntil(int token) {
 
1885     int startCount = constructionsCount;
 
1887     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1888       if (constructions[i] != NONINDENT_BLOCK)
 
1890       constructionsCount--;
 
1892     if (constructionsCount > 0) {
 
1893       if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
 
1895       constructionsCount--;
 
1901    * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1902    * Does not remove it from the stack.
 
1904   private int popInclusiveUntilBlock() {
 
1905     int startCount = constructionsCount;
 
1907     for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
 
1909       constructionsCount--;
 
1911     if (constructionsCount > 0) {
 
1912       if (constructions[constructionsCount - 1] == BLOCK)
 
1914       constructionsCount--;
 
1920    * Pushes a block in the stack. <br>
 
1921    * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>, pushes
 
1922    * <code>NONINDENT_BLOCK</code> otherwise. Creates a new bigger array if the current one is full.
 
1924   private int pushBlock() {
 
1926     if (constructionsCount == constructions.length)
 
1927       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
 
1928     if ((constructionsCount == 0) || (constructions[constructionsCount - 1] == BLOCK)
 
1929         || (constructions[constructionsCount - 1] == NONINDENT_BLOCK) || (constructions[constructionsCount - 1] == TokenNamecase)) {
 
1931       constructions[constructionsCount++] = BLOCK;
 
1933       constructions[constructionsCount++] = NONINDENT_BLOCK;
 
1939    * Pushes <code>token</code>.<br>
 
1940    * Creates a new bigger array if the current one is full.
 
1942   private int pushControlStatement(int token) {
 
1943     if (constructionsCount == constructions.length)
 
1944       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
 
1945     constructions[constructionsCount++] = token;
 
1949   private static boolean separateFirstArgumentOn(int currentToken) {
 
1950     //return (currentToken == TokenNameCOMMA || currentToken ==
 
1951     // TokenNameSEMICOLON);
 
1952     return currentToken != TokenNameif && currentToken != TokenNameLPAREN && currentToken != TokenNameNOT
 
1953         && currentToken != TokenNamewhile && currentToken != TokenNamefor && currentToken != TokenNameforeach
 
1954         && currentToken != TokenNameswitch;
 
1958    * Set the positions to map. The mapped positions should be retrieved using the getMappedPositions() method.
 
1962    * @deprecated Set the positions to map using the format(String, int, int[]) method.
 
1964    * @see #getMappedPositions()
 
1966   public void setPositionsToMap(int[] positions) {
 
1967     positionsToMap = positions;
 
1970     mappedPositions = new int[positions.length];
 
1974    * Appends a space character to the current line buffer.
 
1976   private void space() {
 
1977     currentLineBuffer.append(' ');
 
1978     increaseLineDelta(1);
 
1982    * Splits <code>stringToSplit</code> on the top level token <br>
 
1983    * If there are several identical token at the same level, the string is cut into many pieces.
 
1985    * @return an object containing the operator and all the substrings or null if the string cannot be split
 
1987   public SplitLine split(String stringToSplit) {
 
1988     return split(stringToSplit, 0);
 
1992    * Splits <code>stringToSplit</code> on the top level token <br>
 
1993    * If there are several identical token at the same level, the string is cut into many pieces.
 
1995    * @return an object containing the operator and all the substrings or null if the string cannot be split
 
1997   public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
 
1999      * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
 
2001     if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
 
2004     // split doesn't work correct for PHP
 
2007     //    int currentToken = 0;
 
2008     //    int splitTokenType = 0;
 
2009     //    int splitTokenDepth = Integer.MAX_VALUE;
 
2010     //    int splitTokenPriority = Integer.MAX_VALUE;
 
2011     //    int[] substringsStartPositions = new int[10];
 
2012     //    // contains the start position of substrings
 
2013     //    int[] substringsEndPositions = new int[10];
 
2014     //    // contains the start position of substrings
 
2015     //    int substringsCount = 1; // index in the substringsStartPosition array
 
2016     //    int[] splitOperators = new int[10];
 
2017     //    // contains the start position of substrings
 
2018     //    int splitOperatorsCount = 0; // index in the substringsStartPosition array
 
2019     //    int[] openParenthesisPosition = new int[10];
 
2020     //    int openParenthesisPositionCount = 0;
 
2021     //    int position = 0;
 
2022     //    int lastOpenParenthesisPosition = -1;
 
2023     //    // used to remember the position of the 1st open parenthesis
 
2024     //    // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
 
2025     //    // setup the scanner with a new source
 
2026     //    int lastCommentStartPosition = -1;
 
2027     //    // to remember the start position of the last comment
 
2028     //    int firstTokenOnLine = -1;
 
2029     //    // to remember the first token of the line
 
2030     //    int previousToken = -1;
 
2031     //    // to remember the previous token.
 
2032     //    splitScanner.setSource(stringToSplit.toCharArray());
 
2034     //      // start the loop
 
2036     //        // takes the next token
 
2038     //          if (currentToken != Scanner.TokenNameWHITESPACE)
 
2039     //            previousToken = currentToken;
 
2040     //          currentToken = splitScanner.getNextToken();
 
2041     //          if (Scanner.DEBUG) {
 
2042     //            int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
 
2043     //            int currentStartPosition = splitScanner
 
2044     //                .getCurrentTokenStartPosition();
 
2045     //            System.out.print(currentStartPosition + "," + currentEndPosition
 
2047     //            System.out.println(scanner.toStringAction(currentToken));
 
2049     //        } catch (InvalidInputException e) {
 
2050     //          if (!handleInvalidToken(e))
 
2052     //          currentToken = 0;
 
2053     //          // this value is not modify when an exception is raised.
 
2055     //        if (currentToken == TokenNameEOF)
 
2057     //        if (firstTokenOnLine == -1) {
 
2058     //          firstTokenOnLine = currentToken;
 
2060     //        switch (currentToken) {
 
2061     //          case TokenNameRBRACE :
 
2062     //          case TokenNameRPAREN :
 
2063     //            if (openParenthesisPositionCount > 0) {
 
2064     //              if (openParenthesisPositionCount == 1
 
2065     //                  && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
 
2066     //                lastOpenParenthesisPosition = openParenthesisPosition[0];
 
2067     //              } else if ((splitTokenDepth == Integer.MAX_VALUE)
 
2068     //                  || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
 
2069     //                splitTokenType = 0;
 
2070     //                splitTokenDepth = openParenthesisPositionCount;
 
2071     //                splitTokenPriority = Integer.MAX_VALUE;
 
2072     //                substringsStartPositions[0] = 0;
 
2073     //                // better token means the whole line until now is the first
 
2075     //                substringsCount = 1; // resets the count of substrings
 
2076     //                substringsEndPositions[0] = openParenthesisPosition[0];
 
2077     //                // substring ends on operator start
 
2078     //                position = openParenthesisPosition[0];
 
2079     //                // the string mustn't be cut before the closing parenthesis but
 
2080     //                // after the opening one.
 
2081     //                splitOperatorsCount = 1; // resets the count of split operators
 
2082     //                splitOperators[0] = 0;
 
2084     //              openParenthesisPositionCount--;
 
2087     //          case TokenNameLBRACE :
 
2088     //          case TokenNameLPAREN :
 
2089     //            if (openParenthesisPositionCount == openParenthesisPosition.length) {
 
2092     //                      openParenthesisPosition,
 
2094     //                      (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
 
2095     //                      0, openParenthesisPositionCount);
 
2097     //            openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
 
2098     //            if (currentToken == TokenNameLPAREN
 
2099     //                && previousToken == TokenNameRPAREN) {
 
2100     //              openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
 
2103     //          case TokenNameSEMICOLON :
 
2105     //          case TokenNameCOMMA :
 
2107     //          case TokenNameEQUAL :
 
2109     //            if (openParenthesisPositionCount < splitTokenDepth
 
2110     //                || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
 
2111     //              // the current token is better than the one we currently have
 
2112     //              // (in level or in priority if same level)
 
2113     //              // reset the substringsCount
 
2114     //              splitTokenDepth = openParenthesisPositionCount;
 
2115     //              splitTokenType = currentToken;
 
2116     //              splitTokenPriority = getTokenPriority(currentToken);
 
2117     //              substringsStartPositions[0] = 0;
 
2118     //              // better token means the whole line until now is the first
 
2120     //              if (separateFirstArgumentOn(firstTokenOnLine)
 
2121     //                  && openParenthesisPositionCount > 0) {
 
2122     //                substringsCount = 2; // resets the count of substrings
 
2123     //                substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
 
2124     //                substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
 
2125     //                substringsEndPositions[1] = splitScanner.startPosition;
 
2126     //                splitOperatorsCount = 2; // resets the count of split operators
 
2127     //                splitOperators[0] = 0;
 
2128     //                splitOperators[1] = currentToken;
 
2129     //                position = splitScanner.currentPosition;
 
2130     //                // next substring will start from operator end
 
2132     //                substringsCount = 1; // resets the count of substrings
 
2133     //                substringsEndPositions[0] = splitScanner.startPosition;
 
2134     //                // substring ends on operator start
 
2135     //                position = splitScanner.currentPosition;
 
2136     //                // next substring will start from operator end
 
2137     //                splitOperatorsCount = 1; // resets the count of split operators
 
2138     //                splitOperators[0] = currentToken;
 
2141     //              if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
 
2142     //                  && splitTokenType != TokenNameEQUAL
 
2143     //                  && currentToken != TokenNameEQUAL) {
 
2144     //                // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
 
2146     //                // take only the 1st = into account.
 
2147     //                // if another token with the same priority is found,
 
2148     //                // push the start position of the substring and
 
2149     //                // push the token into the stack.
 
2150     //                // create a new array object if the current one is full.
 
2151     //                if (substringsCount == substringsStartPositions.length) {
 
2154     //                          substringsStartPositions,
 
2156     //                          (substringsStartPositions = new int[substringsCount * 2]),
 
2157     //                          0, substringsCount);
 
2158     //                  System.arraycopy(substringsEndPositions, 0,
 
2159     //                      (substringsEndPositions = new int[substringsCount * 2]),
 
2160     //                      0, substringsCount);
 
2162     //                if (splitOperatorsCount == splitOperators.length) {
 
2163     //                  System.arraycopy(splitOperators, 0,
 
2164     //                      (splitOperators = new int[splitOperatorsCount * 2]), 0,
 
2165     //                      splitOperatorsCount);
 
2167     //                substringsStartPositions[substringsCount] = position;
 
2168     //                substringsEndPositions[substringsCount++] = splitScanner.startPosition;
 
2169     //                // substring ends on operator start
 
2170     //                position = splitScanner.currentPosition;
 
2171     //                // next substring will start from operator end
 
2172     //                splitOperators[splitOperatorsCount++] = currentToken;
 
2176     //          case TokenNameCOLON :
 
2178     //            // see 1FK7C5R, we only split on a colon, when it is associated
 
2179     //            // with a question-mark.
 
2180     //            // indeed it might appear also behind a case statement, and we do
 
2181     //            // not to break at this point.
 
2182     //            if ((splitOperatorsCount == 0)
 
2183     //                || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
 
2186     //          case TokenNameextends :
 
2187     //          case TokenNameimplements :
 
2188     //          //case TokenNamethrows :
 
2189     //          case TokenNameDOT :
 
2191     //          case TokenNameMULTIPLY :
 
2193     //          case TokenNameDIVIDE :
 
2195     //          case TokenNameREMAINDER :
 
2197     //          case TokenNamePLUS :
 
2198     //          // + (15.17, 15.17.2)
 
2199     //          case TokenNameMINUS :
 
2201     //          case TokenNameLEFT_SHIFT :
 
2203     //          case TokenNameRIGHT_SHIFT :
 
2205     //          // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
2206     //          case TokenNameLESS :
 
2208     //          case TokenNameLESS_EQUAL :
 
2210     //          case TokenNameGREATER :
 
2212     //          case TokenNameGREATER_EQUAL :
 
2214     //          // case TokenNameinstanceof : // instanceof
 
2215     //          case TokenNameEQUAL_EQUAL :
 
2216     //          // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2217     //          case TokenNameEQUAL_EQUAL_EQUAL :
 
2218     //          // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2219     //          case TokenNameNOT_EQUAL :
 
2220     //          // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2221     //          case TokenNameNOT_EQUAL_EQUAL :
 
2222     //          // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2223     //          case TokenNameAND :
 
2224     //          // & (15.21, 15.21.1, 15.21.2)
 
2225     //          case TokenNameOR :
 
2226     //          // | (15.21, 15.21.1, 15.21.2)
 
2227     //          case TokenNameXOR :
 
2228     //          // ^ (15.21, 15.21.1, 15.21.2)
 
2229     //          case TokenNameAND_AND :
 
2231     //          case TokenNameOR_OR :
 
2233     //          case TokenNameQUESTION :
 
2235     //          case TokenNameMULTIPLY_EQUAL :
 
2237     //          case TokenNameDIVIDE_EQUAL :
 
2239     //          case TokenNameREMAINDER_EQUAL :
 
2241     //          case TokenNamePLUS_EQUAL :
 
2243     //          case TokenNameMINUS_EQUAL :
 
2245     //          case TokenNameLEFT_SHIFT_EQUAL :
 
2247     //          case TokenNameRIGHT_SHIFT_EQUAL :
 
2249     //          // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
2250     //          case TokenNameAND_EQUAL :
 
2252     //          case TokenNameXOR_EQUAL :
 
2254     //          case TokenNameOR_EQUAL :
 
2256     //            if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority
 
2257     // > getTokenPriority(currentToken)))
 
2258     //                && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
 
2259     //                    || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
 
2260     //              // the current token is better than the one we currently have
 
2261     //              // (in level or in priority if same level)
 
2262     //              // reset the substringsCount
 
2263     //              splitTokenDepth = openParenthesisPositionCount;
 
2264     //              splitTokenType = currentToken;
 
2265     //              splitTokenPriority = getTokenPriority(currentToken);
 
2266     //              substringsStartPositions[0] = 0;
 
2267     //              // better token means the whole line until now is the first
 
2269     //              if (separateFirstArgumentOn(firstTokenOnLine)
 
2270     //                  && openParenthesisPositionCount > 0) {
 
2271     //                substringsCount = 2; // resets the count of substrings
 
2272     //                substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
 
2273     //                substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
 
2274     //                substringsEndPositions[1] = splitScanner.startPosition;
 
2275     //                splitOperatorsCount = 3; // resets the count of split operators
 
2276     //                splitOperators[0] = 0;
 
2277     //                splitOperators[1] = 0;
 
2278     //                splitOperators[2] = currentToken;
 
2279     //                position = splitScanner.currentPosition;
 
2280     //                // next substring will start from operator end
 
2282     //                substringsCount = 1; // resets the count of substrings
 
2283     //                substringsEndPositions[0] = splitScanner.startPosition;
 
2284     //                // substring ends on operator start
 
2285     //                position = splitScanner.currentPosition;
 
2286     //                // next substring will start from operator end
 
2287     //                splitOperatorsCount = 2; // resets the count of split operators
 
2288     //                splitOperators[0] = 0;
 
2289     //                // nothing for first operand since operator will be inserted in
 
2290     //                // front of the second operand
 
2291     //                splitOperators[1] = currentToken;
 
2294     //              if (openParenthesisPositionCount == splitTokenDepth
 
2295     //                  && splitTokenPriority == getTokenPriority(currentToken)) {
 
2296     //                // if another token with the same priority is found,
 
2297     //                // push the start position of the substring and
 
2298     //                // push the token into the stack.
 
2299     //                // create a new array object if the current one is full.
 
2300     //                if (substringsCount == substringsStartPositions.length) {
 
2303     //                          substringsStartPositions,
 
2305     //                          (substringsStartPositions = new int[substringsCount * 2]),
 
2306     //                          0, substringsCount);
 
2307     //                  System.arraycopy(substringsEndPositions, 0,
 
2308     //                      (substringsEndPositions = new int[substringsCount * 2]),
 
2309     //                      0, substringsCount);
 
2311     //                if (splitOperatorsCount == splitOperators.length) {
 
2312     //                  System.arraycopy(splitOperators, 0,
 
2313     //                      (splitOperators = new int[splitOperatorsCount * 2]), 0,
 
2314     //                      splitOperatorsCount);
 
2316     //                substringsStartPositions[substringsCount] = position;
 
2317     //                substringsEndPositions[substringsCount++] = splitScanner.startPosition;
 
2318     //                // substring ends on operator start
 
2319     //                position = splitScanner.currentPosition;
 
2320     //                // next substring will start from operator end
 
2321     //                splitOperators[splitOperatorsCount++] = currentToken;
 
2327     //        if (isComment(currentToken)) {
 
2328     //          lastCommentStartPosition = splitScanner.startPosition;
 
2330     //          lastCommentStartPosition = -1;
 
2333     //    } catch (InvalidInputException e) {
 
2336     //    // if the string cannot be split, return null.
 
2337     //    if (splitOperatorsCount == 0)
 
2339     //    // ## SPECIAL CASES BEGIN
 
2340     //    if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
 
2341     //        && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
 
2342     //        || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
 
2343     //            && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) ||
 
2344     // (separateFirstArgumentOn(firstTokenOnLine)
 
2345     //        && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
 
2346     //        && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
 
2347     //      // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
 
2348     //      // not be broken on two lines
 
2349     //      // only one split on a top level .
 
2350     //      // or more than one split on . and substring before open parenthesis fits
 
2352     //      // or split inside parenthesis and first token is not a for/while/if
 
2353     //      SplitLine sl = split(
 
2354     //          stringToSplit.substring(lastOpenParenthesisPosition),
 
2355     //          lastOpenParenthesisPosition);
 
2356     //      if (sl == null || sl.operators[0] != TokenNameCOMMA) {
 
2357     //        // trim() is used to remove the extra blanks at the end of the
 
2358     //        // substring. See PR 1FGYPI1
 
2359     //        return new SplitLine(new int[]{0, 0}, new String[]{
 
2360     //            stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
 
2361     //            stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
 
2362     //            offsetInGlobalLine,
 
2363     //            lastOpenParenthesisPosition + offsetInGlobalLine});
 
2365     //        // right substring can be split and is split on comma
 
2366     //        // copy substrings and operators
 
2367     //        // except if the 1st string is empty.
 
2368     //        int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
 
2369     //        int subStringsLength = sl.substrings.length + 1 - startIndex;
 
2370     //        String[] result = new String[subStringsLength];
 
2371     //        int[] startIndexes = new int[subStringsLength];
 
2372     //        int operatorsLength = sl.operators.length + 1 - startIndex;
 
2373     //        int[] operators = new int[operatorsLength];
 
2374     //        result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
 
2375     //        operators[0] = 0;
 
2376     //        System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
 
2377     //            1, subStringsLength - 1);
 
2378     //        for (int i = subStringsLength - 1; i >= 0; i--) {
 
2379     //          startIndexes[i] += offsetInGlobalLine;
 
2381     //        System.arraycopy(sl.substrings, startIndex, result, 1,
 
2382     //            subStringsLength - 1);
 
2383     //        System.arraycopy(sl.operators, startIndex, operators, 1,
 
2384     //            operatorsLength - 1);
 
2385     //        return new SplitLine(operators, result, startIndexes);
 
2388     //    // if the last token is a comment and the substring before the comment fits
 
2390     //    // split before the comment and return the result.
 
2391     //    if (lastCommentStartPosition > -1
 
2392     //        && lastCommentStartPosition < options.maxLineLength
 
2393     //        && splitTokenPriority > 50) {
 
2394     //      int end = lastCommentStartPosition;
 
2395     //      int start = lastCommentStartPosition;
 
2396     //      if (stringToSplit.charAt(end - 1) == ' ') {
 
2399     //      if (start != end && stringToSplit.charAt(start) == ' ') {
 
2402     //      return new SplitLine(new int[]{0, 0}, new String[]{
 
2403     //          stringToSplit.substring(0, end), stringToSplit.substring(start)},
 
2404     //          new int[]{0, start});
 
2406     //    if (position != stringToSplit.length()) {
 
2407     //      if (substringsCount == substringsStartPositions.length) {
 
2408     //        System.arraycopy(substringsStartPositions, 0,
 
2409     //            (substringsStartPositions = new int[substringsCount * 2]), 0,
 
2410     //            substringsCount);
 
2411     //        System.arraycopy(substringsEndPositions, 0,
 
2412     //            (substringsEndPositions = new int[substringsCount * 2]), 0,
 
2413     //            substringsCount);
 
2415     //      // avoid empty extra substring, e.g. line terminated with a semi-colon
 
2416     //      substringsStartPositions[substringsCount] = position;
 
2417     //      substringsEndPositions[substringsCount++] = stringToSplit.length();
 
2419     //    if (splitOperatorsCount == splitOperators.length) {
 
2420     //      System.arraycopy(splitOperators, 0,
 
2421     //          (splitOperators = new int[splitOperatorsCount * 2]), 0,
 
2422     //          splitOperatorsCount);
 
2424     //    splitOperators[splitOperatorsCount] = 0;
 
2425     //    // the last element of the stack is the position of the end of
 
2427     //    // +1 because the substring method excludes the last character
 
2428     //    String[] result = new String[substringsCount];
 
2429     //    for (int i = 0; i < substringsCount; i++) {
 
2430     //      int start = substringsStartPositions[i];
 
2431     //      int end = substringsEndPositions[i];
 
2432     //      if (stringToSplit.charAt(start) == ' ') {
 
2434     //        substringsStartPositions[i]++;
 
2436     //      if (end != start && stringToSplit.charAt(end - 1) == ' ') {
 
2439     //      result[i] = stringToSplit.substring(start, end);
 
2440     //      substringsStartPositions[i] += offsetInGlobalLine;
 
2442     //    if (splitOperatorsCount > substringsCount) {
 
2443     //      System.arraycopy(substringsStartPositions, 0,
 
2444     //          (substringsStartPositions = new int[splitOperatorsCount]), 0,
 
2445     //          substringsCount);
 
2446     //      System.arraycopy(substringsEndPositions, 0,
 
2447     //          (substringsEndPositions = new int[splitOperatorsCount]), 0,
 
2448     //          substringsCount);
 
2449     //      for (int i = substringsCount; i < splitOperatorsCount; i++) {
 
2450     //        substringsStartPositions[i] = position;
 
2451     //        substringsEndPositions[i] = position;
 
2453     //      System.arraycopy(splitOperators, 0,
 
2454     //          (splitOperators = new int[splitOperatorsCount]), 0,
 
2455     //          splitOperatorsCount);
 
2457     //      System.arraycopy(substringsStartPositions, 0,
 
2458     //          (substringsStartPositions = new int[substringsCount]), 0,
 
2459     //          substringsCount);
 
2460     //      System.arraycopy(substringsEndPositions, 0,
 
2461     //          (substringsEndPositions = new int[substringsCount]), 0,
 
2462     //          substringsCount);
 
2463     //      System.arraycopy(splitOperators, 0,
 
2464     //          (splitOperators = new int[substringsCount]), 0, substringsCount);
 
2466     //    SplitLine splitLine = new SplitLine(splitOperators, result,
 
2467     //        substringsStartPositions);
 
2468     //    return splitLine;
 
2471   private void updateMappedPositions(int startPosition) {
 
2472     if (positionsToMap == null) {
 
2475     char[] source = scanner.source;
 
2476     int sourceLength = source.length;
 
2477     while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
 
2478       int posToMap = positionsToMap[indexToMap];
 
2479       if (posToMap < 0 || posToMap >= sourceLength) {
 
2480         // protection against out of bounds position
 
2481         if (posToMap == sourceLength) {
 
2482           mappedPositions[indexToMap] = formattedSource.length();
 
2484         indexToMap = positionsToMap.length; // no more mapping
 
2487       if (CharOperation.isWhitespace(source[posToMap])) {
 
2488         mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2490         if (posToMap == sourceLength - 1) {
 
2491           mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2493           mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
 
2500   private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
 
2501     if (mappedPositions == null || mappedPositions.length == indexInMap)
 
2503     while (indexInMap < mappedPositions.length && startPosition <= mappedPositions[indexInMap]
 
2504         && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
 
2505       mappedPositions[indexInMap] += splitDelta;
 
2510   private int getLength(String s, int tabDepth) {
 
2512     for (int i = 0; i < tabDepth; i++) {
 
2513       length += options.tabSize;
 
2515     for (int i = 0, max = s.length(); i < max; i++) {
 
2516       char currentChar = s.charAt(i);
 
2517       switch (currentChar) {
 
2519         length += options.tabSize;
 
2529    * Sets the initial indentation level
 
2531    * @param indentationLevel
 
2532    *          new indentation level
 
2536   public void setInitialIndentationLevel(int newIndentationLevel) {
 
2537     this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;