1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v0.5 
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v05.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  ******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.formatter;
 
  13 import java.io.BufferedReader;
 
  14 import java.io.IOException;
 
  15 import java.io.StringReader;
 
  16 import java.util.Hashtable;
 
  17 import java.util.Locale;
 
  20 import net.sourceforge.phpdt.core.ICodeFormatter;
 
  21 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  22 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
 
  23 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
 
  24 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
 
  25 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
 
  26 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
 
  27 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
 
  29 /** <h2>How to format a piece of code ?</h2>
 
  30  * <ul><li>Create an instance of <code>CodeFormatter</code>
 
  31  * <li>Use the method <code>void format(aString)</code>
 
  32  * on this instance to format <code>aString</code>.
 
  33  * It will return the formatted string.</ul>
 
  35 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
 
  37   public FormatterOptions options;
 
  40    * Represents a block in the <code>constructions</code> stack.
 
  42   public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
 
  45    * Represents a block following a control statement in the <code>constructions</code> stack.
 
  47   public static final int NONINDENT_BLOCK = -100;
 
  50    * Contains the formatted output.
 
  52   StringBuffer formattedSource;
 
  55    * Contains the current line.<br>
 
  56    * Will be dumped at the next "newline"
 
  58   StringBuffer currentLineBuffer;
 
  61    * Used during the formatting to get each token.
 
  66    * Contains the tokens responsible for the current indentation level
 
  67    * and the blocks not closed yet.
 
  69   private int[] constructions;
 
  72    * Index in the <code>constructions</code> array.
 
  74   private int constructionsCount;
 
  77    * Level of indentation of the current token (number of tab char put in front of it).
 
  79   private int indentationLevel;
 
  82    * Regular level of indentation of all the lines
 
  84   private int initialIndentationLevel;
 
  87    * Used to split a line.
 
  92    * To remember the offset between the beginning of the line and the
 
  93    * beginning of the comment.
 
  95   int currentCommentOffset;
 
  96   int currentLineIndentationLevel;
 
  98   private boolean containsOpenCloseBraces;
 
  99   private int indentationLevelForOpenCloseBraces;
 
 102    * Collections of positions to map
 
 104   private int[] positionsToMap;
 
 107    * Collections of mapped positions
 
 109   private int[] mappedPositions;
 
 111   private int indexToMap;
 
 113   private int indexInMap;
 
 115   private int globalDelta;
 
 117   private int lineDelta;
 
 119   private int splitDelta;
 
 121   private int beginningOfLineIndex;
 
 123   private int multipleLineCommentCounter;
 
 126    * Creates a new instance of Code Formatter using the given settings.
 
 128    * @deprecated backport 1.0 internal functionality
 
 130   public CodeFormatter(ConfigurableOption[] settings) {
 
 131     this(convertConfigurableOptions(settings));
 
 135    * Creates a new instance of Code Formatter using the FormattingOptions object
 
 137    * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
 
 139   public CodeFormatter() {
 
 143    * Creates a new instance of Code Formatter using the given settings.
 
 145   public CodeFormatter(Map settings) {
 
 147     // initialize internal state
 
 148     constructionsCount = 0;
 
 149     constructions = new int[10];
 
 150     currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
 
 151     currentCommentOffset = -1;
 
 153     // initialize primary and secondary scanners
 
 154     scanner = new Scanner(true /*comment*/
 
 155     , true /*whitespace*/
 
 158     ); // regular scanner for forming lines
 
 159     scanner.recordLineSeparator = true;
 
 161     // to remind of the position of the beginning of the line.
 
 162     splitScanner = new Scanner(true /*comment*/
 
 163     , true /*whitespace*/
 
 167     // secondary scanner to split long lines formed by primary scanning
 
 169     // initialize current line buffer
 
 170     currentLineBuffer = new StringBuffer();
 
 171     this.options = new FormatterOptions(settings);
 
 175    * Returns true if a lineSeparator has to be inserted before <code>operator</code>
 
 178   private static boolean breakLineBeforeOperator(int operator) {
 
 180       case TokenNameCOMMA :
 
 181       case TokenNameSEMICOLON :
 
 182       case TokenNameEQUAL :
 
 190   * @deprecated backport 1.0 internal functionality
 
 192   private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
 
 193     Hashtable options = new Hashtable(10);
 
 195     for (int i = 0; i < settings.length; i++) {
 
 196       if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
 
 197         String optionName = settings[i].getOptionName();
 
 198         int valueIndex = settings[i].getCurrentValueIndex();
 
 200         if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
 
 201           options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 203         } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
 
 204           options.put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 206         } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
 
 207           options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 209         } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
 
 210           options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 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$
 
 215         } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
 
 216           options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 218         } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
 
 219           options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 221         } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
 
 222           options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 224         } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
 
 225           options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 234    * Returns the end of the source code.
 
 236   private final String copyRemainingSource() {
 
 237     char str[] = scanner.source;
 
 238     int startPosition = scanner.startPosition;
 
 239     int length = str.length - startPosition;
 
 240     StringBuffer bufr = new StringBuffer(length);
 
 241     if (startPosition < str.length) {
 
 242       bufr.append(str, startPosition, length);
 
 244     return (bufr.toString());
 
 248    * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
 
 250   private void dumpTab(int tabCount) {
 
 251     if (options.indentWithTab) {
 
 252       for (int j = 0; j < tabCount; j++) {
 
 253         formattedSource.append('\t');
 
 254         increaseSplitDelta(1);
 
 257       for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
 
 258         formattedSource.append(' ');
 
 259         increaseSplitDelta(1);
 
 265    * Dumps <code>currentLineBuffer</code> into the formatted string.
 
 267   private void flushBuffer() {
 
 268     String currentString = currentLineBuffer.toString();
 
 270     beginningOfLineIndex = formattedSource.length();
 
 271     if (containsOpenCloseBraces) {
 
 272       containsOpenCloseBraces = false;
 
 273       outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
 
 274       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 276       outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
 
 278     int scannerSourceLength = scanner.source.length;
 
 279     if (scannerSourceLength > 2) {
 
 280       if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
 
 281         formattedSource.append(options.lineSeparatorSequence);
 
 282         increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
 283       } else if (scanner.source[scannerSourceLength - 1] == '\n') {
 
 284         formattedSource.append(options.lineSeparatorSequence);
 
 285         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 286       } else if (scanner.source[scannerSourceLength - 1] == '\r') {
 
 287         formattedSource.append(options.lineSeparatorSequence);
 
 288         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 291     updateMappedPositions(scanner.startPosition);
 
 295    * Formats the input string.
 
 297   private void format() {
 
 299     int previousToken = 0;
 
 300     int previousCompilableToken = 0;
 
 301     int indentationOffset = 0;
 
 302     int newLinesInWhitespace = 0;
 
 304     // number of new lines in the previous whitespace token
 
 305     // (used to leave blank lines before comments)
 
 306     int pendingNewLines = 0;
 
 307     boolean expectingOpenBrace = false;
 
 308     boolean clearNonBlockIndents = false;
 
 309     // true if all indentations till the 1st { (usefull after } or ;)
 
 310     boolean pendingSpace = true;
 
 311     boolean pendingNewlineAfterParen = false;
 
 312     // true when a cr is to be put after a ) (in conditional statements)
 
 313     boolean inAssignment = false;
 
 314     boolean inArrayAssignment = false;
 
 315     boolean inThrowsClause = false;
 
 316     boolean inClassOrInterfaceHeader = false;
 
 317     int dollarBraceCount = 0;
 
 319     // openBracketCount is used to count the number of open brackets not closed yet.
 
 320     int openBracketCount = 0;
 
 321     int unarySignModifier = 0;
 
 323     // openParenthesis[0] is used to count the parenthesis not belonging to a condition
 
 324     // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
 
 325     int openParenthesisCount = 1;
 
 326     int[] openParenthesis = new int[10];
 
 328     // tokenBeforeColon is used to know what token goes along with the current :
 
 329     // it can be case or ?
 
 330     int tokenBeforeColonCount = 0;
 
 331     int[] tokenBeforeColon = new int[10];
 
 333     constructionsCount = 0; // initializes the constructions count.
 
 335     // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
 
 338     // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else 
 
 339     boolean specialElse = false;
 
 341     // OPTION (IndentationLevel): initial indentation level may be non-zero.
 
 342     currentLineIndentationLevel += constructionsCount;
 
 344     // An InvalidInputException exception might cause the termination of this loop.
 
 347         // Get the next token.  Catch invalid input and output it
 
 348         // with minimal formatting, also catch end of input and
 
 351           token = scanner.getNextToken();
 
 353             int currentEndPosition = scanner.getCurrentTokenEndPosition();
 
 354             int currentStartPosition = scanner.getCurrentTokenStartPosition();
 
 356             System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
 
 357             System.out.println(scanner.toStringAction(token));
 
 360           // Patch for line comment
 
 361           // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
 
 362           if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
 
 363             int length = scanner.currentPosition;
 
 364             loop : for (int index = length - 1; index >= 0; index--) {
 
 365               switch (scanner.source[index]) {
 
 368                   scanner.currentPosition--;
 
 375         } catch (InvalidInputException e) {
 
 376           if (!handleInvalidToken(e)) {
 
 381         if (token == Scanner.TokenNameEOF)
 
 384         /* ## MODIFYING the indentation level before generating new lines
 
 385         and indentation in the output string
 
 388         // Removes all the indentations made by statements not followed by a block
 
 389         // except if the current token is ELSE, CATCH or if we are in a switch/case
 
 390         if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
 
 393               if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
 
 397               indentationLevel += popInclusiveUntil(TokenNameif);
 
 399               //                                                case TokenNamecatch :
 
 400               //                                                        indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 402               //                                                case TokenNamefinally :
 
 403               //                                                        indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 405             case TokenNamewhile :
 
 406               if (nlicsToken == TokenNamedo) {
 
 407                 indentationLevel += pop(TokenNamedo);
 
 411               indentationLevel += popExclusiveUntilBlockOrCase();
 
 412               // clear until a CASE, DEFAULT or BLOCK is encountered.
 
 413               // Thus, the indentationLevel is correctly cleared either
 
 414               // in a switch/case statement or in any other situation.
 
 416           clearNonBlockIndents = false;
 
 418         // returns to the indentation level created by the SWITCH keyword
 
 419         // if the current token is a CASE or a DEFAULT
 
 420         if (token == TokenNamecase || token == TokenNamedefault) {
 
 421           indentationLevel += pop(TokenNamecase);
 
 423         //                              if (token == Scanner.TokenNamethrows) {
 
 424         //                                      inThrowsClause = true;
 
 426         if ((token == Scanner.TokenNameclass // || token == Scanner.TokenNameinterface
 
 428           && previousToken != Scanner.TokenNameDOT) {
 
 429           inClassOrInterfaceHeader = true;
 
 432         /* ## APPEND newlines and indentations to the output string
 
 434         // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
 
 435         // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
 
 436         //        if (pendingNewlineAfterParen
 
 437         //          && previousCompilableToken == TokenNameelse
 
 438         //          && token == TokenNameif
 
 439         //          && options.compactElseIfMode) {
 
 440         //          pendingNewlineAfterParen = false;
 
 441         //          pendingNewLines = 0;
 
 442         //          indentationLevel += pop(TokenNameelse);
 
 443         //          // because else if is now one single statement,
 
 444         //          // the indentation level after it is increased by one and not by 2
 
 445         //          // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
 
 447         // Add a newline & indent to the formatted source string if
 
 448         // a for/if-else/while statement was scanned and there is no block
 
 450         pendingNewlineAfterParen =
 
 451           pendingNewlineAfterParen || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
 
 452         if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
 
 453           pendingNewlineAfterParen = false;
 
 455           // Do to add a newline & indent sequence if the current token is an
 
 456           // open brace or a period or if the current token is a semi-colon and the
 
 457           // previous token is a close paren.
 
 458           // add a new line if a parenthesis belonging to a for() statement
 
 459           // has been closed and the current token is not an opening brace
 
 460           if (token != TokenNameLBRACE
 
 461             && !isComment(token) // to avoid adding new line between else and a comment
 
 462             && token != TokenNameDOT
 
 463             && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
 
 465             currentLineIndentationLevel = indentationLevel;
 
 467             pendingSpace = false;
 
 469             if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
 
 471               if (constructionsCount > 0
 
 472                 && constructions[constructionsCount - 1] != BLOCK
 
 473                 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
 
 474                 currentLineIndentationLevel = indentationLevel - 1;
 
 476                 currentLineIndentationLevel = indentationLevel;
 
 479               pendingSpace = false;
 
 483         if (token == TokenNameLBRACE
 
 484           && options.newLineBeforeOpeningBraceMode
 
 485           && constructionsCount > 0
 
 486           && constructions[constructionsCount - 1] == TokenNamedo) {
 
 488           currentLineIndentationLevel = indentationLevel - 1;
 
 490           pendingSpace = false;
 
 493         if (token == TokenNameLBRACE && inThrowsClause) {
 
 494           inThrowsClause = false;
 
 495           if (options.newLineBeforeOpeningBraceMode) {
 
 497             currentLineIndentationLevel = indentationLevel;
 
 499             pendingSpace = false;
 
 503         if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
 
 504           inClassOrInterfaceHeader = false;
 
 505           if (options.newLineBeforeOpeningBraceMode) {
 
 507             currentLineIndentationLevel = indentationLevel;
 
 509             pendingSpace = false;
 
 512         // Add pending new lines to the formatted source string.
 
 513         // Note: pending new lines are not added if the current token
 
 514         // is a single line comment or whitespace.
 
 515         // if the comment is between parenthesis, there is no blank line preservation
 
 516         // (if it's a one-line comment, a blank line is added after it).
 
 517         if (((pendingNewLines > 0 && (!isComment(token)))
 
 518           || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
 
 519           || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
 
 520           && token != Scanner.TokenNameWHITESPACE) {
 
 522           // Do not add newline & indent between an adjoining close brace and
 
 523           // close paren.  Anonymous inner classes may use this form.
 
 524           boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
 
 526           // OPTION (NewLineInCompoundStatement): do not add newline & indent
 
 527           // between close brace and else, (do) while, catch, and finally if
 
 528           // newlineInCompoundStatement is true.
 
 529           boolean nlicsOption =
 
 530             previousToken == TokenNameRBRACE
 
 531               && !options.newlineInControlStatementMode
 
 532               && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo));
 
 533           //                                                            || token == TokenNamecatch
 
 534           //                                                            || token == TokenNamefinally);
 
 536           // Do not add a newline & indent between a close brace and semi-colon.
 
 537           boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
 
 539           // Do not add a new line & indent between a multiline comment and a opening brace
 
 540           boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
 
 542           // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
 
 543           boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
 
 545           // Add a newline and indent, if appropriate.
 
 547             || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
 
 549             // if clearAllBlankLinesMode=false, leaves the blank lines
 
 550             // inserted by the user
 
 551             // if clearAllBlankLinesMode=true, removes all of then
 
 552             // and insert only blank lines required by the formatting.
 
 553             if (!options.clearAllBlankLinesMode) {
 
 554               //  (isComment(token))
 
 555               pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
 
 556               pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
 
 558             if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
 
 559               containsOpenCloseBraces = true;
 
 560               indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 561               if (isComment(previousToken)) {
 
 562                 newLine(pendingNewLines);
 
 564                 /*  if (!(constructionsCount > 1
 
 565                         && constructions[constructionsCount-1] == NONINDENT_BLOCK
 
 566                         && (constructions[constructionsCount-2] == TokenNamefor 
 
 567                          || constructions[constructionsCount-2] == TokenNamewhile))) {*/
 
 568                 if (options.newLineInEmptyBlockMode) {
 
 569                   if (inArrayAssignment) {
 
 570                     newLine(1); // array assigment with an empty block
 
 572                     newLine(pendingNewLines);
 
 578               // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
 
 579               if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
 
 580                 && token == TokenNameSEMICOLON)) {
 
 581                 newLine(pendingNewLines);
 
 584             if (((previousCompilableToken == TokenNameSEMICOLON)
 
 585               || (previousCompilableToken == TokenNameLBRACE)
 
 586               || (previousCompilableToken == TokenNameRBRACE)
 
 587               || (isComment(previousToken)))
 
 588               && (token == TokenNameRBRACE)) {
 
 589               indentationOffset = -1;
 
 590               indentationLevel += popExclusiveUntilBlock();
 
 592             if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
 
 594               currentLineIndentationLevel++;
 
 596               currentLineIndentationLevel = indentationLevel + indentationOffset;
 
 598             pendingSpace = false;
 
 599             indentationOffset = 0;
 
 602           newLinesInWhitespace = 0;
 
 605           if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
 
 610           case TokenNameDOLLAR_LBRACE :
 
 614             //                          case TokenNamefinally :
 
 615             expectingOpenBrace = true;
 
 616             pendingNewlineAfterParen = true;
 
 617             indentationLevel += pushControlStatement(token);
 
 620           case TokenNamedefault :
 
 621             if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 625                 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
 
 627                 tokenBeforeColonCount);
 
 629             tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
 
 630             indentationLevel += pushControlStatement(TokenNamecase);
 
 632           case TokenNameQUESTION :
 
 633             if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 637                 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
 
 639                 tokenBeforeColonCount);
 
 641             tokenBeforeColon[tokenBeforeColonCount++] = token;
 
 643           case TokenNameswitch :
 
 646           case TokenNamewhile :
 
 647             if (openParenthesisCount == openParenthesis.length) {
 
 648               System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
 
 650             openParenthesis[openParenthesisCount++] = 0;
 
 651             expectingOpenBrace = true;
 
 653             indentationLevel += pushControlStatement(token);
 
 655             //                                  case TokenNametry :
 
 656             //                                          pendingNewlineAfterParen = true;
 
 657             //                                  case TokenNamecatch :
 
 658             //                                          // several CATCH statements can be contiguous.
 
 659             //                                          // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
 
 660             //                                          // as CATCH and TRY are the same token in the stack).
 
 661             //                                          expectingOpenBrace = true;
 
 662             //                                          indentationLevel += pushControlStatement(TokenNamecatch);
 
 666             expectingOpenBrace = true;
 
 667             indentationLevel += pushControlStatement(token);
 
 672           case TokenNameLPAREN :
 
 673             //                                          if (previousToken == TokenNamesynchronized) {
 
 674             //                                                  indentationLevel += pushControlStatement(previousToken);
 
 676             // Put a space between the previous and current token if the
 
 677             // previous token was not a keyword, open paren, logical
 
 678             // compliment (eg: !), semi-colon, open brace, close brace,
 
 680             if (previousCompilableToken != TokenNameLBRACKET
 
 681               && previousToken != TokenNameIdentifier
 
 682               && previousToken != 0
 
 683               && previousToken != TokenNameNOT
 
 684               && previousToken != TokenNameLPAREN
 
 685               && previousToken != TokenNameTWIDDLE
 
 686               && previousToken != TokenNameSEMICOLON
 
 687               && previousToken != TokenNameLBRACE
 
 688               && previousToken != TokenNameRBRACE) {
 
 689               //                                                                && previousToken != TokenNamesuper
 
 690               //                                                                && previousToken != TokenNamethis) {
 
 693             // If in a for/if/while statement, increase the parenthesis count
 
 694             // for the current openParenthesisCount
 
 695             // else increase the count for stand alone parenthesis.
 
 696             if (openParenthesisCount > 0)
 
 697               openParenthesis[openParenthesisCount - 1]++;
 
 699               openParenthesis[0]++;
 
 701             pendingSpace = false;
 
 704           case TokenNameRPAREN :
 
 706             // Decrease the parenthesis count
 
 707             // if there is no more unclosed parenthesis,
 
 708             // a new line and indent may be append (depending on the next token).
 
 709             if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
 
 710               openParenthesis[openParenthesisCount - 1]--;
 
 711               if (openParenthesis[openParenthesisCount - 1] <= 0) {
 
 712                 pendingNewlineAfterParen = true;
 
 713                 inAssignment = false;
 
 714                 openParenthesisCount--;
 
 717               openParenthesis[0]--;
 
 719             pendingSpace = false;
 
 721           case TokenNameLBRACE :
 
 722             if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
 
 723               //                  if (previousCompilableToken == TokenNameRBRACKET) {
 
 724               inArrayAssignment = true;
 
 725               inAssignment = false;
 
 727             if (inArrayAssignment) {
 
 728               indentationLevel += pushBlock();
 
 730               // Add new line and increase indentation level after open brace.
 
 732               indentationLevel += pushBlock();
 
 735           case TokenNameRBRACE :
 
 736             if (dollarBraceCount > 0) {
 
 740             if (previousCompilableToken == TokenNameRPAREN) {
 
 741               pendingSpace = false;
 
 743             if (inArrayAssignment) {
 
 744               inArrayAssignment = false;
 
 746               indentationLevel += popInclusiveUntilBlock();
 
 749               indentationLevel += popInclusiveUntilBlock();
 
 751               if (previousCompilableToken == TokenNameRPAREN) {
 
 752                 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
 
 753                 currentLineBuffer.append(options.lineSeparatorSequence);
 
 754                 increaseLineDelta(options.lineSeparatorSequence.length);
 
 756               if (constructionsCount > 0) {
 
 757                 switch (constructions[constructionsCount - 1]) {
 
 759                     //indentationLevel += popExclusiveUntilBlock();
 
 761                   case TokenNameswitch :
 
 764                     //                                                                  case TokenNametry :
 
 765                     //                                                                  case TokenNamecatch :
 
 766                     //                                                                  case TokenNamefinally :
 
 767                   case TokenNamewhile :
 
 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 ignored.
 
 784             pendingSpace = false;
 
 786           case TokenNameCOMMA :
 
 788             pendingSpace = false;
 
 790           case TokenNameSEMICOLON :
 
 792             // Do not generate line terminators in the definition of
 
 793             // the for statement.
 
 794             // if not in this case, jump a line and reduce indentation after the brace
 
 795             // if the block it closes belongs to a conditional statement (if, while, do...).
 
 796             if (openParenthesisCount <= 1) {
 
 798               if (expectingOpenBrace) {
 
 799                 clearNonBlockIndents = true;
 
 800                 expectingOpenBrace = false;
 
 803             inAssignment = false;
 
 804             pendingSpace = false;
 
 806           case TokenNamePLUS_PLUS :
 
 807           case TokenNameMINUS_MINUS :
 
 809             // Do not put a space between a post-increment/decrement
 
 810             // and the identifier being modified.
 
 811             if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET) {
 
 812               pendingSpace = false;
 
 815           case TokenNamePLUS : // previously ADDITION
 
 816           case TokenNameMINUS :
 
 818             // Handle the unary operators plus and minus via a flag
 
 819             if (!isLiteralToken(previousToken)
 
 820               && previousToken != TokenNameIdentifier
 
 821               && previousToken != TokenNameRPAREN
 
 822               && previousToken != TokenNameRBRACKET) {
 
 823               unarySignModifier = 1;
 
 826           case TokenNameCOLON :
 
 827             // In a switch/case statement, add a newline & indent
 
 828             // when a colon is encountered.
 
 829             if (tokenBeforeColonCount > 0) {
 
 830               if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
 
 833               tokenBeforeColonCount--;
 
 836           case TokenNameEQUAL :
 
 839           case Scanner.TokenNameCOMMENT_LINE :
 
 842               currentLineIndentationLevel++;
 
 844             break; // a line is always inserted after a one-line comment
 
 845           case Scanner.TokenNameCOMMENT_PHPDOC :
 
 846           case Scanner.TokenNameCOMMENT_BLOCK :
 
 847             currentCommentOffset = getCurrentCommentOffset();
 
 850           case Scanner.TokenNameWHITESPACE :
 
 852             // Count the number of line terminators in the whitespace so
 
 853             // line spacing can be preserved near comments.
 
 854             char[] source = scanner.source;
 
 855             newLinesInWhitespace = 0;
 
 856             for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
 
 857               if (source[i] == '\r') {
 
 859                   if (source[++i] == '\n') {
 
 860                     newLinesInWhitespace++;
 
 862                     newLinesInWhitespace++;
 
 865                   newLinesInWhitespace++;
 
 867               } else if (source[i] == '\n') {
 
 868                 newLinesInWhitespace++;
 
 871             increaseLineDelta(scanner.startPosition - scanner.currentPosition);
 
 873             //          case TokenNameHTML :
 
 874             //            // Add the next token to the formatted source string.
 
 875             //            // outputCurrentToken(token);
 
 876             //            int startPosition = scanner.startPosition;
 
 878             //            for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
 
 879             //              char currentCharacter = scanner.source[i];
 
 880             //              updateMappedPositions(i);
 
 881             //              currentLineBuffer.append(currentCharacter);
 
 885             if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
 
 886               //                                                        || token == TokenNamesuper
 
 887               //                                                        || token == TokenNamethis) {
 
 889               // Do not put a space between a unary operator
 
 890               // (eg: ++, --, +, -) and the identifier being modified.
 
 891               if (previousToken == TokenNamePLUS_PLUS
 
 892                 || previousToken == TokenNameMINUS_MINUS
 
 893                                 || (previousToken == TokenNameMINUS_GREATER &&
 
 894                                                                         options.compactDereferencingMode) // ->
 
 895                 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
 
 896                 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
 
 897                 pendingSpace = false;
 
 899               unarySignModifier = 0;
 
 903         // Do not output whitespace tokens.
 
 904         if (token != Scanner.TokenNameWHITESPACE) {
 
 906           /* Add pending space to the formatted source string.
 
 907           Do not output a space under the following circumstances:
 
 908           1) this is the first pass
 
 909           2) previous token is an open paren
 
 910           3) previous token is a period
 
 911           4) previous token is the logical compliment (eg: !)
 
 912           5) previous token is the bitwise compliment (eg: ~)
 
 913           6) previous token is the open bracket (eg: [)
 
 914           7) in an assignment statement, if the previous token is an 
 
 915           open brace or the current token is a close brace
 
 916           8) previous token is a single line comment
 
 917           9) current token is a '->'
 
 919                   if (token == TokenNameMINUS_GREATER && 
 
 920                                 options.compactDereferencingMode) pendingSpace = false;
 
 922           boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
 
 925             && insertSpaceAfter(previousToken)
 
 926             && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
 
 927             && previousToken != Scanner.TokenNameCOMMENT_LINE) {
 
 928             if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
 
 931           // Add the next token to the formatted source string.
 
 932           outputCurrentToken(token);
 
 933           if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
 
 935             currentLineBuffer.append(options.lineSeparatorSequence);
 
 936             increaseLineDelta(options.lineSeparatorSequence.length);
 
 940         // Whitespace tokens do not need to be remembered.
 
 941         if (token != Scanner.TokenNameWHITESPACE) {
 
 942           previousToken = token;
 
 943           if (token != Scanner.TokenNameCOMMENT_BLOCK
 
 944             && token != Scanner.TokenNameCOMMENT_LINE
 
 945             && token != Scanner.TokenNameCOMMENT_PHPDOC) {
 
 946             previousCompilableToken = token;
 
 950       output(copyRemainingSource());
 
 952       // dump the last token of the source in the formatted output.
 
 953     } catch (InvalidInputException e) {
 
 954       output(copyRemainingSource());
 
 956       // dump the last token of the source in the formatted output.
 
 961    * Formats the char array <code>sourceString</code>,
 
 962    * and returns a string containing the formatted version.
 
 963    * @return the formatted ouput.
 
 965   public String formatSourceString(String sourceString) {
 
 966     char[] sourceChars = sourceString.toCharArray();
 
 967     formattedSource = new StringBuffer(sourceChars.length);
 
 968     scanner.setSource(sourceChars);
 
 970     return formattedSource.toString();
 
 974    * Formats the char array <code>sourceString</code>,
 
 975    * and returns a string containing the formatted version.
 
 976    * @param string the string to format
 
 977    * @param indentationLevel the initial indentation level
 
 978    * @return the formatted ouput.
 
 980   public String format(String string, int indentationLevel) {
 
 981     return format(string, indentationLevel, (int[]) null);
 
 985    * Formats the char array <code>sourceString</code>,
 
 986    * and returns a string containing the formatted version.
 
 987    * The positions array is modified to contain the mapped positions.
 
 988    * @param string the string to format
 
 989    * @param indentationLevel the initial indentation level
 
 990    * @param positions the array of positions to map
 
 991    * @return the formatted ouput.
 
 993   public String format(String string, int indentationLevel, int[] positions) {
 
 994     return this.format(string, indentationLevel, positions, null);
 
 997   public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
 
 998     if (lineSeparator != null) {
 
 999       this.options.setLineSeparator(lineSeparator);
 
1001     if (positions != null) {
 
1002       this.setPositionsToMap(positions);
 
1003       this.setInitialIndentationLevel(indentationLevel);
 
1004       String formattedString = this.formatSourceString(string);
 
1005       int[] mappedPositions = this.getMappedPositions();
 
1006       System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
 
1007       return formattedString;
 
1009       this.setInitialIndentationLevel(indentationLevel);
 
1010       return this.formatSourceString(string);
 
1014    * Formats the char array <code>sourceString</code>,
 
1015    * and returns a string containing the formatted version. The initial indentation level is 0.
 
1016    * @param string the string to format
 
1017    * @return the formatted ouput.
 
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 
 
1025    * depth and using the given options
 
1027    * @deprecated backport 1.0 internal functionality
 
1029   public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
 
1030     CodeFormatter formatter = new CodeFormatter(options);
 
1031     formatter.setInitialIndentationLevel(initialIndentationLevel);
 
1032     return formatter.formatSourceString(sourceString);
 
1036    * Returns the number of characters and tab char between the beginning of the line
 
1037    * and the beginning of the comment.
 
1039   private int getCurrentCommentOffset() {
 
1040     int linePtr = scanner.linePtr;
 
1041     // if there is no beginning of line, return 0.
 
1045     int beginningOfLine = scanner.lineEnds[linePtr];
 
1046     int currentStartPosition = scanner.startPosition;
 
1047     char[] source = scanner.source;
 
1049     // find the position of the beginning of the line containing the comment
 
1050     while (beginningOfLine > currentStartPosition) {
 
1052         beginningOfLine = scanner.lineEnds[--linePtr];
 
1054         beginningOfLine = 0;
 
1058     for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
 
1059       char currentCharacter = source[i];
 
1060       switch (currentCharacter) {
 
1062           offset += options.tabSize;
 
1078    * Returns an array of descriptions for the configurable options.
 
1079    * The descriptions may be changed and passed back to a different
 
1082    * @deprecated backport 1.0 internal functionality
 
1084   public static ConfigurableOption[] getDefaultOptions(Locale locale) {
 
1085     String componentName = CodeFormatter.class.getName();
 
1086     FormatterOptions options = new FormatterOptions();
 
1087     return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
 
1088       new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
 
1089       new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
 
1090       //      new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
 
1091       new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
 
1092       new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
 
1093       new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
 
1094       new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
 
1095       new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
 
1100    * Returns the array of mapped positions.
 
1101    * Returns null is no positions have been set.
 
1103    * @deprecated There is no need to retrieve the mapped positions anymore.
 
1105   public int[] getMappedPositions() {
 
1106     return mappedPositions;
 
1110    * Returns the priority of the token given as argument<br>
 
1111    * The most prioritary the token is, the smallest the return value is.
 
1112    * @return the priority of <code>token</code>
 
1113    * @param token the token of which the priority is requested
 
1115   private static int getTokenPriority(int token) {
 
1117       case TokenNameextends :
 
1118         //                      case TokenNameimplements :
 
1119         //                      case TokenNamethrows :
 
1121       case TokenNameSEMICOLON : // ;
 
1123       case TokenNameCOMMA : // ,
 
1125       case TokenNameEQUAL : // =
 
1127       case TokenNameAND_AND : // && 
 
1128       case TokenNameOR_OR : // || 
 
1130       case TokenNameQUESTION : // ? 
 
1131       case TokenNameCOLON : // :
 
1132         return 50; // it's better cutting on ?: than on ;
 
1133       case TokenNameEQUAL_EQUAL : // == 
 
1134       case TokenNameEQUAL_EQUAL_EQUAL : // === 
 
1135       case TokenNameNOT_EQUAL : // != 
 
1136                         case TokenNameNOT_EQUAL_EQUAL : // != 
 
1138       case TokenNameLESS : // < 
 
1139       case TokenNameLESS_EQUAL : // <= 
 
1140       case TokenNameGREATER : // > 
 
1141       case TokenNameGREATER_EQUAL : // >= 
 
1142         //                      case TokenNameinstanceof : // instanceof
 
1144       case TokenNamePLUS : // + 
 
1145       case TokenNameMINUS : // - 
 
1147       case TokenNameMULTIPLY : // * 
 
1148       case TokenNameDIVIDE : // / 
 
1149       case TokenNameREMAINDER : // % 
 
1151       case TokenNameLEFT_SHIFT : // << 
 
1152       case TokenNameRIGHT_SHIFT : // >> 
 
1153         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> 
 
1155       case TokenNameAND : // &
 
1156       case TokenNameOR : // | 
 
1157       case TokenNameXOR : // ^ 
 
1159       case TokenNameMULTIPLY_EQUAL : // *= 
 
1160       case TokenNameDIVIDE_EQUAL : // /= 
 
1161       case TokenNameREMAINDER_EQUAL : // %= 
 
1162       case TokenNamePLUS_EQUAL : // += 
 
1163       case TokenNameMINUS_EQUAL : // -= 
 
1164       case TokenNameLEFT_SHIFT_EQUAL : // <<= 
 
1165       case TokenNameRIGHT_SHIFT_EQUAL : // >>= 
 
1166         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
 
1167       case TokenNameAND_EQUAL : // &= 
 
1168       case TokenNameXOR_EQUAL : // ^= 
 
1169       case TokenNameOR_EQUAL : // |= 
 
1171       case TokenNameDOT : // .
 
1174         return Integer.MAX_VALUE;
 
1179    * Handles the exception raised when an invalid token is encountered.
 
1180    * Returns true if the exception has been handled, false otherwise.
 
1182   private boolean handleInvalidToken(Exception e) {
 
1183     if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
 
1184       || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
 
1185       || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
 
1191   private final void increaseGlobalDelta(int offset) {
 
1192     globalDelta += offset;
 
1195   private final void increaseLineDelta(int offset) {
 
1196     lineDelta += offset;
 
1199   private final void increaseSplitDelta(int offset) {
 
1200     splitDelta += offset;
 
1204    * Returns true if a space has to be inserted after <code>operator</code>
 
1207   private boolean insertSpaceAfter(int token) {
 
1209       case TokenNameLPAREN :
 
1211       case TokenNameTWIDDLE :
 
1213       case 0 : // no token
 
1214       case TokenNameLBRACKET :
 
1215       case Scanner.TokenNameCOMMENT_LINE :
 
1223    * Returns true if a space has to be inserted before <code>operator</code>
 
1224    * false otherwise.<br>
 
1225    * Cannot be static as it uses the code formatter options
 
1226    * (to know if the compact assignment mode is on).
 
1228   private boolean insertSpaceBefore(int token) {
 
1230       case TokenNameEQUAL :
 
1231         return (!options.compactAssignmentMode);
 
1237   private static boolean isComment(int token) {
 
1239       token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE || token == Scanner.TokenNameCOMMENT_PHPDOC;
 
1243   private static boolean isLiteralToken(int token) {
 
1244     boolean result = token == TokenNameIntegerLiteral
 
1245       //                        || token == TokenNameLongLiteral
 
1246     //                  || token == TokenNameFloatingPointLiteral
 
1247   || token == TokenNameDoubleLiteral
 
1248       //                        || token == TokenNameCharacterLiteral
 
1249   || token == TokenNameStringLiteral;
 
1254    * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
 
1255    * it is split and the result is dumped in <code>formattedSource</code>
 
1256    * @param newLineCount the number of new lines to append
 
1258   private void newLine(int newLineCount) {
 
1260     // format current line
 
1262     beginningOfLineIndex = formattedSource.length();
 
1263     String currentLine = currentLineBuffer.toString();
 
1264     if (containsOpenCloseBraces) {
 
1265       containsOpenCloseBraces = false;
 
1266       outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
 
1267       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
1269       outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
 
1271     // dump line break(s)
 
1272     for (int i = 0; i < newLineCount; i++) {
 
1273       formattedSource.append(options.lineSeparatorSequence);
 
1274       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1276     // reset formatter for next line
 
1277     int currentLength = currentLine.length();
 
1278     currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
 
1280     increaseGlobalDelta(splitDelta);
 
1281     increaseGlobalDelta(lineDelta);
 
1283     currentLineIndentationLevel = initialIndentationLevel;
 
1286   private String operatorString(int operator) {
 
1288       case TokenNameextends :
 
1289         return "extends"; //$NON-NLS-1$
 
1291         //                      case TokenNameimplements :
 
1292         //                              return "implements"; //$NON-NLS-1$
 
1294         //                      case TokenNamethrows :
 
1295         //                              return "throws"; //$NON-NLS-1$
 
1297       case TokenNameSEMICOLON : // ;
 
1298         return ";"; //$NON-NLS-1$
 
1300       case TokenNameCOMMA : // ,
 
1301         return ","; //$NON-NLS-1$
 
1303       case TokenNameEQUAL : // =
 
1304         return "="; //$NON-NLS-1$
 
1306       case TokenNameAND_AND : // && (15.22)
 
1307         return "&&"; //$NON-NLS-1$
 
1309       case TokenNameOR_OR : // || (15.23)
 
1310         return "||"; //$NON-NLS-1$
 
1312       case TokenNameQUESTION : // ? (15.24)
 
1313         return "?"; //$NON-NLS-1$
 
1315       case TokenNameCOLON : // : (15.24)
 
1316         return ":"; //$NON-NLS-1$
 
1318       case TokenNamePAAMAYIM_NEKUDOTAYIM : // : (15.24)
 
1319         return "::"; //$NON-NLS-1$
 
1321       case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1322         return "=="; //$NON-NLS-1$
 
1324       case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1325         return "==="; //$NON-NLS-1$
 
1327       case TokenNameEQUAL_GREATER : // -= (15.25.2)
 
1328         return "=>"; //$NON-NLS-1$                              
 
1330       case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1331         return "!="; //$NON-NLS-1$
 
1333                         case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1334                                 return "!=="; //$NON-NLS-1$
 
1336       case TokenNameLESS : // < (15.19.1)
 
1337         return "<"; //$NON-NLS-1$
 
1339       case TokenNameLESS_EQUAL : // <= (15.19.1)
 
1340         return "<="; //$NON-NLS-1$
 
1342       case TokenNameGREATER : // > (15.19.1)
 
1343         return ">"; //$NON-NLS-1$
 
1345       case TokenNameGREATER_EQUAL : // >= (15.19.1)
 
1346         return ">="; //$NON-NLS-1$
 
1348         //                      case TokenNameinstanceof : // instanceof
 
1349         //                              return "instanceof"; //$NON-NLS-1$
 
1351       case TokenNamePLUS : // + (15.17, 15.17.2)
 
1352         return "+"; //$NON-NLS-1$
 
1354       case TokenNameMINUS : // - (15.17.2)
 
1355         return "-"; //$NON-NLS-1$
 
1357       case TokenNameMULTIPLY : // * (15.16.1)
 
1358         return "*"; //$NON-NLS-1$
 
1360       case TokenNameDIVIDE : // / (15.16.2)
 
1361         return "/"; //$NON-NLS-1$
 
1363       case TokenNameREMAINDER : // % (15.16.3)
 
1364         return "%"; //$NON-NLS-1$
 
1366       case TokenNameLEFT_SHIFT : // << (15.18)
 
1367         return "<<"; //$NON-NLS-1$
 
1369       case TokenNameRIGHT_SHIFT : // >> (15.18)
 
1370         return ">>"; //$NON-NLS-1$
 
1372         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
1373         //                              return ">>>"; //$NON-NLS-1$
 
1375       case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
 
1376         return "&"; //$NON-NLS-1$
 
1378       case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
 
1379         return "|"; //$NON-NLS-1$
 
1381       case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
 
1382         return "^"; //$NON-NLS-1$
 
1384       case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
 
1385         return "*="; //$NON-NLS-1$
 
1387       case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
 
1388         return "/="; //$NON-NLS-1$
 
1390       case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
 
1391         return "%="; //$NON-NLS-1$
 
1393       case TokenNamePLUS_EQUAL : // += (15.25.2)
 
1394         return "+="; //$NON-NLS-1$
 
1396       case TokenNameMINUS_EQUAL : // -= (15.25.2)
 
1397         return "-="; //$NON-NLS-1$
 
1399       case TokenNameMINUS_GREATER : // -= (15.25.2)
 
1400         return "->"; //$NON-NLS-1$
 
1402       case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
 
1403         return "<<="; //$NON-NLS-1$
 
1405       case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
 
1406         return ">>="; //$NON-NLS-1$
 
1408         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
1409         //                              return ">>>="; //$NON-NLS-1$
 
1411       case TokenNameAND_EQUAL : // &= (15.25.2)
 
1412         return "&="; //$NON-NLS-1$
 
1414       case TokenNameXOR_EQUAL : // ^= (15.25.2)
 
1415         return "^="; //$NON-NLS-1$
 
1417       case TokenNameOR_EQUAL : // |= (15.25.2)
 
1418         return "|="; //$NON-NLS-1$
 
1420       case TokenNameDOT : // .
 
1421         return "."; //$NON-NLS-1$
 
1424         return ""; //$NON-NLS-1$
 
1429    * Appends <code>stringToOutput</code> to the formatted output.<br>
 
1430    * If it contains \n, append a LINE_SEPARATOR and indent after it.
 
1432   private void output(String stringToOutput) {
 
1433     char currentCharacter;
 
1434     for (int i = 0, max = stringToOutput.length(); i < max; i++) {
 
1435       currentCharacter = stringToOutput.charAt(i);
 
1436       if (currentCharacter != '\t') {
 
1437         currentLineBuffer.append(currentCharacter);
 
1443    * Appends <code>token</code> to the formatted output.<br>
 
1444    * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
 
1446   private void outputCurrentToken(int token) {
 
1447     char[] source = scanner.source;
 
1448     int startPosition = scanner.startPosition;
 
1451       case Scanner.TokenNameCOMMENT_PHPDOC :
 
1452       case Scanner.TokenNameCOMMENT_BLOCK :
 
1453       case Scanner.TokenNameCOMMENT_LINE :
 
1454         boolean endOfLine = false;
 
1455         int currentCommentOffset = getCurrentCommentOffset();
 
1456         int beginningOfLineSpaces = 0;
 
1458         currentCommentOffset = getCurrentCommentOffset();
 
1459         beginningOfLineSpaces = 0;
 
1460         boolean pendingCarriageReturn = false;
 
1461         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
 
1462           char currentCharacter = source[i];
 
1463           updateMappedPositions(i);
 
1464           switch (currentCharacter) {
 
1466               pendingCarriageReturn = true;
 
1470               if (pendingCarriageReturn) {
 
1471                 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
1473                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1475               pendingCarriageReturn = false;
 
1476               currentLineBuffer.append(options.lineSeparatorSequence);
 
1477               beginningOfLineSpaces = 0;
 
1481               if (pendingCarriageReturn) {
 
1482                 pendingCarriageReturn = false;
 
1483                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1484                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1485                 beginningOfLineSpaces = 0;
 
1489                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
 
1490                 beginningOfLineSpaces += options.tabSize;
 
1491                 if (beginningOfLineSpaces > currentCommentOffset) {
 
1492                   currentLineBuffer.append(currentCharacter);
 
1494                   increaseGlobalDelta(-1);
 
1497                 currentLineBuffer.append(currentCharacter);
 
1501               if (pendingCarriageReturn) {
 
1502                 pendingCarriageReturn = false;
 
1503                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1504                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1505                 beginningOfLineSpaces = 0;
 
1509                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
 
1510                 beginningOfLineSpaces++;
 
1511                 if (beginningOfLineSpaces > currentCommentOffset) {
 
1512                   currentLineBuffer.append(currentCharacter);
 
1514                   increaseGlobalDelta(-1);
 
1517                 currentLineBuffer.append(currentCharacter);
 
1521               if (pendingCarriageReturn) {
 
1522                 pendingCarriageReturn = false;
 
1523                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1524                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1525                 beginningOfLineSpaces = 0;
 
1528                 beginningOfLineSpaces = 0;
 
1529                 currentLineBuffer.append(currentCharacter);
 
1534         updateMappedPositions(scanner.currentPosition - 1);
 
1535         multipleLineCommentCounter++;
 
1538         for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
 
1539           char currentCharacter = source[i];
 
1540           updateMappedPositions(i);
 
1541           currentLineBuffer.append(currentCharacter);
 
1547    * Outputs <code>currentString</code>:<br>
 
1548    * <ul><li>If its length is < maxLineLength, output
 
1549    * <li>Otherwise it is split.</ul>
 
1550    * @param currentString string to output
 
1551    * @param preIndented whether the string to output was pre-indented
 
1552    * @param depth number of indentation to put in front of <code>currentString</code>
 
1553    * @param operator value of the operator belonging to <code>currentString</code>.
 
1555   private void outputLine(
 
1556     String currentString,
 
1557     boolean preIndented,
 
1561     int[] startSubstringIndexes,
 
1562     int offsetInGlobalLine) {
 
1564     boolean emptyFirstSubString = false;
 
1565     String operatorString = operatorString(operator);
 
1566     boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
 
1567     boolean placeOperatorAhead = !placeOperatorBehind;
 
1569     // dump prefix operator?
 
1570     if (placeOperatorAhead) {
 
1575       if (operator != 0) {
 
1576         if (insertSpaceBefore(operator)) {
 
1577           formattedSource.append(' ');
 
1578           increaseSplitDelta(1);
 
1580         formattedSource.append(operatorString);
 
1581         increaseSplitDelta(operatorString.length());
 
1583         if (insertSpaceAfter(operator) //                       && operator != TokenNameimplements
 
1584         && operator != TokenNameextends) {
 
1585           //                    && operator != TokenNamethrows) {
 
1586           formattedSource.append(' ');
 
1587           increaseSplitDelta(1);
 
1591     SplitLine splitLine = null;
 
1592     if (options.maxLineLength == 0
 
1593       || getLength(currentString, depth) < options.maxLineLength
 
1594       || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
 
1596       // depending on the type of operator, outputs new line before of after dumping it
 
1597       // indent before postfix operator
 
1598       // indent also when the line cannot be split
 
1599       if (operator == TokenNameextends) {
 
1600         //                              || operator == TokenNameimplements
 
1601         //                              || operator == TokenNamethrows) {
 
1602         formattedSource.append(' ');
 
1603         increaseSplitDelta(1);
 
1605       if (placeOperatorBehind) {
 
1610       int max = currentString.length();
 
1611       if (multipleLineCommentCounter != 0) {
 
1613           BufferedReader reader = new BufferedReader(new StringReader(currentString));
 
1614           String line = reader.readLine();
 
1615           while (line != null) {
 
1616             updateMappedPositionsWhileSplitting(
 
1617               beginningOfLineIndex,
 
1618               beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length);
 
1619             formattedSource.append(line);
 
1620             beginningOfLineIndex = beginningOfLineIndex + line.length();
 
1621             if ((line = reader.readLine()) != null) {
 
1622               formattedSource.append(options.lineSeparatorSequence);
 
1623               beginningOfLineIndex += options.lineSeparatorSequence.length;
 
1624               dumpTab(currentLineIndentationLevel);
 
1628         } catch (IOException e) {
 
1629           e.printStackTrace();
 
1632         updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
 
1633         for (int i = 0; i < max; i++) {
 
1634           char currentChar = currentString.charAt(i);
 
1635           switch (currentChar) {
 
1640                 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
 
1641                 // a substring cannot end with a lineSeparatorSequence,
 
1642                 // except if it has been added by format() after a one-line comment
 
1643                 formattedSource.append(options.lineSeparatorSequence);
 
1645                 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
 
1650               formattedSource.append(currentChar);
 
1654       // update positions inside the mappedPositions table
 
1655       if (substringIndex != -1) {
 
1656         if (multipleLineCommentCounter == 0) {
 
1657           int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
 
1658           updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
 
1661         // compute the splitDelta resulting with the operator and blank removal
 
1662         if (substringIndex + 1 != startSubstringIndexes.length) {
 
1663           increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
 
1666       // dump postfix operator?
 
1667       if (placeOperatorBehind) {
 
1668         if (insertSpaceBefore(operator)) {
 
1669           formattedSource.append(' ');
 
1670           if (operator != 0) {
 
1671             increaseSplitDelta(1);
 
1674         formattedSource.append(operatorString);
 
1675         if (operator != 0) {
 
1676           increaseSplitDelta(operatorString.length());
 
1681     // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
 
1682     // extends has to stand alone on a line when currentString has been split.
 
1683     if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
 
1684       //                                || operator == TokenNameimplements
 
1685       //                                || operator == TokenNamethrows)) {
 
1686       formattedSource.append(options.lineSeparatorSequence);
 
1687       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1690       if (operator == TokenNameextends) {
 
1691         //                              || operator == TokenNameimplements
 
1692         //                              || operator == TokenNamethrows) {
 
1693         formattedSource.append(' ');
 
1694         increaseSplitDelta(1);
 
1697     // perform actual splitting
 
1698     String result[] = splitLine.substrings;
 
1699     int[] splitOperators = splitLine.operators;
 
1701     if (result[0].length() == 0) {
 
1702       // when the substring 0 is null, the substring 1 is correctly indented.
 
1704       emptyFirstSubString = true;
 
1706     // the operator going in front of the result[0] string is the operator parameter
 
1707     for (int i = 0, max = result.length; i < max; i++) {
 
1708       // the new depth is the current one if this is the first substring,
 
1709       // the current one + 1 otherwise.
 
1710       // if the substring is a comment, use the current indentation Level instead of the depth
 
1711       // (-1 because the ouputline increases depth).
 
1712       // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
 
1713       String currentResult = result[i];
 
1715       if (currentResult.length() != 0 || splitOperators[i] != 0) {
 
1716           int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
 
1717     || currentResult.startsWith("//")) //$NON-NLS-1$ 
 
1718   ? indentationLevel - 1 : depth;
 
1721           i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
 
1722           i == 0 ? newDepth : newDepth + 1,
 
1725           splitLine.startSubstringsIndexes,
 
1726           currentString.indexOf(currentResult));
 
1728           formattedSource.append(options.lineSeparatorSequence);
 
1729           increaseSplitDelta(options.lineSeparatorSequence.length);
 
1733     if (result.length == splitOperators.length - 1) {
 
1734       int lastOperator = splitOperators[result.length];
 
1735       String lastOperatorString = operatorString(lastOperator);
 
1736       formattedSource.append(options.lineSeparatorSequence);
 
1737       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1739       if (breakLineBeforeOperator(lastOperator)) {
 
1741         if (lastOperator != 0) {
 
1742           if (insertSpaceBefore(lastOperator)) {
 
1743             formattedSource.append(' ');
 
1744             increaseSplitDelta(1);
 
1746           formattedSource.append(lastOperatorString);
 
1747           increaseSplitDelta(lastOperatorString.length());
 
1749           if (insertSpaceAfter(lastOperator) //                                 && lastOperator != TokenNameimplements
 
1750             && lastOperator != TokenNameextends) {
 
1751             //                                  && lastOperator != TokenNamethrows) {
 
1752             formattedSource.append(' ');
 
1753             increaseSplitDelta(1);
 
1758     if (placeOperatorBehind) {
 
1759       if (insertSpaceBefore(operator)) {
 
1760         formattedSource.append(' ');
 
1761         increaseSplitDelta(1);
 
1763       formattedSource.append(operatorString);
 
1764       //increaseSplitDelta(operatorString.length());
 
1769    * Pops the top statement of the stack if it is <code>token</code>
 
1771   private int pop(int token) {
 
1773     if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
 
1775       constructionsCount--;
 
1781    * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
 
1783   private int popBlock() {
 
1785     if ((constructionsCount > 0)
 
1786       && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
 
1787       if (constructions[constructionsCount - 1] == BLOCK)
 
1789       constructionsCount--;
 
1795    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1796    * Does not remove <code>token</code> from the stack.
 
1797    * @param token the token to be left as the top of the stack
 
1799   private int popExclusiveUntil(int token) {
 
1801     int startCount = constructionsCount;
 
1802     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1803       if (constructions[i] != NONINDENT_BLOCK)
 
1805       constructionsCount--;
 
1811    * Pops elements until the stack is empty or the top element is
 
1812    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1813    * Does not remove it from the stack.
 
1815   private int popExclusiveUntilBlock() {
 
1816     int startCount = constructionsCount;
 
1818     for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
 
1819       constructionsCount--;
 
1826    * Pops elements until the stack is empty or the top element is
 
1827    * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
 
1828    * Does not remove it from the stack.
 
1830   private int popExclusiveUntilBlockOrCase() {
 
1831     int startCount = constructionsCount;
 
1833     for (int i = startCount - 1;
 
1834       i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK && constructions[i] != TokenNamecase;
 
1836       constructionsCount--;
 
1843    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1844    * Removes <code>token</code> from the stack too.
 
1845    * @param token the token to remove from the stack
 
1847   private int popInclusiveUntil(int token) {
 
1848     int startCount = constructionsCount;
 
1850     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1851       if (constructions[i] != NONINDENT_BLOCK)
 
1853       constructionsCount--;
 
1855     if (constructionsCount > 0) {
 
1856       if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
 
1858       constructionsCount--;
 
1864    * Pops elements until the stack is empty or the top element is
 
1865    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1866    * Does not remove it from the stack.
 
1868   private int popInclusiveUntilBlock() {
 
1869     int startCount = constructionsCount;
 
1871     for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
 
1873       constructionsCount--;
 
1875     if (constructionsCount > 0) {
 
1876       if (constructions[constructionsCount - 1] == BLOCK)
 
1878       constructionsCount--;
 
1884    * Pushes a block in the stack.<br>
 
1885    * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
 
1886    * pushes <code>NONINDENT_BLOCK</code> otherwise.
 
1887    * Creates a new bigger array if the current one is full.
 
1889   private int pushBlock() {
 
1891     if (constructionsCount == constructions.length)
 
1892       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
 
1894     if ((constructionsCount == 0)
 
1895       || (constructions[constructionsCount - 1] == BLOCK)
 
1896       || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
 
1897       || (constructions[constructionsCount - 1] == TokenNamecase)) {
 
1899       constructions[constructionsCount++] = BLOCK;
 
1901       constructions[constructionsCount++] = NONINDENT_BLOCK;
 
1907    * Pushes <code>token</code>.<br>
 
1908    * Creates a new bigger array if the current one is full.
 
1910   private int pushControlStatement(int token) {
 
1911     if (constructionsCount == constructions.length)
 
1912       System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
 
1913     constructions[constructionsCount++] = token;
 
1917   private static boolean separateFirstArgumentOn(int currentToken) {
 
1918     //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
 
1919     return currentToken != TokenNameif
 
1920       && currentToken != TokenNameLPAREN
 
1921       && currentToken != TokenNameNOT
 
1922       && currentToken != TokenNamewhile
 
1923       && currentToken != TokenNamefor
 
1924       && currentToken != TokenNameswitch;
 
1928    * Set the positions to map. The mapped positions should be retrieved using the
 
1929    * getMappedPositions() method.
 
1930    * @param positions int[]
 
1931    * @deprecated Set the positions to map using the format(String, int, int[]) method.
 
1933    * @see #getMappedPositions()
 
1935   public void setPositionsToMap(int[] positions) {
 
1936     positionsToMap = positions;
 
1939     mappedPositions = new int[positions.length];
 
1943    * Appends a space character to the current line buffer.
 
1945   private void space() {
 
1946     currentLineBuffer.append(' ');
 
1947     increaseLineDelta(1);
 
1951    * Splits <code>stringToSplit</code> on the top level token<br>
 
1952    * If there are several identical token at the same level,
 
1953    * the string is cut into many pieces.
 
1954    * @return an object containing the operator and all the substrings
 
1955    * or null if the string cannot be split
 
1957   public SplitLine split(String stringToSplit) {
 
1958     return split(stringToSplit, 0);
 
1962    * Splits <code>stringToSplit</code> on the top level token<br>
 
1963    * If there are several identical token at the same level,
 
1964    * the string is cut into many pieces.
 
1965    * @return an object containing the operator and all the substrings
 
1966    * or null if the string cannot be split
 
1968   public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
 
1970      * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
 
1971      * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387 
 
1973     if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
 
1977     int currentToken = 0;
 
1978     int splitTokenType = 0;
 
1979     int splitTokenDepth = Integer.MAX_VALUE;
 
1980     int splitTokenPriority = Integer.MAX_VALUE;
 
1982     int[] substringsStartPositions = new int[10];
 
1983     // contains the start position of substrings
 
1984     int[] substringsEndPositions = new int[10];
 
1985     // contains the start position of substrings
 
1986     int substringsCount = 1; // index in the substringsStartPosition array
 
1987     int[] splitOperators = new int[10];
 
1988     // contains the start position of substrings
 
1989     int splitOperatorsCount = 0; // index in the substringsStartPosition array
 
1990     int[] openParenthesisPosition = new int[10];
 
1991     int openParenthesisPositionCount = 0;
 
1993     int lastOpenParenthesisPosition = -1;
 
1994     // used to remember the position of the 1st open parenthesis
 
1995     // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
 
1996     // setup the scanner with a new source
 
1997     int lastCommentStartPosition = -1;
 
1998     // to remember the start position of the last comment
 
1999     int firstTokenOnLine = -1;
 
2000     // to remember the first token of the line
 
2001     int previousToken = -1;
 
2002     // to remember the previous token.
 
2003     splitScanner.setSource(stringToSplit.toCharArray());
 
2008         // takes the next token
 
2010           if (currentToken != Scanner.TokenNameWHITESPACE)
 
2011             previousToken = currentToken;
 
2012           currentToken = splitScanner.getNextToken();
 
2013           if (Scanner.DEBUG) {
 
2014             int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
 
2015             int currentStartPosition = splitScanner.getCurrentTokenStartPosition();
 
2017             System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
 
2018             System.out.println(scanner.toStringAction(currentToken));
 
2020         } catch (InvalidInputException e) {
 
2021           if (!handleInvalidToken(e))
 
2024           // this value is not modify when an exception is raised.
 
2026         if (currentToken == TokenNameEOF)
 
2029         if (firstTokenOnLine == -1) {
 
2030           firstTokenOnLine = currentToken;
 
2032         switch (currentToken) {
 
2033           case TokenNameRBRACE :
 
2034           case TokenNameRPAREN :
 
2035             if (openParenthesisPositionCount > 0) {
 
2036               if (openParenthesisPositionCount == 1 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
 
2037                 lastOpenParenthesisPosition = openParenthesisPosition[0];
 
2039                 (splitTokenDepth == Integer.MAX_VALUE)
 
2040                   || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
 
2042                 splitTokenDepth = openParenthesisPositionCount;
 
2043                 splitTokenPriority = Integer.MAX_VALUE;
 
2044                 substringsStartPositions[0] = 0;
 
2045                 // better token means the whole line until now is the first substring
 
2046                 substringsCount = 1; // resets the count of substrings
 
2047                 substringsEndPositions[0] = openParenthesisPosition[0];
 
2048                 // substring ends on operator start
 
2049                 position = openParenthesisPosition[0];
 
2050                 // the string mustn't be cut before the closing parenthesis but after the opening one.
 
2051                 splitOperatorsCount = 1; // resets the count of split operators
 
2052                 splitOperators[0] = 0;
 
2054               openParenthesisPositionCount--;
 
2057           case TokenNameLBRACE :
 
2058           case TokenNameLPAREN :
 
2059             if (openParenthesisPositionCount == openParenthesisPosition.length) {
 
2061                 openParenthesisPosition,
 
2063                 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
 
2065                 openParenthesisPositionCount);
 
2067             openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
 
2068             if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) {
 
2069               openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
 
2072           case TokenNameSEMICOLON : // ;
 
2073           case TokenNameCOMMA : // ,
 
2074           case TokenNameEQUAL : // =
 
2075             if (openParenthesisPositionCount < splitTokenDepth
 
2076               || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
 
2077               // the current token is better than the one we currently have
 
2078               // (in level or in priority if same level)
 
2079               // reset the substringsCount
 
2080               splitTokenDepth = openParenthesisPositionCount;
 
2081               splitTokenType = currentToken;
 
2082               splitTokenPriority = getTokenPriority(currentToken);
 
2083               substringsStartPositions[0] = 0;
 
2084               // better token means the whole line until now is the first substring
 
2086               if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
 
2087                 substringsCount = 2; // resets the count of substrings
 
2089                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
 
2090                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
 
2091                 substringsEndPositions[1] = splitScanner.startPosition;
 
2092                 splitOperatorsCount = 2; // resets the count of split operators
 
2093                 splitOperators[0] = 0;
 
2094                 splitOperators[1] = currentToken;
 
2095                 position = splitScanner.currentPosition;
 
2096                 // next substring will start from operator end
 
2098                 substringsCount = 1; // resets the count of substrings
 
2100                 substringsEndPositions[0] = splitScanner.startPosition;
 
2101                 // substring ends on operator start
 
2102                 position = splitScanner.currentPosition;
 
2103                 // next substring will start from operator end
 
2104                 splitOperatorsCount = 1; // resets the count of split operators
 
2105                 splitOperators[0] = currentToken;
 
2108               if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
 
2109                 && splitTokenType != TokenNameEQUAL
 
2110                 && currentToken != TokenNameEQUAL) {
 
2111                 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
 
2112                 // take only the 1st = into account.
 
2113                 // if another token with the same priority is found,
 
2114                 // push the start position of the substring and
 
2115                 // push the token into the stack.
 
2116                 // create a new array object if the current one is full.
 
2117                 if (substringsCount == substringsStartPositions.length) {
 
2119                     substringsStartPositions,
 
2121                     (substringsStartPositions = new int[substringsCount * 2]),
 
2125                     substringsEndPositions,
 
2127                     (substringsEndPositions = new int[substringsCount * 2]),
 
2131                 if (splitOperatorsCount == splitOperators.length) {
 
2132                   System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
 
2134                 substringsStartPositions[substringsCount] = position;
 
2135                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
 
2136                 // substring ends on operator start
 
2137                 position = splitScanner.currentPosition;
 
2138                 // next substring will start from operator end
 
2139                 splitOperators[splitOperatorsCount++] = currentToken;
 
2144           case TokenNameCOLON : // : (15.24)
 
2145             // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
 
2146             // indeed it might appear also behind a case statement, and we do not to break at this point.
 
2147             if ((splitOperatorsCount == 0) || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
 
2150           case TokenNameextends :
 
2151             //                  case TokenNameimplements :
 
2152             //                  case TokenNamethrows :
 
2154           case TokenNameDOT : // .
 
2155           case TokenNameMULTIPLY : // * (15.16.1)
 
2156           case TokenNameDIVIDE : // / (15.16.2)
 
2157           case TokenNameREMAINDER : // % (15.16.3)
 
2158           case TokenNamePLUS : // + (15.17, 15.17.2)
 
2159           case TokenNameMINUS : // - (15.17.2)
 
2160           case TokenNameLEFT_SHIFT : // << (15.18)
 
2161           case TokenNameRIGHT_SHIFT : // >> (15.18)
 
2162             //                          case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
2163           case TokenNameLESS : // < (15.19.1)
 
2164           case TokenNameLESS_EQUAL : // <= (15.19.1)
 
2165           case TokenNameGREATER : // > (15.19.1)
 
2166           case TokenNameGREATER_EQUAL : // >= (15.19.1)
 
2167             //                          case TokenNameinstanceof : // instanceof
 
2168           case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2169           case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2170           case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2171                                         case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2172           case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
 
2173           case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
 
2174           case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
 
2175           case TokenNameAND_AND : // && (15.22)
 
2176           case TokenNameOR_OR : // || (15.23)
 
2177           case TokenNameQUESTION : // ? (15.24)
 
2178           case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
 
2179           case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
 
2180           case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
 
2181           case TokenNamePLUS_EQUAL : // += (15.25.2)
 
2182           case TokenNameMINUS_EQUAL : // -= (15.25.2)
 
2183           case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
 
2184           case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
 
2185             //                                  case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
2186           case TokenNameAND_EQUAL : // &= (15.25.2)
 
2187           case TokenNameXOR_EQUAL : // ^= (15.25.2)
 
2188           case TokenNameOR_EQUAL : // |= (15.25.2)
 
2190             if ((openParenthesisPositionCount < splitTokenDepth
 
2191               || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
 
2192               && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS)
 
2193                 && (previousToken == TokenNameLBRACE || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
 
2194               // the current token is better than the one we currently have
 
2195               // (in level or in priority if same level)
 
2196               // reset the substringsCount
 
2197               splitTokenDepth = openParenthesisPositionCount;
 
2198               splitTokenType = currentToken;
 
2199               splitTokenPriority = getTokenPriority(currentToken);
 
2200               substringsStartPositions[0] = 0;
 
2201               // better token means the whole line until now is the first substring
 
2203               if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
 
2204                 substringsCount = 2; // resets the count of substrings
 
2206                 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
 
2207                 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
 
2208                 substringsEndPositions[1] = splitScanner.startPosition;
 
2209                 splitOperatorsCount = 3; // resets the count of split operators
 
2210                 splitOperators[0] = 0;
 
2211                 splitOperators[1] = 0;
 
2212                 splitOperators[2] = currentToken;
 
2213                 position = splitScanner.currentPosition;
 
2214                 // next substring will start from operator end
 
2216                 substringsCount = 1; // resets the count of substrings
 
2218                 substringsEndPositions[0] = splitScanner.startPosition;
 
2219                 // substring ends on operator start
 
2220                 position = splitScanner.currentPosition;
 
2221                 // next substring will start from operator end
 
2222                 splitOperatorsCount = 2; // resets the count of split operators
 
2223                 splitOperators[0] = 0;
 
2224                 // nothing for first operand since operator will be inserted in front of the second operand
 
2225                 splitOperators[1] = currentToken;
 
2229               if (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken)) {
 
2230                 // if another token with the same priority is found,
 
2231                 // push the start position of the substring and
 
2232                 // push the token into the stack.
 
2233                 // create a new array object if the current one is full.
 
2234                 if (substringsCount == substringsStartPositions.length) {
 
2236                     substringsStartPositions,
 
2238                     (substringsStartPositions = new int[substringsCount * 2]),
 
2242                     substringsEndPositions,
 
2244                     (substringsEndPositions = new int[substringsCount * 2]),
 
2248                 if (splitOperatorsCount == splitOperators.length) {
 
2249                   System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
 
2251                 substringsStartPositions[substringsCount] = position;
 
2252                 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
 
2253                 // substring ends on operator start
 
2254                 position = splitScanner.currentPosition;
 
2255                 // next substring will start from operator end
 
2256                 splitOperators[splitOperatorsCount++] = currentToken;
 
2262         if (isComment(currentToken)) {
 
2263           lastCommentStartPosition = splitScanner.startPosition;
 
2265           lastCommentStartPosition = -1;
 
2268     } catch (InvalidInputException e) {
 
2271     // if the string cannot be split, return null.
 
2272     if (splitOperatorsCount == 0)
 
2275     // ## SPECIAL CASES BEGIN
 
2276     if (((splitOperatorsCount == 2
 
2277       && splitOperators[1] == TokenNameDOT
 
2278       && splitTokenDepth == 0
 
2279       && lastOpenParenthesisPosition > -1)
 
2280       || (splitOperatorsCount > 2
 
2281         && splitOperators[1] == TokenNameDOT
 
2282         && splitTokenDepth == 0
 
2283         && lastOpenParenthesisPosition > -1
 
2284         && lastOpenParenthesisPosition <= options.maxLineLength)
 
2285       || (separateFirstArgumentOn(firstTokenOnLine) && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
 
2286       && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
 
2287       // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
 
2288       // only one split on a top level .
 
2289       // or more than one split on . and substring before open parenthesis fits one line.
 
2290       // or split inside parenthesis and first token is not a for/while/if
 
2291       SplitLine sl = split(stringToSplit.substring(lastOpenParenthesisPosition), lastOpenParenthesisPosition);
 
2292       if (sl == null || sl.operators[0] != TokenNameCOMMA) {
 
2293         // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
 
2294         return new SplitLine(
 
2297             stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
 
2298             stringToSplit.substring(lastOpenParenthesisPosition)},
 
2299           new int[] { offsetInGlobalLine, lastOpenParenthesisPosition + offsetInGlobalLine });
 
2301         // right substring can be split and is split on comma
 
2302         // copy substrings and operators
 
2303         // except if the 1st string is empty.
 
2304         int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
 
2305         int subStringsLength = sl.substrings.length + 1 - startIndex;
 
2306         String[] result = new String[subStringsLength];
 
2307         int[] startIndexes = new int[subStringsLength];
 
2308         int operatorsLength = sl.operators.length + 1 - startIndex;
 
2309         int[] operators = new int[operatorsLength];
 
2311         result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
 
2314         System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes, 1, subStringsLength - 1);
 
2315         for (int i = subStringsLength - 1; i >= 0; i--) {
 
2316           startIndexes[i] += offsetInGlobalLine;
 
2318         System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1);
 
2319         System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1);
 
2321         return new SplitLine(operators, result, startIndexes);
 
2324     // if the last token is a comment and the substring before the comment fits on a line,
 
2325     // split before the comment and return the result.
 
2326     if (lastCommentStartPosition > -1 && lastCommentStartPosition < options.maxLineLength && splitTokenPriority > 50) {
 
2327       int end = lastCommentStartPosition;
 
2328       int start = lastCommentStartPosition;
 
2329       if (stringToSplit.charAt(end - 1) == ' ') {
 
2332       if (start != end && stringToSplit.charAt(start) == ' ') {
 
2335       return new SplitLine(
 
2337         new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)},
 
2338         new int[] { 0, start });
 
2340     if (position != stringToSplit.length()) {
 
2341       if (substringsCount == substringsStartPositions.length) {
 
2343           substringsStartPositions,
 
2345           (substringsStartPositions = new int[substringsCount * 2]),
 
2348         System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount * 2]), 0, substringsCount);
 
2350       // avoid empty extra substring, e.g. line terminated with a semi-colon
 
2351       substringsStartPositions[substringsCount] = position;
 
2352       substringsEndPositions[substringsCount++] = stringToSplit.length();
 
2354     if (splitOperatorsCount == splitOperators.length) {
 
2355       System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
 
2357     splitOperators[splitOperatorsCount] = 0;
 
2359     // the last element of the stack is the position of the end of StringToSPlit
 
2360     // +1 because the substring method excludes the last character
 
2361     String[] result = new String[substringsCount];
 
2362     for (int i = 0; i < substringsCount; i++) {
 
2363       int start = substringsStartPositions[i];
 
2364       int end = substringsEndPositions[i];
 
2365       if (stringToSplit.charAt(start) == ' ') {
 
2367         substringsStartPositions[i]++;
 
2369       if (end != start && stringToSplit.charAt(end - 1) == ' ') {
 
2372       result[i] = stringToSplit.substring(start, end);
 
2373       substringsStartPositions[i] += offsetInGlobalLine;
 
2375     if (splitOperatorsCount > substringsCount) {
 
2376       System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[splitOperatorsCount]), 0, substringsCount);
 
2377       System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[splitOperatorsCount]), 0, substringsCount);
 
2378       for (int i = substringsCount; i < splitOperatorsCount; i++) {
 
2379         substringsStartPositions[i] = position;
 
2380         substringsEndPositions[i] = position;
 
2382       System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount]), 0, splitOperatorsCount);
 
2384       System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[substringsCount]), 0, substringsCount);
 
2385       System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount]), 0, substringsCount);
 
2386       System.arraycopy(splitOperators, 0, (splitOperators = new int[substringsCount]), 0, substringsCount);
 
2388     SplitLine splitLine = new SplitLine(splitOperators, result, substringsStartPositions);
 
2392   private void updateMappedPositions(int startPosition) {
 
2393     if (positionsToMap == null) {
 
2396     char[] source = scanner.source;
 
2397     int sourceLength = source.length;
 
2398     while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
 
2399       int posToMap = positionsToMap[indexToMap];
 
2400       if (posToMap < 0 || posToMap >= sourceLength) {
 
2401         // protection against out of bounds position
 
2402         if (posToMap == sourceLength) {
 
2403           mappedPositions[indexToMap] = formattedSource.length();
 
2405         indexToMap = positionsToMap.length; // no more mapping
 
2408       if (CharOperation.isWhitespace(source[posToMap])) {
 
2409         mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2411         if (posToMap == sourceLength - 1) {
 
2412           mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2414           mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
 
2421   private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
 
2422     if (mappedPositions == null || mappedPositions.length == indexInMap)
 
2425     while (indexInMap < mappedPositions.length
 
2426       && startPosition <= mappedPositions[indexInMap]
 
2427       && mappedPositions[indexInMap] < endPosition
 
2428       && indexInMap < indexToMap) {
 
2429       mappedPositions[indexInMap] += splitDelta;
 
2434   private int getLength(String s, int tabDepth) {
 
2436     for (int i = 0; i < tabDepth; i++) {
 
2437       length += options.tabSize;
 
2439     for (int i = 0, max = s.length(); i < max; i++) {
 
2440       char currentChar = s.charAt(i);
 
2441       switch (currentChar) {
 
2443           length += options.tabSize;
 
2453   * Sets the initial indentation level
 
2454   * @param indentationLevel new indentation level
 
2458   public void setInitialIndentationLevel(int newIndentationLevel) {
 
2459     this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;