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;
 
  27 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
 
  28 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
 
  30 /** <h2>How to format a piece of code ?</h2>
 
  31  * <ul><li>Create an instance of <code>CodeFormatter</code>
 
  32  * <li>Use the method <code>void format(aString)</code>
 
  33  * on this instance to format <code>aString</code>.
 
  34  * It will return the formatted string.</ul>
 
  36 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
 
  38   public FormatterOptions options;
 
  41    * Represents a block in the <code>constructions</code> stack.
 
  43   public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
 
  46    * Represents a block following a control statement in the <code>constructions</code> stack.
 
  48   public static final int NONINDENT_BLOCK = -100;
 
  51    * Contains the formatted output.
 
  53   StringBuffer formattedSource;
 
  56    * Contains the current line.<br>
 
  57    * Will be dumped at the next "newline"
 
  59   StringBuffer currentLineBuffer;
 
  62    * Used during the formatting to get each token.
 
  67    * Contains the tokens responsible for the current indentation level
 
  68    * and the blocks not closed yet.
 
  70   private int[] constructions;
 
  73    * Index in the <code>constructions</code> array.
 
  75   private int constructionsCount;
 
  78    * Level of indentation of the current token (number of tab char put in front of it).
 
  80   private int indentationLevel;
 
  83    * Regular level of indentation of all the lines
 
  85   private int initialIndentationLevel;
 
  88    * Used to split a line.
 
  93    * To remember the offset between the beginning of the line and the
 
  94    * beginning of the comment.
 
  96   int currentCommentOffset;
 
  97   int currentLineIndentationLevel;
 
  99   private boolean containsOpenCloseBraces;
 
 100   private int indentationLevelForOpenCloseBraces;
 
 103    * Collections of positions to map
 
 105   private int[] positionsToMap;
 
 108    * Collections of mapped positions
 
 110   private int[] mappedPositions;
 
 112   private int indexToMap;
 
 114   private int indexInMap;
 
 116   private int globalDelta;
 
 118   private int lineDelta;
 
 120   private int splitDelta;
 
 122   private int beginningOfLineIndex;
 
 124   private int multipleLineCommentCounter;
 
 127    * Creates a new instance of Code Formatter using the given settings.
 
 129    * @deprecated backport 1.0 internal functionality
 
 131   public CodeFormatter(ConfigurableOption[] settings) {
 
 132     this(convertConfigurableOptions(settings));
 
 136    * Creates a new instance of Code Formatter using the FormattingOptions object
 
 138    * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
 
 140   public CodeFormatter() {
 
 144    * Creates a new instance of Code Formatter using the given settings.
 
 146   public CodeFormatter(Map settings) {
 
 148     // initialize internal state
 
 149     constructionsCount = 0;
 
 150     constructions = new int[10];
 
 151     currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
 
 152     currentCommentOffset = -1;
 
 154     // initialize primary and secondary scanners
 
 155     scanner = new Scanner(true /*comment*/
 
 156     , true /*whitespace*/
 
 159     ); // regular scanner for forming lines
 
 160     scanner.recordLineSeparator = true;
 
 162     // to remind of the position of the beginning of the line.
 
 163     splitScanner = new Scanner(true /*comment*/
 
 164     , true /*whitespace*/
 
 168     // secondary scanner to split long lines formed by primary scanning
 
 170     // initialize current line buffer
 
 171     currentLineBuffer = new StringBuffer();
 
 172     this.options = new FormatterOptions(settings);
 
 176    * Returns true if a lineSeparator has to be inserted before <code>operator</code>
 
 179   private static boolean breakLineBeforeOperator(int operator) {
 
 181       case TokenNameCOMMA :
 
 182       case TokenNameSEMICOLON :
 
 183       case TokenNameEQUAL :
 
 191   * @deprecated backport 1.0 internal functionality
 
 193   private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
 
 194     Hashtable options = new Hashtable(10);
 
 196     for (int i = 0; i < settings.length; i++) {
 
 199         .equals(CodeFormatter.class.getName())) {
 
 200         String optionName = settings[i].getOptionName();
 
 201         int valueIndex = settings[i].getCurrentValueIndex();
 
 203         if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
 
 204           options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 206         } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
 
 207           options.put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 209         } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
 
 210           options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 212         } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
 
 213           options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 215         } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
 
 216           options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 218         } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
 
 219           options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 221         } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
 
 222           options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 224         } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
 
 225           options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 227         } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
 
 228           options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
 
 237    * Returns the end of the source code.
 
 239   private final String copyRemainingSource() {
 
 240     char str[] = scanner.source;
 
 241     int startPosition = scanner.startPosition;
 
 242     int length = str.length - startPosition;
 
 243     StringBuffer bufr = new StringBuffer(length);
 
 244     if (startPosition < str.length) {
 
 245       bufr.append(str, startPosition, length);
 
 247     return (bufr.toString());
 
 251    * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
 
 253   private void dumpTab(int tabCount) {
 
 254     if (options.indentWithTab) {
 
 255       for (int j = 0; j < tabCount; j++) {
 
 256         formattedSource.append('\t');
 
 257         increaseSplitDelta(1);
 
 260       for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
 
 261         formattedSource.append(' ');
 
 262         increaseSplitDelta(1);
 
 268    * Dumps <code>currentLineBuffer</code> into the formatted string.
 
 270   private void flushBuffer() {
 
 271     String currentString = currentLineBuffer.toString();
 
 273     beginningOfLineIndex = formattedSource.length();
 
 274     if (containsOpenCloseBraces) {
 
 275       containsOpenCloseBraces = false;
 
 279         indentationLevelForOpenCloseBraces,
 
 284       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 289         currentLineIndentationLevel,
 
 295     int scannerSourceLength = scanner.source.length;
 
 296     if (scannerSourceLength > 2) {
 
 297       if (scanner.source[scannerSourceLength - 1] == '\n'
 
 298         && scanner.source[scannerSourceLength - 2] == '\r') {
 
 299         formattedSource.append(options.lineSeparatorSequence);
 
 300         increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
 301       } else if (scanner.source[scannerSourceLength - 1] == '\n') {
 
 302         formattedSource.append(options.lineSeparatorSequence);
 
 303         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 304       } else if (scanner.source[scannerSourceLength - 1] == '\r') {
 
 305         formattedSource.append(options.lineSeparatorSequence);
 
 306         increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
 309     updateMappedPositions(scanner.startPosition);
 
 313    * Formats the input string.
 
 315   private void format() {
 
 317     int previousToken = 0;
 
 318     int previousCompilableToken = 0;
 
 319     int indentationOffset = 0;
 
 320     int newLinesInWhitespace = 0;
 
 322     // number of new lines in the previous whitespace token
 
 323     // (used to leave blank lines before comments)
 
 324     int pendingNewLines = 0;
 
 325     boolean expectingOpenBrace = false;
 
 326     boolean clearNonBlockIndents = false;
 
 327     // true if all indentations till the 1st { (usefull after } or ;)
 
 328     boolean pendingSpace = true;
 
 329     boolean pendingNewlineAfterParen = false;
 
 330     // true when a cr is to be put after a ) (in conditional statements)
 
 331     boolean inAssignment = false;
 
 332     boolean inArrayAssignment = false;
 
 333     boolean inThrowsClause = false;
 
 334     boolean inClassOrInterfaceHeader = false;
 
 336     // openBracketCount is used to count the number of open brackets not closed yet.
 
 337     int openBracketCount = 0;
 
 338     int unarySignModifier = 0;
 
 340     // openParenthesis[0] is used to count the parenthesis not belonging to a condition
 
 341     // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
 
 342     int openParenthesisCount = 1;
 
 343     int[] openParenthesis = new int[10];
 
 345     // tokenBeforeColon is used to know what token goes along with the current :
 
 346     // it can be case or ?
 
 347     int tokenBeforeColonCount = 0;
 
 348     int[] tokenBeforeColon = new int[10];
 
 350     constructionsCount = 0; // initializes the constructions count.
 
 352     // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
 
 355     // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else 
 
 356     boolean specialElse = false;
 
 358     // OPTION (IndentationLevel): initial indentation level may be non-zero.
 
 359     currentLineIndentationLevel += constructionsCount;
 
 361     // An InvalidInputException exception might cause the termination of this loop.
 
 364         // Get the next token.  Catch invalid input and output it
 
 365         // with minimal formatting, also catch end of input and
 
 368           token = scanner.getNextToken();
 
 370           // Patch for line comment
 
 371           // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
 
 372           if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
 
 373             int length = scanner.currentPosition;
 
 374             loop : for (int index = length - 1; index >= 0; index--) {
 
 375               switch (scanner.source[index]) {
 
 378                   scanner.currentPosition--;
 
 385         } catch (InvalidInputException e) {
 
 386           if (!handleInvalidToken(e)) {
 
 391         if (token == Scanner.TokenNameEOF)
 
 394         /* ## MODIFYING the indentation level before generating new lines
 
 395         and indentation in the output string
 
 398         // Removes all the indentations made by statements not followed by a block
 
 399         // except if the current token is ELSE, CATCH or if we are in a switch/case
 
 400         if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
 
 403               if (constructionsCount > 0
 
 404                 && constructions[constructionsCount - 1] == TokenNameelse) {
 
 408               indentationLevel += popInclusiveUntil(TokenNameif);
 
 410               //                                                case TokenNamecatch :
 
 411               //                                                        indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 413               //                                                case TokenNamefinally :
 
 414               //                                                        indentationLevel += popInclusiveUntil(TokenNamecatch);
 
 416             case TokenNamewhile :
 
 417               if (nlicsToken == TokenNamedo) {
 
 418                 indentationLevel += pop(TokenNamedo);
 
 422               indentationLevel += popExclusiveUntilBlockOrCase();
 
 423               // clear until a CASE, DEFAULT or BLOCK is encountered.
 
 424               // Thus, the indentationLevel is correctly cleared either
 
 425               // in a switch/case statement or in any other situation.
 
 427           clearNonBlockIndents = false;
 
 429         // returns to the indentation level created by the SWITCH keyword
 
 430         // if the current token is a CASE or a DEFAULT
 
 431         if (token == TokenNamecase || token == TokenNamedefault) {
 
 432           indentationLevel += pop(TokenNamecase);
 
 434         //                              if (token == Scanner.TokenNamethrows) {
 
 435         //                                      inThrowsClause = true;
 
 438           == Scanner.TokenNameclass // || token == Scanner.TokenNameinterface
 
 440           && previousToken != Scanner.TokenNameDOT) {
 
 441           inClassOrInterfaceHeader = true;
 
 444         /* ## APPEND newlines and indentations to the output string
 
 446         // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
 
 447         // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
 
 448         if (pendingNewlineAfterParen
 
 449           && previousCompilableToken == TokenNameelse
 
 450           && token == TokenNameif
 
 451           && options.compactElseIfMode) {
 
 452           pendingNewlineAfterParen = false;
 
 454           indentationLevel += pop(TokenNameelse);
 
 455           // because else if is now one single statement,
 
 456           // the indentation level after it is increased by one and not by 2
 
 457           // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
 
 459         // Add a newline & indent to the formatted source string if
 
 460         // a for/if-else/while statement was scanned and there is no block
 
 462         pendingNewlineAfterParen =
 
 463           pendingNewlineAfterParen
 
 464             || (previousCompilableToken == TokenNameRPAREN
 
 465               && token == TokenNameLBRACE);
 
 466         if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
 
 467           pendingNewlineAfterParen = false;
 
 469           // Do to add a newline & indent sequence if the current token is an
 
 470           // open brace or a period or if the current token is a semi-colon and the
 
 471           // previous token is a close paren.
 
 472           // add a new line if a parenthesis belonging to a for() statement
 
 473           // has been closed and the current token is not an opening brace
 
 474           if (token != TokenNameLBRACE
 
 475             && !isComment(token) // to avoid adding new line between else and a comment
 
 476             && token != TokenNameDOT
 
 477             && !(previousCompilableToken == TokenNameRPAREN
 
 478               && token == TokenNameSEMICOLON)) {
 
 480             currentLineIndentationLevel = indentationLevel;
 
 482             pendingSpace = false;
 
 484             if (token == TokenNameLBRACE
 
 485               && options.newLineBeforeOpeningBraceMode) {
 
 487               if (constructionsCount > 0
 
 488                 && constructions[constructionsCount - 1] != BLOCK
 
 489                 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
 
 490                 currentLineIndentationLevel = indentationLevel - 1;
 
 492                 currentLineIndentationLevel = indentationLevel;
 
 495               pendingSpace = false;
 
 499         if (token == TokenNameLBRACE
 
 500           && options.newLineBeforeOpeningBraceMode
 
 501           && constructionsCount > 0
 
 502           && constructions[constructionsCount - 1] == TokenNamedo) {
 
 504           currentLineIndentationLevel = indentationLevel - 1;
 
 506           pendingSpace = false;
 
 509         if (token == TokenNameLBRACE && inThrowsClause) {
 
 510           inThrowsClause = false;
 
 511           if (options.newLineBeforeOpeningBraceMode) {
 
 513             currentLineIndentationLevel = indentationLevel;
 
 515             pendingSpace = false;
 
 519         if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
 
 520           inClassOrInterfaceHeader = false;
 
 521           if (options.newLineBeforeOpeningBraceMode) {
 
 523             currentLineIndentationLevel = indentationLevel;
 
 525             pendingSpace = false;
 
 528         // Add pending new lines to the formatted source string.
 
 529         // Note: pending new lines are not added if the current token
 
 530         // is a single line comment or whitespace.
 
 531         // if the comment is between parenthesis, there is no blank line preservation
 
 532         // (if it's a one-line comment, a blank line is added after it).
 
 533         if (((pendingNewLines > 0 && (!isComment(token)))
 
 534           || (newLinesInWhitespace > 0
 
 535             && (openParenthesisCount <= 1 && isComment(token)))
 
 536           || (previousCompilableToken == TokenNameLBRACE
 
 537             && token == TokenNameRBRACE))
 
 538           && token != Scanner.TokenNameWHITESPACE) {
 
 540           // Do not add newline & indent between an adjoining close brace and
 
 541           // close paren.  Anonymous inner classes may use this form.
 
 542           boolean closeBraceAndCloseParen =
 
 543             previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
 
 545           // OPTION (NewLineInCompoundStatement): do not add newline & indent
 
 546           // between close brace and else, (do) while, catch, and finally if
 
 547           // newlineInCompoundStatement is true.
 
 548           boolean nlicsOption =
 
 549             previousToken == TokenNameRBRACE
 
 550               && !options.newlineInControlStatementMode
 
 551               && (token == TokenNameelse
 
 552                 || (token == TokenNamewhile && nlicsToken == TokenNamedo));
 
 553           //                                                            || token == TokenNamecatch
 
 554           //                                                            || token == TokenNamefinally);
 
 556           // Do not add a newline & indent between a close brace and semi-colon.
 
 557           boolean semiColonAndCloseBrace =
 
 558             previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
 
 560           // Do not add a new line & indent between a multiline comment and a opening brace
 
 561           boolean commentAndOpenBrace =
 
 562             previousToken == Scanner.TokenNameCOMMENT_BLOCK
 
 563               && token == TokenNameLBRACE;
 
 565           // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
 
 566           boolean commaAndCloseBrace =
 
 567             previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
 
 569           // Add a newline and indent, if appropriate.
 
 571             || (!commentAndOpenBrace
 
 572               && !closeBraceAndCloseParen
 
 574               && !semiColonAndCloseBrace
 
 575               && !commaAndCloseBrace)) {
 
 577             // if clearAllBlankLinesMode=false, leaves the blank lines
 
 578             // inserted by the user
 
 579             // if clearAllBlankLinesMode=true, removes all of then
 
 580             // and insert only blank lines required by the formatting.
 
 581             if (!options.clearAllBlankLinesMode) {
 
 582               //  (isComment(token))
 
 584                 (pendingNewLines < newLinesInWhitespace)
 
 585                   ? newLinesInWhitespace
 
 587               pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
 
 589             if (previousCompilableToken == TokenNameLBRACE
 
 590               && token == TokenNameRBRACE) {
 
 591               containsOpenCloseBraces = true;
 
 592               indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
 593               if (isComment(previousToken)) {
 
 594                 newLine(pendingNewLines);
 
 596                 /*  if (!(constructionsCount > 1
 
 597                         && constructions[constructionsCount-1] == NONINDENT_BLOCK
 
 598                         && (constructions[constructionsCount-2] == TokenNamefor 
 
 599                          || constructions[constructionsCount-2] == TokenNamewhile))) {*/
 
 600                 if (options.newLineInEmptyBlockMode) {
 
 601                   if (inArrayAssignment) {
 
 602                     newLine(1); // array assigment with an empty block
 
 604                     newLine(pendingNewLines);
 
 610               // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
 
 611               if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK
 
 612                 || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
 
 613                 && token == TokenNameSEMICOLON)) {
 
 614                 newLine(pendingNewLines);
 
 617             if (((previousCompilableToken == TokenNameSEMICOLON)
 
 618               || (previousCompilableToken == TokenNameLBRACE)
 
 619               || (previousCompilableToken == TokenNameRBRACE)
 
 620               || (isComment(previousToken)))
 
 621               && (token == TokenNameRBRACE)) {
 
 622               indentationOffset = -1;
 
 623               indentationLevel += popExclusiveUntilBlock();
 
 625             if (previousToken == Scanner.TokenNameCOMMENT_LINE
 
 628               currentLineIndentationLevel++;
 
 630               currentLineIndentationLevel =
 
 631                 indentationLevel + indentationOffset;
 
 633             pendingSpace = false;
 
 634             indentationOffset = 0;
 
 637           newLinesInWhitespace = 0;
 
 640           if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
 
 646             //                          case TokenNamefinally :
 
 647             expectingOpenBrace = true;
 
 648             pendingNewlineAfterParen = true;
 
 649             indentationLevel += pushControlStatement(token);
 
 652           case TokenNamedefault :
 
 653             if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 657                 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
 
 659                 tokenBeforeColonCount);
 
 661             tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
 
 662             indentationLevel += pushControlStatement(TokenNamecase);
 
 664           case TokenNameQUESTION :
 
 665             if (tokenBeforeColonCount == tokenBeforeColon.length) {
 
 669                 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
 
 671                 tokenBeforeColonCount);
 
 673             tokenBeforeColon[tokenBeforeColonCount++] = token;
 
 675           case TokenNameswitch :
 
 678           case TokenNamewhile :
 
 679             if (openParenthesisCount == openParenthesis.length) {
 
 683                 (openParenthesis = new int[openParenthesisCount * 2]),
 
 685                 openParenthesisCount);
 
 687             openParenthesis[openParenthesisCount++] = 0;
 
 688             expectingOpenBrace = true;
 
 690             indentationLevel += pushControlStatement(token);
 
 692             //                                  case TokenNametry :
 
 693             //                                          pendingNewlineAfterParen = true;
 
 694             //                                  case TokenNamecatch :
 
 695             //                                          // several CATCH statements can be contiguous.
 
 696             //                                          // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
 
 697             //                                          // as CATCH and TRY are the same token in the stack).
 
 698             //                                          expectingOpenBrace = true;
 
 699             //                                          indentationLevel += pushControlStatement(TokenNamecatch);
 
 703             expectingOpenBrace = true;
 
 704             indentationLevel += pushControlStatement(token);
 
 709           case TokenNameLPAREN :
 
 710             //                                          if (previousToken == TokenNamesynchronized) {
 
 711             //                                                  indentationLevel += pushControlStatement(previousToken);
 
 713             // Put a space between the previous and current token if the
 
 714             // previous token was not a keyword, open paren, logical
 
 715             // compliment (eg: !), semi-colon, open brace, close brace,
 
 717             if (previousCompilableToken != TokenNameLBRACKET
 
 718               && previousToken != TokenNameIdentifier
 
 719               && previousToken != 0
 
 720               && previousToken != TokenNameNOT
 
 721               && previousToken != TokenNameLPAREN
 
 722               && previousToken != TokenNameTWIDDLE
 
 723               && previousToken != TokenNameSEMICOLON
 
 724               && previousToken != TokenNameLBRACE
 
 725               && previousToken != TokenNameRBRACE) {
 
 726               //                                                                && previousToken != TokenNamesuper
 
 727               //                                                                && previousToken != TokenNamethis) {
 
 730             // If in a for/if/while statement, increase the parenthesis count
 
 731             // for the current openParenthesisCount
 
 732             // else increase the count for stand alone parenthesis.
 
 733             if (openParenthesisCount > 0)
 
 734               openParenthesis[openParenthesisCount - 1]++;
 
 736               openParenthesis[0]++;
 
 738             pendingSpace = false;
 
 741           case TokenNameRPAREN :
 
 743             // Decrease the parenthesis count
 
 744             // if there is no more unclosed parenthesis,
 
 745             // a new line and indent may be append (depending on the next token).
 
 746             if ((openParenthesisCount > 1)
 
 747               && (openParenthesis[openParenthesisCount - 1] > 0)) {
 
 748               openParenthesis[openParenthesisCount - 1]--;
 
 749               if (openParenthesis[openParenthesisCount - 1] <= 0) {
 
 750                 pendingNewlineAfterParen = true;
 
 751                 inAssignment = false;
 
 752                 openParenthesisCount--;
 
 755               openParenthesis[0]--;
 
 757             pendingSpace = false;
 
 759           case TokenNameLBRACE :
 
 760             if ((previousCompilableToken == TokenNameRBRACKET)
 
 761               || (previousCompilableToken == TokenNameEQUAL)) {
 
 762               //                  if (previousCompilableToken == TokenNameRBRACKET) {
 
 763               inArrayAssignment = true;
 
 764               inAssignment = false;
 
 766             if (inArrayAssignment) {
 
 767               indentationLevel += pushBlock();
 
 769               // Add new line and increase indentation level after open brace.
 
 771               indentationLevel += pushBlock();
 
 774           case TokenNameRBRACE :
 
 775             if (previousCompilableToken == TokenNameRPAREN) {
 
 776               pendingSpace = false;
 
 778             if (inArrayAssignment) {
 
 779               inArrayAssignment = false;
 
 781               indentationLevel += popInclusiveUntilBlock();
 
 784               indentationLevel += popInclusiveUntilBlock();
 
 786               if (previousCompilableToken == TokenNameRPAREN) {
 
 787                 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
 
 788                 currentLineBuffer.append(options.lineSeparatorSequence);
 
 789                 increaseLineDelta(options.lineSeparatorSequence.length);
 
 791               if (constructionsCount > 0) {
 
 792                 switch (constructions[constructionsCount - 1]) {
 
 794                     //indentationLevel += popExclusiveUntilBlock();
 
 796                   case TokenNameswitch :
 
 799                     //                                                                  case TokenNametry :
 
 800                     //                                                                  case TokenNamecatch :
 
 801                     //                                                                  case TokenNamefinally :
 
 802                   case TokenNamewhile :
 
 804                     //                                                                  case TokenNamesynchronized :
 
 805                     clearNonBlockIndents = true;
 
 812           case TokenNameLBRACKET :
 
 814             pendingSpace = false;
 
 816           case TokenNameRBRACKET :
 
 817             openBracketCount -= (openBracketCount > 0) ? 1 : 0;
 
 818             // if there is no left bracket to close, the right bracket is ignored.
 
 819             pendingSpace = false;
 
 821           case TokenNameCOMMA :
 
 823             pendingSpace = false;
 
 825           case TokenNameSEMICOLON :
 
 827             // Do not generate line terminators in the definition of
 
 828             // the for statement.
 
 829             // if not in this case, jump a line and reduce indentation after the brace
 
 830             // if the block it closes belongs to a conditional statement (if, while, do...).
 
 831             if (openParenthesisCount <= 1) {
 
 833               if (expectingOpenBrace) {
 
 834                 clearNonBlockIndents = true;
 
 835                 expectingOpenBrace = false;
 
 838             inAssignment = false;
 
 839             pendingSpace = false;
 
 841           case TokenNamePLUS_PLUS :
 
 842           case TokenNameMINUS_MINUS :
 
 844             // Do not put a space between a post-increment/decrement
 
 845             // and the identifier being modified.
 
 846             if (previousToken == TokenNameIdentifier
 
 847               || previousToken == TokenNameRBRACKET) {
 
 848               pendingSpace = false;
 
 851           case TokenNamePLUS : // previously ADDITION
 
 852           case TokenNameMINUS :
 
 854             // Handle the unary operators plus and minus via a flag
 
 855             if (!isLiteralToken(previousToken)
 
 856               && previousToken != TokenNameIdentifier
 
 857               && previousToken != TokenNameRPAREN
 
 858               && previousToken != TokenNameRBRACKET) {
 
 859               unarySignModifier = 1;
 
 862           case TokenNameCOLON :
 
 863             // In a switch/case statement, add a newline & indent
 
 864             // when a colon is encountered.
 
 865             if (tokenBeforeColonCount > 0) {
 
 866               if (tokenBeforeColon[tokenBeforeColonCount - 1]
 
 870               tokenBeforeColonCount--;
 
 873           case TokenNameEQUAL :
 
 876           case Scanner.TokenNameCOMMENT_LINE :
 
 879               currentLineIndentationLevel++;
 
 881             break; // a line is always inserted after a one-line comment
 
 882           case Scanner.TokenNameCOMMENT_PHPDOC :
 
 883           case Scanner.TokenNameCOMMENT_BLOCK :
 
 884             currentCommentOffset = getCurrentCommentOffset();
 
 887           case Scanner.TokenNameWHITESPACE :
 
 889             // Count the number of line terminators in the whitespace so
 
 890             // line spacing can be preserved near comments.
 
 891             char[] source = scanner.source;
 
 892             newLinesInWhitespace = 0;
 
 893             for (int i = scanner.startPosition, max = scanner.currentPosition;
 
 896               if (source[i] == '\r') {
 
 898                   if (source[++i] == '\n') {
 
 899                     newLinesInWhitespace++;
 
 901                     newLinesInWhitespace++;
 
 904                   newLinesInWhitespace++;
 
 906               } else if (source[i] == '\n') {
 
 907                 newLinesInWhitespace++;
 
 910             increaseLineDelta(scanner.startPosition - scanner.currentPosition);
 
 913             if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
 
 914               //                                                        || token == TokenNamesuper
 
 915               //                                                        || token == TokenNamethis) {
 
 917               // Do not put a space between a unary operator
 
 918               // (eg: ++, --, +, -) and the identifier being modified.
 
 919               if (previousToken == TokenNamePLUS_PLUS
 
 920                 || previousToken == TokenNameMINUS_MINUS
 
 921                 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
 
 922                 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
 
 923                 pendingSpace = false;
 
 925               unarySignModifier = 0;
 
 929         // Do not output whitespace tokens.
 
 930         if (token != Scanner.TokenNameWHITESPACE) {
 
 932           /* Add pending space to the formatted source string.
 
 933           Do not output a space under the following circumstances:
 
 934           1) this is the first pass
 
 935           2) previous token is an open paren
 
 936           3) previous token is a period
 
 937           4) previous token is the logical compliment (eg: !)
 
 938           5) previous token is the bitwise compliment (eg: ~)
 
 939           6) previous token is the open bracket (eg: [)
 
 940           7) in an assignment statement, if the previous token is an 
 
 941           open brace or the current token is a close brace
 
 942           8) previous token is a single line comment
 
 944           boolean openAndCloseBrace =
 
 945             previousCompilableToken == TokenNameLBRACE
 
 946               && token == TokenNameRBRACE;
 
 949             && insertSpaceAfter(previousToken)
 
 951               && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
 
 952             && previousToken != Scanner.TokenNameCOMMENT_LINE) {
 
 953             if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
 
 954               && !openAndCloseBrace)
 
 957           // Add the next token to the formatted source string.
 
 958           outputCurrentToken(token);
 
 959           if (token == Scanner.TokenNameCOMMENT_LINE
 
 960             && openParenthesisCount > 1) {
 
 962             currentLineBuffer.append(options.lineSeparatorSequence);
 
 963             increaseLineDelta(options.lineSeparatorSequence.length);
 
 967         // Whitespace tokens do not need to be remembered.
 
 968         if (token != Scanner.TokenNameWHITESPACE) {
 
 969           previousToken = token;
 
 970           if (token != Scanner.TokenNameCOMMENT_BLOCK
 
 971             && token != Scanner.TokenNameCOMMENT_LINE
 
 972             && token != Scanner.TokenNameCOMMENT_PHPDOC) {
 
 973             previousCompilableToken = token;
 
 977       output(copyRemainingSource());
 
 979       // dump the last token of the source in the formatted output.
 
 980     } catch (InvalidInputException e) {
 
 981       output(copyRemainingSource());
 
 983       // dump the last token of the source in the formatted output.
 
 988    * Formats the char array <code>sourceString</code>,
 
 989    * and returns a string containing the formatted version.
 
 990    * @return the formatted ouput.
 
 992   public String formatSourceString(String sourceString) {
 
 993     char[] sourceChars = sourceString.toCharArray();
 
 994     formattedSource = new StringBuffer(sourceChars.length);
 
 995     scanner.setSource(sourceChars);
 
 997     return formattedSource.toString();
 
1001    * Formats the char array <code>sourceString</code>,
 
1002    * and returns a string containing the formatted version.
 
1003    * @param string the string to format
 
1004    * @param indentationLevel the initial indentation level
 
1005    * @return the formatted ouput.
 
1007   public String format(String string, int indentationLevel) {
 
1008     return format(string, indentationLevel, (int[]) null);
 
1012    * Formats the char array <code>sourceString</code>,
 
1013    * and returns a string containing the formatted version.
 
1014    * The positions array is modified to contain the mapped positions.
 
1015    * @param string the string to format
 
1016    * @param indentationLevel the initial indentation level
 
1017    * @param positions the array of positions to map
 
1018    * @return the formatted ouput.
 
1020   public String format(String string, int indentationLevel, int[] positions) {
 
1021     return this.format(string, indentationLevel, positions, null);
 
1024   public String format(
 
1026     int indentationLevel,
 
1028     String lineSeparator) {
 
1029     if (lineSeparator != null) {
 
1030       this.options.setLineSeparator(lineSeparator);
 
1032     if (positions != null) {
 
1033       this.setPositionsToMap(positions);
 
1034       this.setInitialIndentationLevel(indentationLevel);
 
1035       String formattedString = this.formatSourceString(string);
 
1036       int[] mappedPositions = this.getMappedPositions();
 
1037       System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
 
1038       return formattedString;
 
1040       this.setInitialIndentationLevel(indentationLevel);
 
1041       return this.formatSourceString(string);
 
1045    * Formats the char array <code>sourceString</code>,
 
1046    * and returns a string containing the formatted version. The initial indentation level is 0.
 
1047    * @param string the string to format
 
1048    * @return the formatted ouput.
 
1050   public String format(String string) {
 
1051     return this.format(string, 0, (int[]) null);
 
1055    * Formats a given source string, starting indenting it at a particular 
 
1056    * depth and using the given options
 
1058    * @deprecated backport 1.0 internal functionality
 
1060   public static String format(
 
1061     String sourceString,
 
1062     int initialIndentationLevel,
 
1063     ConfigurableOption[] options) {
 
1064     CodeFormatter formatter = new CodeFormatter(options);
 
1065     formatter.setInitialIndentationLevel(initialIndentationLevel);
 
1066     return formatter.formatSourceString(sourceString);
 
1070    * Returns the number of characters and tab char between the beginning of the line
 
1071    * and the beginning of the comment.
 
1073   private int getCurrentCommentOffset() {
 
1074     int linePtr = scanner.linePtr;
 
1075     // if there is no beginning of line, return 0.
 
1079     int beginningOfLine = scanner.lineEnds[linePtr];
 
1080     int currentStartPosition = scanner.startPosition;
 
1081     char[] source = scanner.source;
 
1083     // find the position of the beginning of the line containing the comment
 
1084     while (beginningOfLine > currentStartPosition) {
 
1086         beginningOfLine = scanner.lineEnds[--linePtr];
 
1088         beginningOfLine = 0;
 
1092     for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
 
1093       char currentCharacter = source[i];
 
1094       switch (currentCharacter) {
 
1096           offset += options.tabSize;
 
1112    * Returns an array of descriptions for the configurable options.
 
1113    * The descriptions may be changed and passed back to a different
 
1116    * @deprecated backport 1.0 internal functionality
 
1118   public static ConfigurableOption[] getDefaultOptions(Locale locale) {
 
1119     String componentName = CodeFormatter.class.getName();
 
1120     FormatterOptions options = new FormatterOptions();
 
1121     return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
 
1122       new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
 
1123       new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
 
1124       new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
 
1125       new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
 
1126       new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
 
1127       new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
 
1128       new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
 
1129       new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
 
1134    * Returns the array of mapped positions.
 
1135    * Returns null is no positions have been set.
 
1137    * @deprecated There is no need to retrieve the mapped positions anymore.
 
1139   public int[] getMappedPositions() {
 
1140     return mappedPositions;
 
1144    * Returns the priority of the token given as argument<br>
 
1145    * The most prioritary the token is, the smallest the return value is.
 
1146    * @return the priority of <code>token</code>
 
1147    * @param token the token of which the priority is requested
 
1149   private static int getTokenPriority(int token) {
 
1151       case TokenNameextends :
 
1152         //                      case TokenNameimplements :
 
1153         //                      case TokenNamethrows :
 
1155       case TokenNameSEMICOLON : // ;
 
1157       case TokenNameCOMMA : // ,
 
1159       case TokenNameEQUAL : // =
 
1161       case TokenNameAND_AND : // && 
 
1162       case TokenNameOR_OR : // || 
 
1164       case TokenNameQUESTION : // ? 
 
1165       case TokenNameCOLON : // :
 
1166         return 50; // it's better cutting on ?: than on ;
 
1167       case TokenNameEQUAL_EQUAL : // == 
 
1168       case TokenNameNOT_EQUAL : // != 
 
1170       case TokenNameLESS : // < 
 
1171       case TokenNameLESS_EQUAL : // <= 
 
1172       case TokenNameGREATER : // > 
 
1173       case TokenNameGREATER_EQUAL : // >= 
 
1174         //                      case TokenNameinstanceof : // instanceof
 
1176       case TokenNamePLUS : // + 
 
1177       case TokenNameMINUS : // - 
 
1179       case TokenNameMULTIPLY : // * 
 
1180       case TokenNameDIVIDE : // / 
 
1181       case TokenNameREMAINDER : // % 
 
1183       case TokenNameLEFT_SHIFT : // << 
 
1184       case TokenNameRIGHT_SHIFT : // >> 
 
1185         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> 
 
1187       case TokenNameAND : // &
 
1188       case TokenNameOR : // | 
 
1189       case TokenNameXOR : // ^ 
 
1191       case TokenNameMULTIPLY_EQUAL : // *= 
 
1192       case TokenNameDIVIDE_EQUAL : // /= 
 
1193       case TokenNameREMAINDER_EQUAL : // %= 
 
1194       case TokenNamePLUS_EQUAL : // += 
 
1195       case TokenNameMINUS_EQUAL : // -= 
 
1196       case TokenNameLEFT_SHIFT_EQUAL : // <<= 
 
1197       case TokenNameRIGHT_SHIFT_EQUAL : // >>= 
 
1198         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
 
1199       case TokenNameAND_EQUAL : // &= 
 
1200       case TokenNameXOR_EQUAL : // ^= 
 
1201       case TokenNameOR_EQUAL : // |= 
 
1203       case TokenNameDOT : // .
 
1206         return Integer.MAX_VALUE;
 
1211    * Handles the exception raised when an invalid token is encountered.
 
1212    * Returns true if the exception has been handled, false otherwise.
 
1214   private boolean handleInvalidToken(Exception e) {
 
1215     if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
 
1216       || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
 
1217       || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
 
1223   private final void increaseGlobalDelta(int offset) {
 
1224     globalDelta += offset;
 
1227   private final void increaseLineDelta(int offset) {
 
1228     lineDelta += offset;
 
1231   private final void increaseSplitDelta(int offset) {
 
1232     splitDelta += offset;
 
1236    * Returns true if a space has to be inserted after <code>operator</code>
 
1239   private boolean insertSpaceAfter(int token) {
 
1241       case TokenNameLPAREN :
 
1243       case TokenNameTWIDDLE :
 
1245       case 0 : // no token
 
1246       case TokenNameLBRACKET :
 
1247       case Scanner.TokenNameCOMMENT_LINE :
 
1255    * Returns true if a space has to be inserted before <code>operator</code>
 
1256    * false otherwise.<br>
 
1257    * Cannot be static as it uses the code formatter options
 
1258    * (to know if the compact assignment mode is on).
 
1260   private boolean insertSpaceBefore(int token) {
 
1262       case TokenNameEQUAL :
 
1263         return (!options.compactAssignmentMode);
 
1269   private static boolean isComment(int token) {
 
1271       token == Scanner.TokenNameCOMMENT_BLOCK
 
1272         || token == Scanner.TokenNameCOMMENT_LINE
 
1273         || token == Scanner.TokenNameCOMMENT_PHPDOC;
 
1277   private static boolean isLiteralToken(int token) {
 
1278     boolean result = token == TokenNameIntegerLiteral
 
1279       //                        || token == TokenNameLongLiteral
 
1280     //                  || token == TokenNameFloatingPointLiteral
 
1281   || token == TokenNameDoubleLiteral
 
1282       //                        || token == TokenNameCharacterLiteral
 
1283   || token == TokenNameStringLiteral;
 
1288    * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
 
1289    * it is split and the result is dumped in <code>formattedSource</code>
 
1290    * @param newLineCount the number of new lines to append
 
1292   private void newLine(int newLineCount) {
 
1294     // format current line
 
1296     beginningOfLineIndex = formattedSource.length();
 
1297     String currentLine = currentLineBuffer.toString();
 
1298     if (containsOpenCloseBraces) {
 
1299       containsOpenCloseBraces = false;
 
1303         indentationLevelForOpenCloseBraces,
 
1308       indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
 
1313         currentLineIndentationLevel,
 
1319     // dump line break(s)
 
1320     for (int i = 0; i < newLineCount; i++) {
 
1321       formattedSource.append(options.lineSeparatorSequence);
 
1322       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1324     // reset formatter for next line
 
1325     int currentLength = currentLine.length();
 
1328         currentLength > maxLineSize ? maxLineSize =
 
1329           currentLength : maxLineSize);
 
1331     increaseGlobalDelta(splitDelta);
 
1332     increaseGlobalDelta(lineDelta);
 
1334     currentLineIndentationLevel = initialIndentationLevel;
 
1337   private String operatorString(int operator) {
 
1339       case TokenNameextends :
 
1340         return "extends"; //$NON-NLS-1$
 
1342         //                      case TokenNameimplements :
 
1343         //                              return "implements"; //$NON-NLS-1$
 
1345         //                      case TokenNamethrows :
 
1346         //                              return "throws"; //$NON-NLS-1$
 
1348       case TokenNameSEMICOLON : // ;
 
1349         return ";"; //$NON-NLS-1$
 
1351       case TokenNameCOMMA : // ,
 
1352         return ","; //$NON-NLS-1$
 
1354       case TokenNameEQUAL : // =
 
1355         return "="; //$NON-NLS-1$
 
1357       case TokenNameAND_AND : // && (15.22)
 
1358         return "&&"; //$NON-NLS-1$
 
1360       case TokenNameOR_OR : // || (15.23)
 
1361         return "||"; //$NON-NLS-1$
 
1363       case TokenNameQUESTION : // ? (15.24)
 
1364         return "?"; //$NON-NLS-1$
 
1366       case TokenNameCOLON : // : (15.24)
 
1367         return ":"; //$NON-NLS-1$
 
1369       case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1370         return "=="; //$NON-NLS-1$
 
1372       case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
1373         return "!="; //$NON-NLS-1$
 
1375       case TokenNameLESS : // < (15.19.1)
 
1376         return "<"; //$NON-NLS-1$
 
1378       case TokenNameLESS_EQUAL : // <= (15.19.1)
 
1379         return "<="; //$NON-NLS-1$
 
1381       case TokenNameGREATER : // > (15.19.1)
 
1382         return ">"; //$NON-NLS-1$
 
1384       case TokenNameGREATER_EQUAL : // >= (15.19.1)
 
1385         return ">="; //$NON-NLS-1$
 
1387         //                      case TokenNameinstanceof : // instanceof
 
1388         //                              return "instanceof"; //$NON-NLS-1$
 
1390       case TokenNamePLUS : // + (15.17, 15.17.2)
 
1391         return "+"; //$NON-NLS-1$
 
1393       case TokenNameMINUS : // - (15.17.2)
 
1394         return "-"; //$NON-NLS-1$
 
1396       case TokenNameMULTIPLY : // * (15.16.1)
 
1397         return "*"; //$NON-NLS-1$
 
1399       case TokenNameDIVIDE : // / (15.16.2)
 
1400         return "/"; //$NON-NLS-1$
 
1402       case TokenNameREMAINDER : // % (15.16.3)
 
1403         return "%"; //$NON-NLS-1$
 
1405       case TokenNameLEFT_SHIFT : // << (15.18)
 
1406         return "<<"; //$NON-NLS-1$
 
1408       case TokenNameRIGHT_SHIFT : // >> (15.18)
 
1409         return ">>"; //$NON-NLS-1$
 
1411         //                      case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
1412         //                              return ">>>"; //$NON-NLS-1$
 
1414       case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
 
1415         return "&"; //$NON-NLS-1$
 
1417       case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
 
1418         return "|"; //$NON-NLS-1$
 
1420       case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
 
1421         return "^"; //$NON-NLS-1$
 
1423       case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
 
1424         return "*="; //$NON-NLS-1$
 
1426       case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
 
1427         return "/="; //$NON-NLS-1$
 
1429       case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
 
1430         return "%="; //$NON-NLS-1$
 
1432       case TokenNamePLUS_EQUAL : // += (15.25.2)
 
1433         return "+="; //$NON-NLS-1$
 
1435       case TokenNameMINUS_EQUAL : // -= (15.25.2)
 
1436         return "-="; //$NON-NLS-1$
 
1438       case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
 
1439         return "<<="; //$NON-NLS-1$
 
1441       case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
 
1442         return ">>="; //$NON-NLS-1$
 
1444         //                      case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
1445         //                              return ">>>="; //$NON-NLS-1$
 
1447       case TokenNameAND_EQUAL : // &= (15.25.2)
 
1448         return "&="; //$NON-NLS-1$
 
1450       case TokenNameXOR_EQUAL : // ^= (15.25.2)
 
1451         return "^="; //$NON-NLS-1$
 
1453       case TokenNameOR_EQUAL : // |= (15.25.2)
 
1454         return "|="; //$NON-NLS-1$
 
1456       case TokenNameDOT : // .
 
1457         return "."; //$NON-NLS-1$
 
1460         return ""; //$NON-NLS-1$
 
1465    * Appends <code>stringToOutput</code> to the formatted output.<br>
 
1466    * If it contains \n, append a LINE_SEPARATOR and indent after it.
 
1468   private void output(String stringToOutput) {
 
1469     char currentCharacter;
 
1470     for (int i = 0, max = stringToOutput.length(); i < max; i++) {
 
1471       currentCharacter = stringToOutput.charAt(i);
 
1472       if (currentCharacter != '\t') {
 
1473         currentLineBuffer.append(currentCharacter);
 
1479    * Appends <code>token</code> to the formatted output.<br>
 
1480    * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
 
1482   private void outputCurrentToken(int token) {
 
1483     char[] source = scanner.source;
 
1484     int startPosition = scanner.startPosition;
 
1487       case Scanner.TokenNameCOMMENT_PHPDOC :
 
1488       case Scanner.TokenNameCOMMENT_BLOCK :
 
1489       case Scanner.TokenNameCOMMENT_LINE :
 
1490         boolean endOfLine = false;
 
1491         int currentCommentOffset = getCurrentCommentOffset();
 
1492         int beginningOfLineSpaces = 0;
 
1494         currentCommentOffset = getCurrentCommentOffset();
 
1495         beginningOfLineSpaces = 0;
 
1496         boolean pendingCarriageReturn = false;
 
1497         for (int i = startPosition, max = scanner.currentPosition;
 
1500           char currentCharacter = source[i];
 
1501           updateMappedPositions(i);
 
1502           switch (currentCharacter) {
 
1504               pendingCarriageReturn = true;
 
1508               if (pendingCarriageReturn) {
 
1509                 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
 
1511                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1513               pendingCarriageReturn = false;
 
1514               currentLineBuffer.append(options.lineSeparatorSequence);
 
1515               beginningOfLineSpaces = 0;
 
1519               if (pendingCarriageReturn) {
 
1520                 pendingCarriageReturn = false;
 
1521                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1522                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1523                 beginningOfLineSpaces = 0;
 
1527                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
 
1528                 beginningOfLineSpaces += options.tabSize;
 
1529                 if (beginningOfLineSpaces > currentCommentOffset) {
 
1530                   currentLineBuffer.append(currentCharacter);
 
1532                   increaseGlobalDelta(-1);
 
1535                 currentLineBuffer.append(currentCharacter);
 
1539               if (pendingCarriageReturn) {
 
1540                 pendingCarriageReturn = false;
 
1541                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1542                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1543                 beginningOfLineSpaces = 0;
 
1547                 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
 
1548                 beginningOfLineSpaces++;
 
1549                 if (beginningOfLineSpaces > currentCommentOffset) {
 
1550                   currentLineBuffer.append(currentCharacter);
 
1552                   increaseGlobalDelta(-1);
 
1555                 currentLineBuffer.append(currentCharacter);
 
1559               if (pendingCarriageReturn) {
 
1560                 pendingCarriageReturn = false;
 
1561                 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
 
1562                 currentLineBuffer.append(options.lineSeparatorSequence);
 
1563                 beginningOfLineSpaces = 0;
 
1566                 beginningOfLineSpaces = 0;
 
1567                 currentLineBuffer.append(currentCharacter);
 
1572         updateMappedPositions(scanner.currentPosition - 1);
 
1573         multipleLineCommentCounter++;
 
1576         for (int i = startPosition, max = scanner.currentPosition;
 
1579           char currentCharacter = source[i];
 
1580           updateMappedPositions(i);
 
1581           currentLineBuffer.append(currentCharacter);
 
1587    * Outputs <code>currentString</code>:<br>
 
1588    * <ul><li>If its length is < maxLineLength, output
 
1589    * <li>Otherwise it is split.</ul>
 
1590    * @param currentString string to output
 
1591    * @param preIndented whether the string to output was pre-indented
 
1592    * @param depth number of indentation to put in front of <code>currentString</code>
 
1593    * @param operator value of the operator belonging to <code>currentString</code>.
 
1595   private void outputLine(
 
1596     String currentString,
 
1597     boolean preIndented,
 
1601     int[] startSubstringIndexes,
 
1602     int offsetInGlobalLine) {
 
1604     boolean emptyFirstSubString = false;
 
1605     String operatorString = operatorString(operator);
 
1606     boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
 
1607     boolean placeOperatorAhead = !placeOperatorBehind;
 
1609     // dump prefix operator?
 
1610     if (placeOperatorAhead) {
 
1615       if (operator != 0) {
 
1616         if (insertSpaceBefore(operator)) {
 
1617           formattedSource.append(' ');
 
1618           increaseSplitDelta(1);
 
1620         formattedSource.append(operatorString);
 
1621         increaseSplitDelta(operatorString.length());
 
1623         if (insertSpaceAfter(operator)
 
1624           //                    && operator != TokenNameimplements
 
1625           && operator != TokenNameextends) {
 
1626           //                    && operator != TokenNamethrows) {
 
1627           formattedSource.append(' ');
 
1628           increaseSplitDelta(1);
 
1632     SplitLine splitLine = null;
 
1633     if (options.maxLineLength == 0
 
1634       || getLength(currentString, depth) < options.maxLineLength
 
1635       || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
 
1637       // depending on the type of operator, outputs new line before of after dumping it
 
1638       // indent before postfix operator
 
1639       // indent also when the line cannot be split
 
1640       if (operator == TokenNameextends) {
 
1641         //                              || operator == TokenNameimplements
 
1642         //                              || operator == TokenNamethrows) {
 
1643         formattedSource.append(' ');
 
1644         increaseSplitDelta(1);
 
1646       if (placeOperatorBehind) {
 
1651       int max = currentString.length();
 
1652       if (multipleLineCommentCounter != 0) {
 
1654           BufferedReader reader =
 
1655             new BufferedReader(new StringReader(currentString));
 
1656           String line = reader.readLine();
 
1657           while (line != null) {
 
1658             updateMappedPositionsWhileSplitting(
 
1659               beginningOfLineIndex,
 
1660               beginningOfLineIndex
 
1662                 + options.lineSeparatorSequence.length);
 
1663             formattedSource.append(line);
 
1664             beginningOfLineIndex = beginningOfLineIndex + line.length();
 
1665             if ((line = reader.readLine()) != null) {
 
1666               formattedSource.append(options.lineSeparatorSequence);
 
1667               beginningOfLineIndex += options.lineSeparatorSequence.length;
 
1668               dumpTab(currentLineIndentationLevel);
 
1672         } catch (IOException e) {
 
1673           e.printStackTrace();
 
1676         updateMappedPositionsWhileSplitting(
 
1677           beginningOfLineIndex,
 
1678           beginningOfLineIndex + max);
 
1679         for (int i = 0; i < max; i++) {
 
1680           char currentChar = currentString.charAt(i);
 
1681           switch (currentChar) {
 
1686                 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
 
1687                 // a substring cannot end with a lineSeparatorSequence,
 
1688                 // except if it has been added by format() after a one-line comment
 
1689                 formattedSource.append(options.lineSeparatorSequence);
 
1691                 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
 
1696               formattedSource.append(currentChar);
 
1700       // update positions inside the mappedPositions table
 
1701       if (substringIndex != -1) {
 
1702         if (multipleLineCommentCounter == 0) {
 
1704             beginningOfLineIndex + startSubstringIndexes[substringIndex];
 
1705           updateMappedPositionsWhileSplitting(
 
1707             startPosition + max);
 
1710         // compute the splitDelta resulting with the operator and blank removal
 
1711         if (substringIndex + 1 != startSubstringIndexes.length) {
 
1713             startSubstringIndexes[substringIndex]
 
1715               - startSubstringIndexes[substringIndex
 
1719       // dump postfix operator?
 
1720       if (placeOperatorBehind) {
 
1721         if (insertSpaceBefore(operator)) {
 
1722           formattedSource.append(' ');
 
1723           if (operator != 0) {
 
1724             increaseSplitDelta(1);
 
1727         formattedSource.append(operatorString);
 
1728         if (operator != 0) {
 
1729           increaseSplitDelta(operatorString.length());
 
1734     // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
 
1735     // extends has to stand alone on a line when currentString has been split.
 
1736     if (options.maxLineLength != 0
 
1737       && splitLine != null
 
1738       && (operator == TokenNameextends)) {
 
1739       //                                || operator == TokenNameimplements
 
1740       //                                || operator == TokenNamethrows)) {
 
1741       formattedSource.append(options.lineSeparatorSequence);
 
1742       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1745       if (operator == TokenNameextends) {
 
1746         //                              || operator == TokenNameimplements
 
1747         //                              || operator == TokenNamethrows) {
 
1748         formattedSource.append(' ');
 
1749         increaseSplitDelta(1);
 
1752     // perform actual splitting
 
1753     String result[] = splitLine.substrings;
 
1754     int[] splitOperators = splitLine.operators;
 
1756     if (result[0].length() == 0) {
 
1757       // when the substring 0 is null, the substring 1 is correctly indented.
 
1759       emptyFirstSubString = true;
 
1761     // the operator going in front of the result[0] string is the operator parameter
 
1762     for (int i = 0, max = result.length; i < max; i++) {
 
1763       // the new depth is the current one if this is the first substring,
 
1764       // the current one + 1 otherwise.
 
1765       // if the substring is a comment, use the current indentation Level instead of the depth
 
1766       // (-1 because the ouputline increases depth).
 
1767       // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
 
1768       String currentResult = result[i];
 
1770       if (currentResult.length() != 0 || splitOperators[i] != 0) {
 
1771           int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
 
1772     || currentResult.startsWith("//")) //$NON-NLS-1$ 
 
1773   ? indentationLevel - 1 : depth;
 
1776           i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
 
1777           i == 0 ? newDepth : newDepth + 1,
 
1780           splitLine.startSubstringsIndexes,
 
1781           currentString.indexOf(currentResult));
 
1783           formattedSource.append(options.lineSeparatorSequence);
 
1784           increaseSplitDelta(options.lineSeparatorSequence.length);
 
1788     if (result.length == splitOperators.length - 1) {
 
1789       int lastOperator = splitOperators[result.length];
 
1790       String lastOperatorString = operatorString(lastOperator);
 
1791       formattedSource.append(options.lineSeparatorSequence);
 
1792       increaseSplitDelta(options.lineSeparatorSequence.length);
 
1794       if (breakLineBeforeOperator(lastOperator)) {
 
1796         if (lastOperator != 0) {
 
1797           if (insertSpaceBefore(lastOperator)) {
 
1798             formattedSource.append(' ');
 
1799             increaseSplitDelta(1);
 
1801           formattedSource.append(lastOperatorString);
 
1802           increaseSplitDelta(lastOperatorString.length());
 
1804           if (insertSpaceAfter(lastOperator)
 
1805             //                                  && lastOperator != TokenNameimplements
 
1806             && lastOperator != TokenNameextends) {
 
1807             //                                  && lastOperator != TokenNamethrows) {
 
1808             formattedSource.append(' ');
 
1809             increaseSplitDelta(1);
 
1814     if (placeOperatorBehind) {
 
1815       if (insertSpaceBefore(operator)) {
 
1816         formattedSource.append(' ');
 
1817         increaseSplitDelta(1);
 
1819       formattedSource.append(operatorString);
 
1820       //increaseSplitDelta(operatorString.length());
 
1825    * Pops the top statement of the stack if it is <code>token</code>
 
1827   private int pop(int token) {
 
1829     if ((constructionsCount > 0)
 
1830       && (constructions[constructionsCount - 1] == token)) {
 
1832       constructionsCount--;
 
1838    * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
 
1840   private int popBlock() {
 
1842     if ((constructionsCount > 0)
 
1843       && ((constructions[constructionsCount - 1] == BLOCK)
 
1844         || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
 
1845       if (constructions[constructionsCount - 1] == BLOCK)
 
1847       constructionsCount--;
 
1853    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1854    * Does not remove <code>token</code> from the stack.
 
1855    * @param token the token to be left as the top of the stack
 
1857   private int popExclusiveUntil(int token) {
 
1859     int startCount = constructionsCount;
 
1860     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1861       if (constructions[i] != NONINDENT_BLOCK)
 
1863       constructionsCount--;
 
1869    * Pops elements until the stack is empty or the top element is
 
1870    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1871    * Does not remove it from the stack.
 
1873   private int popExclusiveUntilBlock() {
 
1874     int startCount = constructionsCount;
 
1876     for (int i = startCount - 1;
 
1878         && constructions[i] != BLOCK
 
1879         && constructions[i] != NONINDENT_BLOCK;
 
1881       constructionsCount--;
 
1888    * Pops elements until the stack is empty or the top element is
 
1889    * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
 
1890    * Does not remove it from the stack.
 
1892   private int popExclusiveUntilBlockOrCase() {
 
1893     int startCount = constructionsCount;
 
1895     for (int i = startCount - 1;
 
1897         && constructions[i] != BLOCK
 
1898         && constructions[i] != NONINDENT_BLOCK
 
1899         && constructions[i] != TokenNamecase;
 
1901       constructionsCount--;
 
1908    * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
 
1909    * Removes <code>token</code> from the stack too.
 
1910    * @param token the token to remove from the stack
 
1912   private int popInclusiveUntil(int token) {
 
1913     int startCount = constructionsCount;
 
1915     for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
 
1916       if (constructions[i] != NONINDENT_BLOCK)
 
1918       constructionsCount--;
 
1920     if (constructionsCount > 0) {
 
1921       if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
 
1923       constructionsCount--;
 
1929    * Pops elements until the stack is empty or the top element is
 
1930    * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
 
1931    * Does not remove it from the stack.
 
1933   private int popInclusiveUntilBlock() {
 
1934     int startCount = constructionsCount;
 
1936     for (int i = startCount - 1;
 
1938         && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK);
 
1941       constructionsCount--;
 
1943     if (constructionsCount > 0) {
 
1944       if (constructions[constructionsCount - 1] == BLOCK)
 
1946       constructionsCount--;
 
1952    * Pushes a block in the stack.<br>
 
1953    * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
 
1954    * pushes <code>NONINDENT_BLOCK</code> otherwise.
 
1955    * Creates a new bigger array if the current one is full.
 
1957   private int pushBlock() {
 
1959     if (constructionsCount == constructions.length)
 
1963         (constructions = new int[constructionsCount * 2]),
 
1965         constructionsCount);
 
1967     if ((constructionsCount == 0)
 
1968       || (constructions[constructionsCount - 1] == BLOCK)
 
1969       || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
 
1970       || (constructions[constructionsCount - 1] == TokenNamecase)) {
 
1972       constructions[constructionsCount++] = BLOCK;
 
1974       constructions[constructionsCount++] = NONINDENT_BLOCK;
 
1980    * Pushes <code>token</code>.<br>
 
1981    * Creates a new bigger array if the current one is full.
 
1983   private int pushControlStatement(int token) {
 
1984     if (constructionsCount == constructions.length)
 
1988         (constructions = new int[constructionsCount * 2]),
 
1990         constructionsCount);
 
1991     constructions[constructionsCount++] = token;
 
1995   private static boolean separateFirstArgumentOn(int currentToken) {
 
1996     //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
 
1997     return currentToken != TokenNameif
 
1998       && currentToken != TokenNameLPAREN
 
1999       && currentToken != TokenNameNOT
 
2000       && currentToken != TokenNamewhile
 
2001       && currentToken != TokenNamefor
 
2002       && currentToken != TokenNameswitch;
 
2006    * Set the positions to map. The mapped positions should be retrieved using the
 
2007    * getMappedPositions() method.
 
2008    * @param positions int[]
 
2009    * @deprecated Set the positions to map using the format(String, int, int[]) method.
 
2011    * @see #getMappedPositions()
 
2013   public void setPositionsToMap(int[] positions) {
 
2014     positionsToMap = positions;
 
2017     mappedPositions = new int[positions.length];
 
2021    * Appends a space character to the current line buffer.
 
2023   private void space() {
 
2024     currentLineBuffer.append(' ');
 
2025     increaseLineDelta(1);
 
2029    * Splits <code>stringToSplit</code> on the top level token<br>
 
2030    * If there are several identical token at the same level,
 
2031    * the string is cut into many pieces.
 
2032    * @return an object containing the operator and all the substrings
 
2033    * or null if the string cannot be split
 
2035   public SplitLine split(String stringToSplit) {
 
2036     return split(stringToSplit, 0);
 
2040    * Splits <code>stringToSplit</code> on the top level token<br>
 
2041    * If there are several identical token at the same level,
 
2042    * the string is cut into many pieces.
 
2043    * @return an object containing the operator and all the substrings
 
2044    * or null if the string cannot be split
 
2046   public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
 
2048      * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
 
2049      * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387 
 
2051     if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
 
2055     int currentToken = 0;
 
2056     int splitTokenType = 0;
 
2057     int splitTokenDepth = Integer.MAX_VALUE;
 
2058     int splitTokenPriority = Integer.MAX_VALUE;
 
2060     int[] substringsStartPositions = new int[10];
 
2061     // contains the start position of substrings
 
2062     int[] substringsEndPositions = new int[10];
 
2063     // contains the start position of substrings
 
2064     int substringsCount = 1; // index in the substringsStartPosition array
 
2065     int[] splitOperators = new int[10];
 
2066     // contains the start position of substrings
 
2067     int splitOperatorsCount = 0; // index in the substringsStartPosition array
 
2068     int[] openParenthesisPosition = new int[10];
 
2069     int openParenthesisPositionCount = 0;
 
2071     int lastOpenParenthesisPosition = -1;
 
2072     // used to remember the position of the 1st open parenthesis
 
2073     // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
 
2074     // setup the scanner with a new source
 
2075     int lastCommentStartPosition = -1;
 
2076     // to remember the start position of the last comment
 
2077     int firstTokenOnLine = -1;
 
2078     // to remember the first token of the line
 
2079     int previousToken = -1;
 
2080     // to remember the previous token.
 
2081     splitScanner.setSource(stringToSplit.toCharArray());
 
2086         // takes the next token
 
2088           if (currentToken != Scanner.TokenNameWHITESPACE)
 
2089             previousToken = currentToken;
 
2090           currentToken = splitScanner.getNextToken();
 
2091         } catch (InvalidInputException e) {
 
2092           if (!handleInvalidToken(e))
 
2095           // this value is not modify when an exception is raised.
 
2097         if (currentToken == TokenNameEOF)
 
2100         if (firstTokenOnLine == -1) {
 
2101           firstTokenOnLine = currentToken;
 
2103         switch (currentToken) {
 
2104           case TokenNameRBRACE :
 
2105           case TokenNameRPAREN :
 
2106             if (openParenthesisPositionCount > 0) {
 
2107               if (openParenthesisPositionCount == 1
 
2108                 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
 
2109                 lastOpenParenthesisPosition = openParenthesisPosition[0];
 
2111                 (splitTokenDepth == Integer.MAX_VALUE)
 
2112                   || (splitTokenDepth > openParenthesisPositionCount
 
2113                     && openParenthesisPositionCount == 1)) {
 
2115                 splitTokenDepth = openParenthesisPositionCount;
 
2116                 splitTokenPriority = Integer.MAX_VALUE;
 
2117                 substringsStartPositions[0] = 0;
 
2118                 // better token means the whole line until now is the first substring
 
2119                 substringsCount = 1; // resets the count of substrings
 
2120                 substringsEndPositions[0] = openParenthesisPosition[0];
 
2121                 // substring ends on operator start
 
2122                 position = openParenthesisPosition[0];
 
2123                 // the string mustn't be cut before the closing parenthesis but after the opening one.
 
2124                 splitOperatorsCount = 1; // resets the count of split operators
 
2125                 splitOperators[0] = 0;
 
2127               openParenthesisPositionCount--;
 
2130           case TokenNameLBRACE :
 
2131           case TokenNameLPAREN :
 
2132             if (openParenthesisPositionCount
 
2133               == openParenthesisPosition.length) {
 
2135                 openParenthesisPosition,
 
2137                 (openParenthesisPosition =
 
2138                   new int[openParenthesisPositionCount * 2]),
 
2140                 openParenthesisPositionCount);
 
2142             openParenthesisPosition[openParenthesisPositionCount++] =
 
2143               splitScanner.currentPosition;
 
2144             if (currentToken == TokenNameLPAREN
 
2145               && previousToken == TokenNameRPAREN) {
 
2146               openParenthesisPosition[openParenthesisPositionCount - 1] =
 
2147                 splitScanner.startPosition;
 
2150           case TokenNameSEMICOLON : // ;
 
2151           case TokenNameCOMMA : // ,
 
2152           case TokenNameEQUAL : // =
 
2153             if (openParenthesisPositionCount < splitTokenDepth
 
2154               || (openParenthesisPositionCount == splitTokenDepth
 
2155                 && splitTokenPriority > getTokenPriority(currentToken))) {
 
2156               // the current token is better than the one we currently have
 
2157               // (in level or in priority if same level)
 
2158               // reset the substringsCount
 
2159               splitTokenDepth = openParenthesisPositionCount;
 
2160               splitTokenType = currentToken;
 
2161               splitTokenPriority = getTokenPriority(currentToken);
 
2162               substringsStartPositions[0] = 0;
 
2163               // better token means the whole line until now is the first substring
 
2165               if (separateFirstArgumentOn(firstTokenOnLine)
 
2166                 && openParenthesisPositionCount > 0) {
 
2167                 substringsCount = 2; // resets the count of substrings
 
2169                 substringsEndPositions[0] =
 
2170                   openParenthesisPosition[splitTokenDepth - 1];
 
2171                 substringsStartPositions[1] =
 
2172                   openParenthesisPosition[splitTokenDepth - 1];
 
2173                 substringsEndPositions[1] = splitScanner.startPosition;
 
2174                 splitOperatorsCount = 2; // resets the count of split operators
 
2175                 splitOperators[0] = 0;
 
2176                 splitOperators[1] = currentToken;
 
2177                 position = splitScanner.currentPosition;
 
2178                 // next substring will start from operator end
 
2180                 substringsCount = 1; // resets the count of substrings
 
2182                 substringsEndPositions[0] = splitScanner.startPosition;
 
2183                 // substring ends on operator start
 
2184                 position = splitScanner.currentPosition;
 
2185                 // next substring will start from operator end
 
2186                 splitOperatorsCount = 1; // resets the count of split operators
 
2187                 splitOperators[0] = currentToken;
 
2190               if ((openParenthesisPositionCount == splitTokenDepth
 
2191                 && splitTokenPriority == getTokenPriority(currentToken))
 
2192                 && splitTokenType != TokenNameEQUAL
 
2193                 && currentToken != TokenNameEQUAL) {
 
2194                 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
 
2195                 // take only the 1st = into account.
 
2196                 // if another token with the same priority is found,
 
2197                 // push the start position of the substring and
 
2198                 // push the token into the stack.
 
2199                 // create a new array object if the current one is full.
 
2200                 if (substringsCount == substringsStartPositions.length) {
 
2202                     substringsStartPositions,
 
2204                     (substringsStartPositions = new int[substringsCount * 2]),
 
2208                     substringsEndPositions,
 
2210                     (substringsEndPositions = new int[substringsCount * 2]),
 
2214                 if (splitOperatorsCount == splitOperators.length) {
 
2218                     (splitOperators = new int[splitOperatorsCount * 2]),
 
2220                     splitOperatorsCount);
 
2222                 substringsStartPositions[substringsCount] = position;
 
2223                 substringsEndPositions[substringsCount++] =
 
2224                   splitScanner.startPosition;
 
2225                 // substring ends on operator start
 
2226                 position = splitScanner.currentPosition;
 
2227                 // next substring will start from operator end
 
2228                 splitOperators[splitOperatorsCount++] = currentToken;
 
2233           case TokenNameCOLON : // : (15.24)
 
2234             // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
 
2235             // indeed it might appear also behind a case statement, and we do not to break at this point.
 
2236             if ((splitOperatorsCount == 0)
 
2237               || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
 
2240           case TokenNameextends :
 
2241             //                  case TokenNameimplements :
 
2242             //                  case TokenNamethrows :
 
2244           case TokenNameDOT : // .
 
2245           case TokenNameMULTIPLY : // * (15.16.1)
 
2246           case TokenNameDIVIDE : // / (15.16.2)
 
2247           case TokenNameREMAINDER : // % (15.16.3)
 
2248           case TokenNamePLUS : // + (15.17, 15.17.2)
 
2249           case TokenNameMINUS : // - (15.17.2)
 
2250           case TokenNameLEFT_SHIFT : // << (15.18)
 
2251           case TokenNameRIGHT_SHIFT : // >> (15.18)
 
2252             //                          case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
 
2253           case TokenNameLESS : // < (15.19.1)
 
2254           case TokenNameLESS_EQUAL : // <= (15.19.1)
 
2255           case TokenNameGREATER : // > (15.19.1)
 
2256           case TokenNameGREATER_EQUAL : // >= (15.19.1)
 
2257             //                          case TokenNameinstanceof : // instanceof
 
2258           case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2259           case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
 
2260           case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
 
2261           case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
 
2262           case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
 
2263           case TokenNameAND_AND : // && (15.22)
 
2264           case TokenNameOR_OR : // || (15.23)
 
2265           case TokenNameQUESTION : // ? (15.24)
 
2266           case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
 
2267           case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
 
2268           case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
 
2269           case TokenNamePLUS_EQUAL : // += (15.25.2)
 
2270           case TokenNameMINUS_EQUAL : // -= (15.25.2)
 
2271           case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
 
2272           case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
 
2273             //                                  case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
 
2274           case TokenNameAND_EQUAL : // &= (15.25.2)
 
2275           case TokenNameXOR_EQUAL : // ^= (15.25.2)
 
2276           case TokenNameOR_EQUAL : // |= (15.25.2)
 
2278             if ((openParenthesisPositionCount < splitTokenDepth
 
2279               || (openParenthesisPositionCount == splitTokenDepth
 
2280                 && splitTokenPriority > getTokenPriority(currentToken)))
 
2281               && !((currentToken == TokenNamePLUS
 
2282                 || currentToken == TokenNameMINUS)
 
2283                 && (previousToken == TokenNameLBRACE
 
2284                   || previousToken == TokenNameLBRACKET
 
2285                   || splitScanner.startPosition == 0))) {
 
2286               // the current token is better than the one we currently have
 
2287               // (in level or in priority if same level)
 
2288               // reset the substringsCount
 
2289               splitTokenDepth = openParenthesisPositionCount;
 
2290               splitTokenType = currentToken;
 
2291               splitTokenPriority = getTokenPriority(currentToken);
 
2292               substringsStartPositions[0] = 0;
 
2293               // better token means the whole line until now is the first substring
 
2295               if (separateFirstArgumentOn(firstTokenOnLine)
 
2296                 && openParenthesisPositionCount > 0) {
 
2297                 substringsCount = 2; // resets the count of substrings
 
2299                 substringsEndPositions[0] =
 
2300                   openParenthesisPosition[splitTokenDepth - 1];
 
2301                 substringsStartPositions[1] =
 
2302                   openParenthesisPosition[splitTokenDepth - 1];
 
2303                 substringsEndPositions[1] = splitScanner.startPosition;
 
2304                 splitOperatorsCount = 3; // resets the count of split operators
 
2305                 splitOperators[0] = 0;
 
2306                 splitOperators[1] = 0;
 
2307                 splitOperators[2] = currentToken;
 
2308                 position = splitScanner.currentPosition;
 
2309                 // next substring will start from operator end
 
2311                 substringsCount = 1; // resets the count of substrings
 
2313                 substringsEndPositions[0] = splitScanner.startPosition;
 
2314                 // substring ends on operator start
 
2315                 position = splitScanner.currentPosition;
 
2316                 // next substring will start from operator end
 
2317                 splitOperatorsCount = 2; // resets the count of split operators
 
2318                 splitOperators[0] = 0;
 
2319                 // nothing for first operand since operator will be inserted in front of the second operand
 
2320                 splitOperators[1] = currentToken;
 
2324               if (openParenthesisPositionCount == splitTokenDepth
 
2325                 && splitTokenPriority == getTokenPriority(currentToken)) {
 
2326                 // if another token with the same priority is found,
 
2327                 // push the start position of the substring and
 
2328                 // push the token into the stack.
 
2329                 // create a new array object if the current one is full.
 
2330                 if (substringsCount == substringsStartPositions.length) {
 
2332                     substringsStartPositions,
 
2334                     (substringsStartPositions = new int[substringsCount * 2]),
 
2338                     substringsEndPositions,
 
2340                     (substringsEndPositions = new int[substringsCount * 2]),
 
2344                 if (splitOperatorsCount == splitOperators.length) {
 
2348                     (splitOperators = new int[splitOperatorsCount * 2]),
 
2350                     splitOperatorsCount);
 
2352                 substringsStartPositions[substringsCount] = position;
 
2353                 substringsEndPositions[substringsCount++] =
 
2354                   splitScanner.startPosition;
 
2355                 // substring ends on operator start
 
2356                 position = splitScanner.currentPosition;
 
2357                 // next substring will start from operator end
 
2358                 splitOperators[splitOperatorsCount++] = currentToken;
 
2364         if (isComment(currentToken)) {
 
2365           lastCommentStartPosition = splitScanner.startPosition;
 
2367           lastCommentStartPosition = -1;
 
2370     } catch (InvalidInputException e) {
 
2373     // if the string cannot be split, return null.
 
2374     if (splitOperatorsCount == 0)
 
2377     // ## SPECIAL CASES BEGIN
 
2378     if (((splitOperatorsCount == 2
 
2379       && splitOperators[1] == TokenNameDOT
 
2380       && splitTokenDepth == 0
 
2381       && lastOpenParenthesisPosition > -1)
 
2382       || (splitOperatorsCount > 2
 
2383         && splitOperators[1] == TokenNameDOT
 
2384         && splitTokenDepth == 0
 
2385         && lastOpenParenthesisPosition > -1
 
2386         && lastOpenParenthesisPosition <= options.maxLineLength)
 
2387       || (separateFirstArgumentOn(firstTokenOnLine)
 
2388         && splitTokenDepth > 0
 
2389         && lastOpenParenthesisPosition > -1))
 
2390       && (lastOpenParenthesisPosition < splitScanner.source.length
 
2391         && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
 
2392       // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
 
2393       // only one split on a top level .
 
2394       // or more than one split on . and substring before open parenthesis fits one line.
 
2395       // or split inside parenthesis and first token is not a for/while/if
 
2398           stringToSplit.substring(lastOpenParenthesisPosition),
 
2399           lastOpenParenthesisPosition);
 
2400       if (sl == null || sl.operators[0] != TokenNameCOMMA) {
 
2401         // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
 
2402         return new SplitLine(
 
2405             stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
 
2406             stringToSplit.substring(lastOpenParenthesisPosition)},
 
2409             lastOpenParenthesisPosition + offsetInGlobalLine });
 
2411         // right substring can be split and is split on comma
 
2412         // copy substrings and operators
 
2413         // except if the 1st string is empty.
 
2414         int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
 
2415         int subStringsLength = sl.substrings.length + 1 - startIndex;
 
2416         String[] result = new String[subStringsLength];
 
2417         int[] startIndexes = new int[subStringsLength];
 
2418         int operatorsLength = sl.operators.length + 1 - startIndex;
 
2419         int[] operators = new int[operatorsLength];
 
2421         result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
 
2425           sl.startSubstringsIndexes,
 
2429           subStringsLength - 1);
 
2430         for (int i = subStringsLength - 1; i >= 0; i--) {
 
2431           startIndexes[i] += offsetInGlobalLine;
 
2438           subStringsLength - 1);
 
2444           operatorsLength - 1);
 
2446         return new SplitLine(operators, result, startIndexes);
 
2449     // if the last token is a comment and the substring before the comment fits on a line,
 
2450     // split before the comment and return the result.
 
2451     if (lastCommentStartPosition > -1
 
2452       && lastCommentStartPosition < options.maxLineLength
 
2453       && splitTokenPriority > 50) {
 
2454       int end = lastCommentStartPosition;
 
2455       int start = lastCommentStartPosition;
 
2456       if (stringToSplit.charAt(end - 1) == ' ') {
 
2459       if (start != end && stringToSplit.charAt(start) == ' ') {
 
2462       return new SplitLine(
 
2465           stringToSplit.substring(0, end),
 
2466           stringToSplit.substring(start)},
 
2467         new int[] { 0, start });
 
2469     if (position != stringToSplit.length()) {
 
2470       if (substringsCount == substringsStartPositions.length) {
 
2472           substringsStartPositions,
 
2474           (substringsStartPositions = new int[substringsCount * 2]),
 
2478           substringsEndPositions,
 
2480           (substringsEndPositions = new int[substringsCount * 2]),
 
2484       // avoid empty extra substring, e.g. line terminated with a semi-colon
 
2485       substringsStartPositions[substringsCount] = position;
 
2486       substringsEndPositions[substringsCount++] = stringToSplit.length();
 
2488     if (splitOperatorsCount == splitOperators.length) {
 
2492         (splitOperators = new int[splitOperatorsCount * 2]),
 
2494         splitOperatorsCount);
 
2496     splitOperators[splitOperatorsCount] = 0;
 
2498     // the last element of the stack is the position of the end of StringToSPlit
 
2499     // +1 because the substring method excludes the last character
 
2500     String[] result = new String[substringsCount];
 
2501     for (int i = 0; i < substringsCount; i++) {
 
2502       int start = substringsStartPositions[i];
 
2503       int end = substringsEndPositions[i];
 
2504       if (stringToSplit.charAt(start) == ' ') {
 
2506         substringsStartPositions[i]++;
 
2508       if (end != start && stringToSplit.charAt(end - 1) == ' ') {
 
2511       result[i] = stringToSplit.substring(start, end);
 
2512       substringsStartPositions[i] += offsetInGlobalLine;
 
2514     if (splitOperatorsCount > substringsCount) {
 
2516         substringsStartPositions,
 
2518         (substringsStartPositions = new int[splitOperatorsCount]),
 
2522         substringsEndPositions,
 
2524         (substringsEndPositions = new int[splitOperatorsCount]),
 
2527       for (int i = substringsCount; i < splitOperatorsCount; i++) {
 
2528         substringsStartPositions[i] = position;
 
2529         substringsEndPositions[i] = position;
 
2534         (splitOperators = new int[splitOperatorsCount]),
 
2536         splitOperatorsCount);
 
2539         substringsStartPositions,
 
2541         (substringsStartPositions = new int[substringsCount]),
 
2545         substringsEndPositions,
 
2547         (substringsEndPositions = new int[substringsCount]),
 
2553         (splitOperators = new int[substringsCount]),
 
2557     SplitLine splitLine =
 
2558       new SplitLine(splitOperators, result, substringsStartPositions);
 
2562   private void updateMappedPositions(int startPosition) {
 
2563     if (positionsToMap == null) {
 
2566     char[] source = scanner.source;
 
2567     int sourceLength = source.length;
 
2568     while (indexToMap < positionsToMap.length
 
2569       && positionsToMap[indexToMap] <= startPosition) {
 
2570       int posToMap = positionsToMap[indexToMap];
 
2572         || posToMap >= sourceLength) {
 
2573         // protection against out of bounds position
 
2574         if (posToMap == sourceLength) {
 
2575           mappedPositions[indexToMap] = formattedSource.length();
 
2577         indexToMap = positionsToMap.length; // no more mapping
 
2580       if (CharOperation.isWhitespace(source[posToMap])) {
 
2581         mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2583         if (posToMap == sourceLength - 1) {
 
2584           mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
 
2586           mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
 
2593   private void updateMappedPositionsWhileSplitting(
 
2596     if (mappedPositions == null || mappedPositions.length == indexInMap)
 
2599     while (indexInMap < mappedPositions.length
 
2600       && startPosition <= mappedPositions[indexInMap]
 
2601       && mappedPositions[indexInMap] < endPosition
 
2602       && indexInMap < indexToMap) {
 
2603       mappedPositions[indexInMap] += splitDelta;
 
2608   private int getLength(String s, int tabDepth) {
 
2610     for (int i = 0; i < tabDepth; i++) {
 
2611       length += options.tabSize;
 
2613     for (int i = 0, max = s.length(); i < max; i++) {
 
2614       char currentChar = s.charAt(i);
 
2615       switch (currentChar) {
 
2617           length += options.tabSize;
 
2627   * Sets the initial indentation level
 
2628   * @param indentationLevel new indentation level
 
2632   public void setInitialIndentationLevel(int newIndentationLevel) {
 
2633     this.initialIndentationLevel =
 
2634       currentLineIndentationLevel = indentationLevel = newIndentationLevel;