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