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