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