1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.formatter;
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.StringReader;
15 import java.util.Hashtable;
16 import java.util.Locale;
18 import net.sourceforge.phpdt.core.ICodeFormatter;
19 import net.sourceforge.phpdt.core.compiler.CharOperation;
20 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
21 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
22 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
23 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
24 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
25 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
27 * <h2>How to format a piece of code ?</h2>
29 * <li>Create an instance of <code>CodeFormatter</code>
30 * <li>Use the method <code>void format(aString)</code> on this instance to
31 * format <code>aString</code>. It will return the formatted string.
34 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
35 public FormatterOptions options;
37 * Represents a block in the <code>constructions</code> stack.
39 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
41 * Represents a block following a control statement in the <code>constructions</code>
44 public static final int NONINDENT_BLOCK = -100;
46 * Contains the formatted output.
48 StringBuffer formattedSource;
50 * Contains the current line. <br>
51 * Will be dumped at the next "newline"
53 StringBuffer currentLineBuffer;
55 * Used during the formatting to get each token.
59 * Contains the tokens responsible for the current indentation level and the
60 * blocks not closed yet.
62 private int[] constructions;
64 * Index in the <code>constructions</code> array.
66 private int constructionsCount;
68 * Level of indentation of the current token (number of tab char put in front
71 private int indentationLevel;
73 * Regular level of indentation of all the lines
75 private int initialIndentationLevel;
77 * Used to split a line.
81 * To remember the offset between the beginning of the line and the beginning
84 int currentCommentOffset;
85 int currentLineIndentationLevel;
87 private boolean containsOpenCloseBraces;
88 private int indentationLevelForOpenCloseBraces;
90 * Collections of positions to map
92 private int[] positionsToMap;
94 * Collections of mapped positions
96 private int[] mappedPositions;
97 private int indexToMap;
98 private int indexInMap;
99 private int globalDelta;
100 private int lineDelta;
101 private int splitDelta;
102 private int beginningOfLineIndex;
103 private int multipleLineCommentCounter;
105 * Creates a new instance of Code Formatter using the given settings.
107 * @deprecated backport 1.0 internal functionality
109 public CodeFormatter(ConfigurableOption[] settings) {
110 this(convertConfigurableOptions(settings));
113 * Creates a new instance of Code Formatter using the FormattingOptions
114 * object given as argument
116 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
118 public CodeFormatter() {
122 * Creates a new instance of Code Formatter using the given settings.
124 public CodeFormatter(Map settings) {
125 // initialize internal state
126 constructionsCount = 0;
127 constructions = new int[10];
128 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
129 currentCommentOffset = -1;
130 // initialize primary and secondary scanners
131 scanner = new Scanner(true /* comment */
132 , true /* whitespace */
135 , true /* tokenizeStrings */
136 ); // regular scanner for forming lines
137 scanner.recordLineSeparator = true;
138 // to remind of the position of the beginning of the line.
139 splitScanner = new Scanner(true /* comment */
140 , true /* whitespace */
143 , true /* tokenizeStrings */
145 // secondary scanner to split long lines formed by primary scanning
146 // initialize current line buffer
147 currentLineBuffer = new StringBuffer();
148 this.options = new FormatterOptions(settings);
151 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
154 private static boolean breakLineBeforeOperator(int operator) {
156 case TokenNameCOMMA :
157 case TokenNameSEMICOLON :
158 case TokenNameEQUAL :
165 * @deprecated backport 1.0 internal functionality
167 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
168 Hashtable options = new Hashtable(10);
169 for (int i = 0; i < settings.length; i++) {
170 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
171 String optionName = settings[i].getOptionName();
172 int valueIndex = settings[i].getCurrentValueIndex();
173 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
175 "net.sourceforge.phpdt.core.formatter.newline.openingBrace",
176 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
177 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
179 "net.sourceforge.phpdt.core.formatter.newline.controlStatement",
180 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
181 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
182 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll",
183 valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
184 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
185 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf",
186 valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
187 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
189 "net.sourceforge.phpdt.core.formatter.newline.emptyBlock",
190 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
191 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
192 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String
193 .valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
194 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
195 options.put("net.sourceforge.phpdt.core.formatter.style.assignment",
196 valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
197 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
198 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char",
199 valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
200 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
201 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size",
202 String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
209 * Returns the end of the source code.
211 private final String copyRemainingSource() {
212 char str[] = scanner.source;
213 int startPosition = scanner.startPosition;
214 int length = str.length - startPosition;
215 StringBuffer bufr = new StringBuffer(length);
216 if (startPosition < str.length) {
217 bufr.append(str, startPosition, length);
219 return (bufr.toString());
222 * Inserts <code>tabCount</code> tab character or their equivalent number
225 private void dumpTab(int tabCount) {
226 if (options.indentWithTab) {
227 for (int j = 0; j < tabCount; j++) {
228 formattedSource.append('\t');
229 increaseSplitDelta(1);
232 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
233 formattedSource.append(' ');
234 increaseSplitDelta(1);
239 * Dumps <code>currentLineBuffer</code> into the formatted string.
241 private void flushBuffer() {
242 String currentString = currentLineBuffer.toString();
244 beginningOfLineIndex = formattedSource.length();
245 if (containsOpenCloseBraces) {
246 containsOpenCloseBraces = false;
247 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0,
249 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
251 outputLine(currentString, false, currentLineIndentationLevel, 0, -1,
254 int scannerSourceLength = scanner.source.length;
255 if (scannerSourceLength > 2) {
256 if (scanner.source[scannerSourceLength - 1] == '\n'
257 && scanner.source[scannerSourceLength - 2] == '\r') {
258 formattedSource.append(options.lineSeparatorSequence);
259 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
260 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
261 formattedSource.append(options.lineSeparatorSequence);
262 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
263 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
264 formattedSource.append(options.lineSeparatorSequence);
265 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
268 updateMappedPositions(scanner.startPosition);
271 * Formats the input string.
273 private void format() {
275 int previousToken = 0;
276 int previousCompilableToken = 0;
277 int indentationOffset = 0;
278 int newLinesInWhitespace = 0;
279 // number of new lines in the previous whitespace token
280 // (used to leave blank lines before comments)
281 int pendingNewLines = 0;
282 boolean expectingOpenBrace = false;
283 boolean clearNonBlockIndents = false;
284 // true if all indentations till the 1st { (usefull after } or ;)
285 boolean pendingSpace = true;
286 boolean pendingNewlineAfterParen = false;
287 // true when a cr is to be put after a ) (in conditional statements)
288 boolean inAssignment = false;
289 boolean inArrayAssignment = false;
290 boolean inThrowsClause = false;
291 boolean inClassOrInterfaceHeader = false;
292 int dollarBraceCount = 0;
293 // openBracketCount is used to count the number of open brackets not closed
295 int openBracketCount = 0;
296 int unarySignModifier = 0;
297 // openParenthesis[0] is used to count the parenthesis not belonging to a
299 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
300 int openParenthesisCount = 1;
301 int[] openParenthesis = new int[10];
302 // tokenBeforeColon is used to know what token goes along with the current
304 // it can be case or ?
305 int tokenBeforeColonCount = 0;
306 int[] tokenBeforeColon = new int[10];
307 constructionsCount = 0; // initializes the constructions count.
308 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
310 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
311 boolean specialElse = false;
312 // OPTION (IndentationLevel): initial indentation level may be non-zero.
313 currentLineIndentationLevel += constructionsCount;
314 // An InvalidInputException exception might cause the termination of this
318 // Get the next token. Catch invalid input and output it
319 // with minimal formatting, also catch end of input and
322 token = scanner.getNextToken();
324 int currentEndPosition = scanner.getCurrentTokenEndPosition();
325 int currentStartPosition = scanner.getCurrentTokenStartPosition();
326 System.out.print(currentStartPosition + "," + currentEndPosition
328 System.out.println(scanner.toStringAction(token));
330 // Patch for line comment
331 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
332 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
333 int length = scanner.currentPosition;
334 loop : for (int index = length - 1; index >= 0; index--) {
335 switch (scanner.source[index]) {
338 scanner.currentPosition--;
345 } catch (InvalidInputException e) {
346 if (!handleInvalidToken(e)) {
351 if (token == Scanner.TokenNameEOF)
354 * ## MODIFYING the indentation level before generating new lines and
355 * indentation in the output string
357 // Removes all the indentations made by statements not followed by a
359 // except if the current token is ELSE, CATCH or if we are in a
361 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
364 if (constructionsCount > 0
365 && constructions[constructionsCount - 1] == TokenNameelse) {
369 indentationLevel += popInclusiveUntil(TokenNameif);
371 // case TokenNamecatch :
372 // indentationLevel += popInclusiveUntil(TokenNamecatch);
374 // case TokenNamefinally :
375 // indentationLevel += popInclusiveUntil(TokenNamecatch);
377 case TokenNamewhile :
378 if (nlicsToken == TokenNamedo) {
379 indentationLevel += pop(TokenNamedo);
383 indentationLevel += popExclusiveUntilBlockOrCase();
384 // clear until a CASE, DEFAULT or BLOCK is encountered.
385 // Thus, the indentationLevel is correctly cleared either
386 // in a switch/case statement or in any other situation.
388 clearNonBlockIndents = false;
390 // returns to the indentation level created by the SWITCH keyword
391 // if the current token is a CASE or a DEFAULT
392 if (token == TokenNamecase || token == TokenNamedefault) {
393 indentationLevel += pop(TokenNamecase);
395 // if (token == Scanner.TokenNamethrows) {
396 // inThrowsClause = true;
398 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface)
399 && previousToken != Scanner.TokenNameDOT) {
400 inClassOrInterfaceHeader = true;
403 * ## APPEND newlines and indentations to the output string
405 // Do not add a new line between ELSE and IF, if the option
406 // elseIfOnSameLine is true.
407 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
408 // if (pendingNewlineAfterParen
409 // && previousCompilableToken == TokenNameelse
410 // && token == TokenNameif
411 // && options.compactElseIfMode) {
412 // pendingNewlineAfterParen = false;
413 // pendingNewLines = 0;
414 // indentationLevel += pop(TokenNameelse);
415 // // because else if is now one single statement,
416 // // the indentation level after it is increased by one and not by 2
417 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
419 // Add a newline & indent to the formatted source string if
420 // a for/if-else/while statement was scanned and there is no block
422 pendingNewlineAfterParen = pendingNewlineAfterParen
423 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
424 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
425 pendingNewlineAfterParen = false;
426 // Do to add a newline & indent sequence if the current token is an
427 // open brace or a period or if the current token is a semi-colon and
429 // previous token is a close paren.
430 // add a new line if a parenthesis belonging to a for() statement
431 // has been closed and the current token is not an opening brace
432 if (token != TokenNameLBRACE
434 // to avoid adding new line between else and a comment
435 && token != TokenNameDOT
436 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
438 currentLineIndentationLevel = indentationLevel;
440 pendingSpace = false;
442 if (token == TokenNameLBRACE
443 && options.newLineBeforeOpeningBraceMode) {
445 if (constructionsCount > 0
446 && constructions[constructionsCount - 1] != BLOCK
447 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
448 currentLineIndentationLevel = indentationLevel - 1;
450 currentLineIndentationLevel = indentationLevel;
453 pendingSpace = false;
457 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode
458 && constructionsCount > 0
459 && constructions[constructionsCount - 1] == TokenNamedo) {
461 currentLineIndentationLevel = indentationLevel - 1;
463 pendingSpace = false;
466 if (token == TokenNameLBRACE && inThrowsClause) {
467 inThrowsClause = false;
468 if (options.newLineBeforeOpeningBraceMode) {
470 currentLineIndentationLevel = indentationLevel;
472 pendingSpace = false;
476 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
477 inClassOrInterfaceHeader = false;
478 if (options.newLineBeforeOpeningBraceMode) {
480 currentLineIndentationLevel = indentationLevel;
482 pendingSpace = false;
485 // Add pending new lines to the formatted source string.
486 // Note: pending new lines are not added if the current token
487 // is a single line comment or whitespace.
488 // if the comment is between parenthesis, there is no blank line
490 // (if it's a one-line comment, a blank line is added after it).
491 if (((pendingNewLines > 0 && (!isComment(token)))
492 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
493 && token != Scanner.TokenNameWHITESPACE) {
494 // Do not add newline & indent between an adjoining close brace and
495 // close paren. Anonymous inner classes may use this form.
496 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
497 && token == TokenNameRPAREN;
498 // OPTION (NewLineInCompoundStatement): do not add newline & indent
499 // between close brace and else, (do) while, catch, and finally if
500 // newlineInCompoundStatement is true.
501 boolean nlicsOption = previousToken == TokenNameRBRACE
502 && !options.newlineInControlStatementMode
503 && (token == TokenNameelse
504 || (token == TokenNamewhile && nlicsToken == TokenNamedo)
505 || token == TokenNamecatch || token == TokenNamefinally);
506 // Do not add a newline & indent between a close brace and
508 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
509 && token == TokenNameSEMICOLON;
510 // Do not add a new line & indent between a multiline comment and a
512 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
513 && token == TokenNameLBRACE;
514 // Do not add a newline & indent between a close brace and a colon
515 // (in array assignments, for example).
516 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
517 && token == TokenNameCOMMA;
518 // Add a newline and indent, if appropriate.
520 || (!commentAndOpenBrace && !closeBraceAndCloseParen
521 && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
522 // if clearAllBlankLinesMode=false, leaves the blank lines
523 // inserted by the user
524 // if clearAllBlankLinesMode=true, removes all of then
525 // and insert only blank lines required by the formatting.
526 if (!options.clearAllBlankLinesMode) {
527 // (isComment(token))
528 pendingNewLines = (pendingNewLines < newLinesInWhitespace)
529 ? newLinesInWhitespace
531 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
533 if (previousCompilableToken == TokenNameLBRACE
534 && token == TokenNameRBRACE) {
535 containsOpenCloseBraces = true;
536 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
537 if (isComment(previousToken)) {
538 newLine(pendingNewLines);
541 * if (!(constructionsCount > 1 &&
542 * constructions[constructionsCount-1] == NONINDENT_BLOCK &&
543 * (constructions[constructionsCount-2] == TokenNamefor
545 if (options.newLineInEmptyBlockMode) {
546 if (inArrayAssignment) {
547 newLine(1); // array assigment with an empty block
549 newLine(pendingNewLines);
555 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
557 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
558 newLine(pendingNewLines);
561 if (((previousCompilableToken == TokenNameSEMICOLON)
562 || (previousCompilableToken == TokenNameLBRACE)
563 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
564 && (token == TokenNameRBRACE)) {
565 indentationOffset = -1;
566 indentationLevel += popExclusiveUntilBlock();
568 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
570 currentLineIndentationLevel++;
572 currentLineIndentationLevel = indentationLevel
575 pendingSpace = false;
576 indentationOffset = 0;
579 newLinesInWhitespace = 0;
581 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
585 boolean phpTagAndWhitespace =
586 previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
588 // case TokenNameDOLLAR :
589 // dollarBraceCount++;
592 // case TokenNamefinally :
593 expectingOpenBrace = true;
594 pendingNewlineAfterParen = true;
595 indentationLevel += pushControlStatement(token);
598 case TokenNamedefault :
599 if (tokenBeforeColonCount == tokenBeforeColon.length) {
600 System.arraycopy(tokenBeforeColon, 0,
601 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
602 tokenBeforeColonCount);
604 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
605 indentationLevel += pushControlStatement(TokenNamecase);
607 case TokenNameQUESTION :
608 if (tokenBeforeColonCount == tokenBeforeColon.length) {
609 System.arraycopy(tokenBeforeColon, 0,
610 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
611 tokenBeforeColonCount);
613 tokenBeforeColon[tokenBeforeColonCount++] = token;
615 case TokenNameswitch :
618 case TokenNamewhile :
619 if (openParenthesisCount == openParenthesis.length) {
620 System.arraycopy(openParenthesis, 0,
621 (openParenthesis = new int[openParenthesisCount * 2]), 0,
622 openParenthesisCount);
624 openParenthesis[openParenthesisCount++] = 0;
625 expectingOpenBrace = true;
626 indentationLevel += pushControlStatement(token);
629 pendingNewlineAfterParen = true;
630 case TokenNamecatch :
631 // several CATCH statements can be contiguous.
632 // a CATCH is encountered pop until first CATCH (if a CATCH
633 // follows a TRY it works the same way,
634 // as CATCH and TRY are the same token in the stack).
635 expectingOpenBrace = true;
636 indentationLevel += pushControlStatement(TokenNamecatch);
639 expectingOpenBrace = true;
640 indentationLevel += pushControlStatement(token);
645 case TokenNameLPAREN :
646 // if (previousToken == TokenNamesynchronized) {
647 // indentationLevel += pushControlStatement(previousToken);
649 // Put a space between the previous and current token if the
650 // previous token was not a keyword, open paren, logical
651 // compliment (eg: !), semi-colon, open brace, close brace,
653 if (previousCompilableToken != TokenNameLBRACKET
654 && previousToken != TokenNameIdentifier && previousToken != 0
655 && previousToken != TokenNameNOT
656 && previousToken != TokenNameLPAREN
657 && previousToken != TokenNameTWIDDLE
658 && previousToken != TokenNameSEMICOLON
659 && previousToken != TokenNameLBRACE
660 && previousToken != TokenNameRBRACE
661 && previousToken != TokenNamesuper) {
662 // && previousToken != TokenNamethis) {
665 // If in a for/if/while statement, increase the parenthesis count
666 // for the current openParenthesisCount
667 // else increase the count for stand alone parenthesis.
668 if (openParenthesisCount > 0)
669 openParenthesis[openParenthesisCount - 1]++;
671 openParenthesis[0]++;
672 pendingSpace = false;
675 case TokenNameRPAREN :
676 // Decrease the parenthesis count
677 // if there is no more unclosed parenthesis,
678 // a new line and indent may be append (depending on the next
680 if ((openParenthesisCount > 1)
681 && (openParenthesis[openParenthesisCount - 1] > 0)) {
682 openParenthesis[openParenthesisCount - 1]--;
683 if (openParenthesis[openParenthesisCount - 1] <= 0) {
684 pendingNewlineAfterParen = true;
685 inAssignment = false;
686 openParenthesisCount--;
689 openParenthesis[0]--;
691 pendingSpace = false;
693 case TokenNameLBRACE :
694 if (previousCompilableToken == TokenNameDOLLAR) {
697 if ((previousCompilableToken == TokenNameRBRACKET)
698 || (previousCompilableToken == TokenNameEQUAL)) {
699 // if (previousCompilableToken == TokenNameRBRACKET) {
700 inArrayAssignment = true;
701 inAssignment = false;
703 if (inArrayAssignment) {
704 indentationLevel += pushBlock();
706 // Add new line and increase indentation level after open brace.
708 indentationLevel += pushBlock();
712 case TokenNameRBRACE :
713 if (dollarBraceCount > 0) {
717 if (previousCompilableToken == TokenNameRPAREN) {
718 pendingSpace = false;
720 if (inArrayAssignment) {
721 inArrayAssignment = false;
723 indentationLevel += popInclusiveUntilBlock();
726 indentationLevel += popInclusiveUntilBlock();
727 if (previousCompilableToken == TokenNameRPAREN) {
728 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
730 currentLineBuffer.append(options.lineSeparatorSequence);
731 increaseLineDelta(options.lineSeparatorSequence.length);
733 if (constructionsCount > 0) {
734 switch (constructions[constructionsCount - 1]) {
736 //indentationLevel += popExclusiveUntilBlock();
738 case TokenNameswitch :
742 case TokenNamecatch :
743 case TokenNamefinally :
744 case TokenNamewhile :
746 // case TokenNamesynchronized :
747 clearNonBlockIndents = true;
754 case TokenNameLBRACKET :
756 pendingSpace = false;
758 case TokenNameRBRACKET :
759 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
760 // if there is no left bracket to close, the right bracket is
762 pendingSpace = false;
764 case TokenNameCOMMA :
766 pendingSpace = false;
768 case TokenNameSEMICOLON :
769 // Do not generate line terminators in the definition of
770 // the for statement.
771 // if not in this case, jump a line and reduce indentation after
773 // if the block it closes belongs to a conditional statement (if,
775 if (openParenthesisCount <= 1) {
777 if (expectingOpenBrace) {
778 clearNonBlockIndents = true;
779 expectingOpenBrace = false;
782 inAssignment = false;
783 pendingSpace = false;
785 case TokenNamePLUS_PLUS :
786 case TokenNameMINUS_MINUS :
787 // Do not put a space between a post-increment/decrement
788 // and the identifier being modified.
789 if (previousToken == TokenNameIdentifier
790 || previousToken == TokenNameRBRACKET) {
791 pendingSpace = false;
795 // previously ADDITION
796 case TokenNameMINUS :
797 // Handle the unary operators plus and minus via a flag
798 if (!isLiteralToken(previousToken)
799 && previousToken != TokenNameIdentifier
800 && previousToken != TokenNameRPAREN
801 && previousToken != TokenNameRBRACKET) {
802 unarySignModifier = 1;
805 case TokenNameCOLON :
806 // In a switch/case statement, add a newline & indent
807 // when a colon is encountered.
808 if (tokenBeforeColonCount > 0) {
809 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
812 tokenBeforeColonCount--;
815 case TokenNameEQUAL :
818 case Scanner.TokenNameCOMMENT_LINE :
821 currentLineIndentationLevel++;
823 break; // a line is always inserted after a one-line comment
824 case Scanner.TokenNameCOMMENT_PHPDOC :
825 case Scanner.TokenNameCOMMENT_BLOCK :
826 currentCommentOffset = getCurrentCommentOffset();
829 case Scanner.TokenNameWHITESPACE :
830 if (!phpTagAndWhitespace) {
831 // Count the number of line terminators in the whitespace so
832 // line spacing can be preserved near comments.
833 char[] source = scanner.source;
834 newLinesInWhitespace = 0;
835 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
836 if (source[i] == '\r') {
838 if (source[++i] == '\n') {
839 newLinesInWhitespace++;
841 newLinesInWhitespace++;
844 newLinesInWhitespace++;
846 } else if (source[i] == '\n') {
847 newLinesInWhitespace++;
850 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
853 // case TokenNameHTML :
854 // // Add the next token to the formatted source string.
855 // // outputCurrentToken(token);
856 // int startPosition = scanner.startPosition;
858 // for (int i = startPosition, max = scanner.currentPosition; i <
860 // char currentCharacter = scanner.source[i];
861 // updateMappedPositions(i);
862 // currentLineBuffer.append(currentCharacter);
866 if ((token == TokenNameIdentifier) || isLiteralToken(token)
867 || token == TokenNamesuper) {
868 // || token == TokenNamethis) {
869 // Do not put a space between a unary operator
870 // (eg: ++, --, +, -) and the identifier being modified.
871 if (previousToken == TokenNamePLUS_PLUS
872 || previousToken == TokenNameMINUS_MINUS
873 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
874 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
875 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
876 pendingSpace = false;
878 unarySignModifier = 0;
882 // Do not output whitespace tokens.
883 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
885 * Add pending space to the formatted source string. Do not output a
886 * space under the following circumstances: 1) this is the first pass 2)
887 * previous token is an open paren 3) previous token is a period 4)
888 * previous token is the logical compliment (eg: !) 5) previous token
889 * is the bitwise compliment (eg: ~) 6) previous token is the open
890 * bracket (eg: [) 7) in an assignment statement, if the previous
891 * token is an open brace or the current token is a close brace 8)
892 * previous token is a single line comment 9) current token is a '->'
894 if (token == TokenNameMINUS_GREATER
895 && options.compactDereferencingMode)
896 pendingSpace = false;
898 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
899 && token == TokenNameRBRACE;
901 && insertSpaceAfter(previousToken)
902 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
903 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
904 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
905 && !openAndCloseBrace)
908 // Add the next token to the formatted source string.
909 outputCurrentToken(token);
910 if (token == Scanner.TokenNameCOMMENT_LINE
911 && openParenthesisCount > 1) {
913 currentLineBuffer.append(options.lineSeparatorSequence);
914 increaseLineDelta(options.lineSeparatorSequence.length);
918 // Whitespace tokens do not need to be remembered.
919 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
920 previousToken = token;
921 if (token != Scanner.TokenNameCOMMENT_BLOCK
922 && token != Scanner.TokenNameCOMMENT_LINE
923 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
924 previousCompilableToken = token;
928 output(copyRemainingSource());
930 // dump the last token of the source in the formatted output.
931 } catch (InvalidInputException e) {
932 output(copyRemainingSource());
934 // dump the last token of the source in the formatted output.
938 * Formats the char array <code>sourceString</code>, and returns a string
939 * containing the formatted version.
941 * @return the formatted ouput.
943 public String formatSourceString(String sourceString) {
944 char[] sourceChars = sourceString.toCharArray();
945 formattedSource = new StringBuffer(sourceChars.length);
946 scanner.setSource(sourceChars);
948 return formattedSource.toString();
951 * Formats the char array <code>sourceString</code>, and returns a string
952 * containing the formatted version.
955 * the string to format
956 * @param indentationLevel
957 * the initial indentation level
958 * @return the formatted ouput.
960 public String format(String string, int indentationLevel) {
961 return format(string, indentationLevel, (int[]) null);
964 * Formats the char array <code>sourceString</code>, and returns a string
965 * containing the formatted version. The positions array is modified to
966 * contain the mapped positions.
969 * the string to format
970 * @param indentationLevel
971 * the initial indentation level
973 * the array of positions to map
974 * @return the formatted ouput.
976 public String format(String string, int indentationLevel, int[] positions) {
977 return this.format(string, indentationLevel, positions, null);
979 public String format(String string, int indentationLevel, int[] positions,
980 String lineSeparator) {
981 if (lineSeparator != null) {
982 this.options.setLineSeparator(lineSeparator);
984 if (positions != null) {
985 this.setPositionsToMap(positions);
986 this.setInitialIndentationLevel(indentationLevel);
987 String formattedString = this.formatSourceString(string);
988 int[] mappedPositions = this.getMappedPositions();
989 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
990 return formattedString;
992 this.setInitialIndentationLevel(indentationLevel);
993 return this.formatSourceString(string);
997 * Formats the char array <code>sourceString</code>, and returns a string
998 * containing the formatted version. The initial indentation level is 0.
1001 * the string to format
1002 * @return the formatted ouput.
1004 public String format(String string) {
1005 return this.format(string, 0, (int[]) null);
1008 * Formats a given source string, starting indenting it at a particular depth
1009 * and using the given options
1011 * @deprecated backport 1.0 internal functionality
1013 public static String format(String sourceString, int initialIndentationLevel,
1014 ConfigurableOption[] options) {
1015 CodeFormatter formatter = new CodeFormatter(options);
1016 formatter.setInitialIndentationLevel(initialIndentationLevel);
1017 return formatter.formatSourceString(sourceString);
1020 * Returns the number of characters and tab char between the beginning of the
1021 * line and the beginning of the comment.
1023 private int getCurrentCommentOffset() {
1024 int linePtr = scanner.linePtr;
1025 // if there is no beginning of line, return 0.
1029 int beginningOfLine = scanner.lineEnds[linePtr];
1030 int currentStartPosition = scanner.startPosition;
1031 char[] source = scanner.source;
1032 // find the position of the beginning of the line containing the comment
1033 while (beginningOfLine > currentStartPosition) {
1035 beginningOfLine = scanner.lineEnds[--linePtr];
1037 beginningOfLine = 0;
1041 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1042 char currentCharacter = source[i];
1043 switch (currentCharacter) {
1045 offset += options.tabSize;
1060 * Returns an array of descriptions for the configurable options. The
1061 * descriptions may be changed and passed back to a different compiler.
1063 * @deprecated backport 1.0 internal functionality
1065 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1066 String componentName = CodeFormatter.class.getName();
1067 FormatterOptions options = new FormatterOptions();
1068 return new ConfigurableOption[]{
1069 new ConfigurableOption(componentName, "newline.openingBrace", locale,
1070 options.newLineBeforeOpeningBraceMode ? 0 : 1),
1072 new ConfigurableOption(componentName, "newline.controlStatement",
1073 locale, options.newlineInControlStatementMode ? 0 : 1),
1075 new ConfigurableOption(componentName, "newline.clearAll", locale,
1076 options.clearAllBlankLinesMode ? 0 : 1),
1078 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1079 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1080 new ConfigurableOption(componentName, "newline.emptyBlock", locale,
1081 options.newLineInEmptyBlockMode ? 0 : 1),
1083 new ConfigurableOption(componentName, "line.split", locale,
1084 options.maxLineLength),
1086 new ConfigurableOption(componentName, "style.compactAssignment",
1087 locale, options.compactAssignmentMode ? 0 : 1),
1089 new ConfigurableOption(componentName, "tabulation.char", locale,
1090 options.indentWithTab ? 0 : 1),
1092 new ConfigurableOption(componentName, "tabulation.size", locale,
1093 options.tabSize) //$NON-NLS-1$
1097 * Returns the array of mapped positions. Returns null is no positions have
1101 * @deprecated There is no need to retrieve the mapped positions anymore.
1103 public int[] getMappedPositions() {
1104 return mappedPositions;
1107 * Returns the priority of the token given as argument <br>
1108 * The most prioritary the token is, the smallest the return value is.
1110 * @return the priority of <code>token</code>
1112 * the token of which the priority is requested
1114 private static int getTokenPriority(int token) {
1116 case TokenNameextends :
1117 // case TokenNameimplements :
1118 // case TokenNamethrows :
1120 case TokenNameSEMICOLON :
1123 case TokenNameCOMMA :
1126 case TokenNameEQUAL :
1129 case TokenNameAND_AND :
1131 case TokenNameOR_OR :
1134 case TokenNameQUESTION :
1136 case TokenNameCOLON :
1138 return 50; // it's better cutting on ?: than on ;
1139 case TokenNameEQUAL_EQUAL :
1141 case TokenNameEQUAL_EQUAL_EQUAL :
1143 case TokenNameNOT_EQUAL :
1145 case TokenNameNOT_EQUAL_EQUAL :
1148 case TokenNameLESS :
1150 case TokenNameLESS_EQUAL :
1152 case TokenNameGREATER :
1154 case TokenNameGREATER_EQUAL :
1156 // case TokenNameinstanceof : // instanceof
1158 case TokenNamePLUS :
1160 case TokenNameMINUS :
1163 case TokenNameMULTIPLY :
1165 case TokenNameDIVIDE :
1167 case TokenNameREMAINDER :
1170 case TokenNameLEFT_SHIFT :
1172 case TokenNameRIGHT_SHIFT :
1174 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1183 case TokenNameMULTIPLY_EQUAL :
1185 case TokenNameDIVIDE_EQUAL :
1187 case TokenNameREMAINDER_EQUAL :
1189 case TokenNamePLUS_EQUAL :
1191 case TokenNameMINUS_EQUAL :
1193 case TokenNameLEFT_SHIFT_EQUAL :
1195 case TokenNameRIGHT_SHIFT_EQUAL :
1197 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1198 case TokenNameAND_EQUAL :
1200 case TokenNameXOR_EQUAL :
1202 case TokenNameOR_EQUAL :
1209 return Integer.MAX_VALUE;
1213 * Handles the exception raised when an invalid token is encountered. Returns
1214 * true if the exception has been handled, false otherwise.
1216 private boolean handleInvalidToken(Exception e) {
1217 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1218 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1219 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1224 private final void increaseGlobalDelta(int offset) {
1225 globalDelta += offset;
1227 private final void increaseLineDelta(int offset) {
1228 lineDelta += offset;
1230 private final void increaseSplitDelta(int offset) {
1231 splitDelta += offset;
1234 * Returns true if a space has to be inserted after <code>operator</code>
1237 private boolean insertSpaceAfter(int token) {
1239 case TokenNameLPAREN :
1241 case TokenNameTWIDDLE :
1245 case TokenNameWHITESPACE :
1246 case TokenNameLBRACKET :
1247 case TokenNameDOLLAR :
1248 case Scanner.TokenNameCOMMENT_LINE :
1255 * Returns true if a space has to be inserted before <code>operator</code>
1256 * false otherwise. <br>
1257 * Cannot be static as it uses the code formatter options (to know if the
1258 * compact assignment mode is on).
1260 private boolean insertSpaceBefore(int token) {
1262 case TokenNameEQUAL :
1263 return (!options.compactAssignmentMode);
1268 private static boolean isComment(int token) {
1269 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1270 || token == Scanner.TokenNameCOMMENT_LINE
1271 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1274 private static boolean isLiteralToken(int token) {
1275 boolean result = token == TokenNameIntegerLiteral
1276 // || token == TokenNameLongLiteral
1277 // || token == TokenNameFloatingPointLiteral
1278 || token == TokenNameDoubleLiteral
1279 // || token == TokenNameCharacterLiteral
1280 || token == TokenNameStringLiteral;
1284 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1285 * it is split and the result is dumped in <code>formattedSource</code>
1287 * @param newLineCount
1288 * the number of new lines to append
1290 private void newLine(int newLineCount) {
1291 // format current line
1293 beginningOfLineIndex = formattedSource.length();
1294 String currentLine = currentLineBuffer.toString();
1295 if (containsOpenCloseBraces) {
1296 containsOpenCloseBraces = false;
1297 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1,
1299 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1301 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null,
1304 // dump line break(s)
1305 for (int i = 0; i < newLineCount; i++) {
1306 formattedSource.append(options.lineSeparatorSequence);
1307 increaseSplitDelta(options.lineSeparatorSequence.length);
1309 // reset formatter for next line
1310 int currentLength = currentLine.length();
1311 currentLineBuffer = new StringBuffer(currentLength > maxLineSize
1312 ? maxLineSize = currentLength
1314 increaseGlobalDelta(splitDelta);
1315 increaseGlobalDelta(lineDelta);
1317 currentLineIndentationLevel = initialIndentationLevel;
1319 private String operatorString(int operator) {
1321 case TokenNameextends :
1322 return "extends"; //$NON-NLS-1$
1323 // case TokenNameimplements :
1324 // return "implements"; //$NON-NLS-1$
1326 // case TokenNamethrows :
1327 // return "throws"; //$NON-NLS-1$
1328 case TokenNameSEMICOLON :
1330 return ";"; //$NON-NLS-1$
1331 case TokenNameCOMMA :
1333 return ","; //$NON-NLS-1$
1334 case TokenNameEQUAL :
1336 return "="; //$NON-NLS-1$
1337 case TokenNameAND_AND :
1339 return "&&"; //$NON-NLS-1$
1340 case TokenNameOR_OR :
1342 return "||"; //$NON-NLS-1$
1343 case TokenNameQUESTION :
1345 return "?"; //$NON-NLS-1$
1346 case TokenNameCOLON :
1348 return ":"; //$NON-NLS-1$
1349 case TokenNamePAAMAYIM_NEKUDOTAYIM :
1351 return "::"; //$NON-NLS-1$
1352 case TokenNameEQUAL_EQUAL :
1353 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1354 return "=="; //$NON-NLS-1$
1355 case TokenNameEQUAL_EQUAL_EQUAL :
1356 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1357 return "==="; //$NON-NLS-1$
1358 case TokenNameEQUAL_GREATER :
1360 return "=>"; //$NON-NLS-1$
1361 case TokenNameNOT_EQUAL :
1362 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1363 return "!="; //$NON-NLS-1$
1364 case TokenNameNOT_EQUAL_EQUAL :
1365 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1366 return "!=="; //$NON-NLS-1$
1367 case TokenNameLESS :
1369 return "<"; //$NON-NLS-1$
1370 case TokenNameLESS_EQUAL :
1372 return "<="; //$NON-NLS-1$
1373 case TokenNameGREATER :
1375 return ">"; //$NON-NLS-1$
1376 case TokenNameGREATER_EQUAL :
1378 return ">="; //$NON-NLS-1$
1379 // case TokenNameinstanceof : // instanceof
1380 // return "instanceof"; //$NON-NLS-1$
1381 case TokenNamePLUS :
1382 // + (15.17, 15.17.2)
1383 return "+"; //$NON-NLS-1$
1384 case TokenNameMINUS :
1386 return "-"; //$NON-NLS-1$
1387 case TokenNameMULTIPLY :
1389 return "*"; //$NON-NLS-1$
1390 case TokenNameDIVIDE :
1392 return "/"; //$NON-NLS-1$
1393 case TokenNameREMAINDER :
1395 return "%"; //$NON-NLS-1$
1396 case TokenNameLEFT_SHIFT :
1398 return "<<"; //$NON-NLS-1$
1399 case TokenNameRIGHT_SHIFT :
1401 return ">>"; //$NON-NLS-1$
1402 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1403 // return ">>>"; //$NON-NLS-1$
1405 // & (15.21, 15.21.1, 15.21.2)
1406 return "&"; //$NON-NLS-1$
1408 // | (15.21, 15.21.1, 15.21.2)
1409 return "|"; //$NON-NLS-1$
1411 // ^ (15.21, 15.21.1, 15.21.2)
1412 return "^"; //$NON-NLS-1$
1413 case TokenNameMULTIPLY_EQUAL :
1415 return "*="; //$NON-NLS-1$
1416 case TokenNameDIVIDE_EQUAL :
1418 return "/="; //$NON-NLS-1$
1419 case TokenNameREMAINDER_EQUAL :
1421 return "%="; //$NON-NLS-1$
1422 case TokenNamePLUS_EQUAL :
1424 return "+="; //$NON-NLS-1$
1425 case TokenNameMINUS_EQUAL :
1427 return "-="; //$NON-NLS-1$
1428 case TokenNameMINUS_GREATER :
1430 return "->"; //$NON-NLS-1$
1431 case TokenNameLEFT_SHIFT_EQUAL :
1433 return "<<="; //$NON-NLS-1$
1434 case TokenNameRIGHT_SHIFT_EQUAL :
1436 return ">>="; //$NON-NLS-1$
1437 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1438 // return ">>>="; //$NON-NLS-1$
1439 case TokenNameAND_EQUAL :
1441 return "&="; //$NON-NLS-1$
1442 case TokenNameXOR_EQUAL :
1444 return "^="; //$NON-NLS-1$
1445 case TokenNameOR_EQUAL :
1447 return "|="; //$NON-NLS-1$
1450 return "."; //$NON-NLS-1$
1452 return ""; //$NON-NLS-1$
1456 * Appends <code>stringToOutput</code> to the formatted output. <br>
1457 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1459 private void output(String stringToOutput) {
1460 char currentCharacter;
1461 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1462 currentCharacter = stringToOutput.charAt(i);
1463 if (currentCharacter != '\t') {
1464 currentLineBuffer.append(currentCharacter);
1469 * Appends <code>token</code> to the formatted output. <br>
1470 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1473 private void outputCurrentToken(int token) {
1474 char[] source = scanner.source;
1475 int startPosition = scanner.startPosition;
1477 case Scanner.TokenNameCOMMENT_PHPDOC :
1478 case Scanner.TokenNameCOMMENT_BLOCK :
1479 case Scanner.TokenNameCOMMENT_LINE :
1480 boolean endOfLine = false;
1481 int currentCommentOffset = getCurrentCommentOffset();
1482 int beginningOfLineSpaces = 0;
1484 currentCommentOffset = getCurrentCommentOffset();
1485 beginningOfLineSpaces = 0;
1486 boolean pendingCarriageReturn = false;
1487 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1488 char currentCharacter = source[i];
1489 updateMappedPositions(i);
1490 switch (currentCharacter) {
1492 pendingCarriageReturn = true;
1496 if (pendingCarriageReturn) {
1497 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1499 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1501 pendingCarriageReturn = false;
1502 currentLineBuffer.append(options.lineSeparatorSequence);
1503 beginningOfLineSpaces = 0;
1507 if (pendingCarriageReturn) {
1508 pendingCarriageReturn = false;
1509 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1510 currentLineBuffer.append(options.lineSeparatorSequence);
1511 beginningOfLineSpaces = 0;
1515 // we remove a maximum of currentCommentOffset characters (tabs
1516 // are converted to space numbers).
1517 beginningOfLineSpaces += options.tabSize;
1518 if (beginningOfLineSpaces > currentCommentOffset) {
1519 currentLineBuffer.append(currentCharacter);
1521 increaseGlobalDelta(-1);
1524 currentLineBuffer.append(currentCharacter);
1528 if (pendingCarriageReturn) {
1529 pendingCarriageReturn = false;
1530 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1531 currentLineBuffer.append(options.lineSeparatorSequence);
1532 beginningOfLineSpaces = 0;
1536 // we remove a maximum of currentCommentOffset characters (tabs
1537 // are converted to space numbers).
1538 beginningOfLineSpaces++;
1539 if (beginningOfLineSpaces > currentCommentOffset) {
1540 currentLineBuffer.append(currentCharacter);
1542 increaseGlobalDelta(-1);
1545 currentLineBuffer.append(currentCharacter);
1549 if (pendingCarriageReturn) {
1550 pendingCarriageReturn = false;
1551 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1552 currentLineBuffer.append(options.lineSeparatorSequence);
1553 beginningOfLineSpaces = 0;
1556 beginningOfLineSpaces = 0;
1557 currentLineBuffer.append(currentCharacter);
1562 updateMappedPositions(scanner.currentPosition - 1);
1563 multipleLineCommentCounter++;
1566 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1567 char currentCharacter = source[i];
1568 updateMappedPositions(i);
1569 currentLineBuffer.append(currentCharacter);
1574 * Outputs <code>currentString</code>:<br>
1576 * <li>If its length is < maxLineLength, output
1577 * <li>Otherwise it is split.
1580 * @param currentString
1582 * @param preIndented
1583 * whether the string to output was pre-indented
1585 * number of indentation to put in front of <code>currentString</code>
1587 * value of the operator belonging to <code>currentString</code>.
1589 private void outputLine(String currentString, boolean preIndented, int depth,
1590 int operator, int substringIndex, int[] startSubstringIndexes,
1591 int offsetInGlobalLine) {
1592 boolean emptyFirstSubString = false;
1593 String operatorString = operatorString(operator);
1594 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1595 boolean placeOperatorAhead = !placeOperatorBehind;
1596 // dump prefix operator?
1597 if (placeOperatorAhead) {
1602 if (operator != 0) {
1603 if (insertSpaceBefore(operator)) {
1604 formattedSource.append(' ');
1605 increaseSplitDelta(1);
1607 formattedSource.append(operatorString);
1608 increaseSplitDelta(operatorString.length());
1609 if (insertSpaceAfter(operator)
1610 && operator != TokenNameimplements
1611 && operator != TokenNameextends) {
1612 // && operator != TokenNamethrows) {
1613 formattedSource.append(' ');
1614 increaseSplitDelta(1);
1618 SplitLine splitLine = null;
1619 if (options.maxLineLength == 0
1620 || getLength(currentString, depth) < options.maxLineLength
1621 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1622 // depending on the type of operator, outputs new line before of after
1624 // indent before postfix operator
1625 // indent also when the line cannot be split
1626 if (operator == TokenNameextends
1627 || operator == TokenNameimplements ) {
1628 // || operator == TokenNamethrows) {
1629 formattedSource.append(' ');
1630 increaseSplitDelta(1);
1632 if (placeOperatorBehind) {
1637 int max = currentString.length();
1638 if (multipleLineCommentCounter != 0) {
1640 BufferedReader reader = new BufferedReader(new StringReader(
1642 String line = reader.readLine();
1643 while (line != null) {
1644 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1645 beginningOfLineIndex + line.length()
1646 + options.lineSeparatorSequence.length);
1647 formattedSource.append(line);
1648 beginningOfLineIndex = beginningOfLineIndex + line.length();
1649 if ((line = reader.readLine()) != null) {
1650 formattedSource.append(options.lineSeparatorSequence);
1651 beginningOfLineIndex += options.lineSeparatorSequence.length;
1652 dumpTab(currentLineIndentationLevel);
1656 } catch (IOException e) {
1657 e.printStackTrace();
1660 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1661 beginningOfLineIndex + max);
1662 for (int i = 0; i < max; i++) {
1663 char currentChar = currentString.charAt(i);
1664 switch (currentChar) {
1669 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1670 // split with a comment inside a condition
1671 // a substring cannot end with a lineSeparatorSequence,
1672 // except if it has been added by format() after a one-line
1674 formattedSource.append(options.lineSeparatorSequence);
1675 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1680 formattedSource.append(currentChar);
1684 // update positions inside the mappedPositions table
1685 if (substringIndex != -1) {
1686 if (multipleLineCommentCounter == 0) {
1687 int startPosition = beginningOfLineIndex
1688 + startSubstringIndexes[substringIndex];
1689 updateMappedPositionsWhileSplitting(startPosition, startPosition
1692 // compute the splitDelta resulting with the operator and blank removal
1693 if (substringIndex + 1 != startSubstringIndexes.length) {
1694 increaseSplitDelta(startSubstringIndexes[substringIndex] + max
1695 - startSubstringIndexes[substringIndex + 1]);
1698 // dump postfix operator?
1699 if (placeOperatorBehind) {
1700 if (insertSpaceBefore(operator)) {
1701 formattedSource.append(' ');
1702 if (operator != 0) {
1703 increaseSplitDelta(1);
1706 formattedSource.append(operatorString);
1707 if (operator != 0) {
1708 increaseSplitDelta(operatorString.length());
1713 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1714 // extends has to stand alone on a line when currentString has been split.
1715 if (options.maxLineLength != 0 && splitLine != null
1716 && (operator == TokenNameextends)) {
1717 // || operator == TokenNameimplements
1718 // || operator == TokenNamethrows)) {
1719 formattedSource.append(options.lineSeparatorSequence);
1720 increaseSplitDelta(options.lineSeparatorSequence.length);
1723 if (operator == TokenNameextends) {
1724 // || operator == TokenNameimplements
1725 // || operator == TokenNamethrows) {
1726 formattedSource.append(' ');
1727 increaseSplitDelta(1);
1730 // perform actual splitting
1731 String result[] = splitLine.substrings;
1732 int[] splitOperators = splitLine.operators;
1733 if (result[0].length() == 0) {
1734 // when the substring 0 is null, the substring 1 is correctly indented.
1736 emptyFirstSubString = true;
1738 // the operator going in front of the result[0] string is the operator
1740 for (int i = 0, max = result.length; i < max; i++) {
1741 // the new depth is the current one if this is the first substring,
1742 // the current one + 1 otherwise.
1743 // if the substring is a comment, use the current indentation Level
1744 // instead of the depth
1745 // (-1 because the ouputline increases depth).
1746 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1748 String currentResult = result[i];
1749 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1750 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1751 || currentResult.startsWith("//")) //$NON-NLS-1$
1752 ? indentationLevel - 1 : depth;
1753 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString)
1755 : false, i == 0 ? newDepth : newDepth + 1, splitOperators[i], i,
1756 splitLine.startSubstringsIndexes, currentString
1757 .indexOf(currentResult));
1759 formattedSource.append(options.lineSeparatorSequence);
1760 increaseSplitDelta(options.lineSeparatorSequence.length);
1764 if (result.length == splitOperators.length - 1) {
1765 int lastOperator = splitOperators[result.length];
1766 String lastOperatorString = operatorString(lastOperator);
1767 formattedSource.append(options.lineSeparatorSequence);
1768 increaseSplitDelta(options.lineSeparatorSequence.length);
1769 if (breakLineBeforeOperator(lastOperator)) {
1771 if (lastOperator != 0) {
1772 if (insertSpaceBefore(lastOperator)) {
1773 formattedSource.append(' ');
1774 increaseSplitDelta(1);
1776 formattedSource.append(lastOperatorString);
1777 increaseSplitDelta(lastOperatorString.length());
1778 if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements
1779 && lastOperator != TokenNameextends) {
1780 // && lastOperator != TokenNamethrows) {
1781 formattedSource.append(' ');
1782 increaseSplitDelta(1);
1787 if (placeOperatorBehind) {
1788 if (insertSpaceBefore(operator)) {
1789 formattedSource.append(' ');
1790 increaseSplitDelta(1);
1792 formattedSource.append(operatorString);
1793 //increaseSplitDelta(operatorString.length());
1797 * Pops the top statement of the stack if it is <code>token</code>
1799 private int pop(int token) {
1801 if ((constructionsCount > 0)
1802 && (constructions[constructionsCount - 1] == token)) {
1804 constructionsCount--;
1809 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
1810 * <code>NONINDENT_BLOCK</code>.
1812 private int popBlock() {
1814 if ((constructionsCount > 0)
1815 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1816 if (constructions[constructionsCount - 1] == BLOCK)
1818 constructionsCount--;
1823 * Pops elements until the stack is empty or the top element is <code>token</code>.
1825 * Does not remove <code>token</code> from the stack.
1828 * the token to be left as the top of the stack
1830 private int popExclusiveUntil(int token) {
1832 int startCount = constructionsCount;
1833 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1834 if (constructions[i] != NONINDENT_BLOCK)
1836 constructionsCount--;
1841 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1842 * or a <code>NONINDENT_BLOCK</code>.<br>
1843 * Does not remove it from the stack.
1845 private int popExclusiveUntilBlock() {
1846 int startCount = constructionsCount;
1848 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1849 && constructions[i] != NONINDENT_BLOCK; i--) {
1850 constructionsCount--;
1856 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>,
1857 * a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1858 * Does not remove it from the stack.
1860 private int popExclusiveUntilBlockOrCase() {
1861 int startCount = constructionsCount;
1863 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1864 && constructions[i] != NONINDENT_BLOCK
1865 && constructions[i] != TokenNamecase; i--) {
1866 constructionsCount--;
1872 * Pops elements until the stack is empty or the top element is <code>token</code>.
1874 * Removes <code>token</code> from the stack too.
1877 * the token to remove from the stack
1879 private int popInclusiveUntil(int token) {
1880 int startCount = constructionsCount;
1882 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1883 if (constructions[i] != NONINDENT_BLOCK)
1885 constructionsCount--;
1887 if (constructionsCount > 0) {
1888 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1890 constructionsCount--;
1895 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1896 * or a <code>NONINDENT_BLOCK</code>.<br>
1897 * Does not remove it from the stack.
1899 private int popInclusiveUntilBlock() {
1900 int startCount = constructionsCount;
1902 for (int i = startCount - 1; i >= 0
1903 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1905 constructionsCount--;
1907 if (constructionsCount > 0) {
1908 if (constructions[constructionsCount - 1] == BLOCK)
1910 constructionsCount--;
1915 * Pushes a block in the stack. <br>
1916 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
1917 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
1918 * otherwise. Creates a new bigger array if the current one is full.
1920 private int pushBlock() {
1922 if (constructionsCount == constructions.length)
1923 System.arraycopy(constructions, 0,
1924 (constructions = new int[constructionsCount * 2]), 0,
1925 constructionsCount);
1926 if ((constructionsCount == 0)
1927 || (constructions[constructionsCount - 1] == BLOCK)
1928 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1929 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1931 constructions[constructionsCount++] = BLOCK;
1933 constructions[constructionsCount++] = NONINDENT_BLOCK;
1938 * Pushes <code>token</code>.<br>
1939 * Creates a new bigger array if the current one is full.
1941 private int pushControlStatement(int token) {
1942 if (constructionsCount == constructions.length)
1943 System.arraycopy(constructions, 0,
1944 (constructions = new int[constructionsCount * 2]), 0,
1945 constructionsCount);
1946 constructions[constructionsCount++] = token;
1949 private static boolean separateFirstArgumentOn(int currentToken) {
1950 //return (currentToken == TokenNameCOMMA || currentToken ==
1951 // TokenNameSEMICOLON);
1952 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
1953 && currentToken != TokenNameNOT && currentToken != TokenNamewhile
1954 && currentToken != TokenNamefor && currentToken != TokenNameswitch;
1957 * Set the positions to map. The mapped positions should be retrieved using
1958 * the getMappedPositions() method.
1962 * @deprecated Set the positions to map using the format(String, int, int[])
1965 * @see #getMappedPositions()
1967 public void setPositionsToMap(int[] positions) {
1968 positionsToMap = positions;
1971 mappedPositions = new int[positions.length];
1974 * Appends a space character to the current line buffer.
1976 private void space() {
1977 currentLineBuffer.append(' ');
1978 increaseLineDelta(1);
1981 * Splits <code>stringToSplit</code> on the top level token <br>
1982 * If there are several identical token at the same level, the string is cut
1985 * @return an object containing the operator and all the substrings or null
1986 * if the string cannot be split
1988 public SplitLine split(String stringToSplit) {
1989 return split(stringToSplit, 0);
1992 * Splits <code>stringToSplit</code> on the top level token <br>
1993 * If there are several identical token at the same level, the string is cut
1996 * @return an object containing the operator and all the substrings or null
1997 * if the string cannot be split
1999 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2001 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2002 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2004 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2007 // split doesn't work correct for PHP
2010 // int currentToken = 0;
2011 // int splitTokenType = 0;
2012 // int splitTokenDepth = Integer.MAX_VALUE;
2013 // int splitTokenPriority = Integer.MAX_VALUE;
2014 // int[] substringsStartPositions = new int[10];
2015 // // contains the start position of substrings
2016 // int[] substringsEndPositions = new int[10];
2017 // // contains the start position of substrings
2018 // int substringsCount = 1; // index in the substringsStartPosition array
2019 // int[] splitOperators = new int[10];
2020 // // contains the start position of substrings
2021 // int splitOperatorsCount = 0; // index in the substringsStartPosition array
2022 // int[] openParenthesisPosition = new int[10];
2023 // int openParenthesisPositionCount = 0;
2024 // int position = 0;
2025 // int lastOpenParenthesisPosition = -1;
2026 // // used to remember the position of the 1st open parenthesis
2027 // // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2028 // // setup the scanner with a new source
2029 // int lastCommentStartPosition = -1;
2030 // // to remember the start position of the last comment
2031 // int firstTokenOnLine = -1;
2032 // // to remember the first token of the line
2033 // int previousToken = -1;
2034 // // to remember the previous token.
2035 // splitScanner.setSource(stringToSplit.toCharArray());
2037 // // start the loop
2039 // // takes the next token
2041 // if (currentToken != Scanner.TokenNameWHITESPACE)
2042 // previousToken = currentToken;
2043 // currentToken = splitScanner.getNextToken();
2044 // if (Scanner.DEBUG) {
2045 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2046 // int currentStartPosition = splitScanner
2047 // .getCurrentTokenStartPosition();
2048 // System.out.print(currentStartPosition + "," + currentEndPosition
2050 // System.out.println(scanner.toStringAction(currentToken));
2052 // } catch (InvalidInputException e) {
2053 // if (!handleInvalidToken(e))
2055 // currentToken = 0;
2056 // // this value is not modify when an exception is raised.
2058 // if (currentToken == TokenNameEOF)
2060 // if (firstTokenOnLine == -1) {
2061 // firstTokenOnLine = currentToken;
2063 // switch (currentToken) {
2064 // case TokenNameRBRACE :
2065 // case TokenNameRPAREN :
2066 // if (openParenthesisPositionCount > 0) {
2067 // if (openParenthesisPositionCount == 1
2068 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2069 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2070 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2071 // || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2072 // splitTokenType = 0;
2073 // splitTokenDepth = openParenthesisPositionCount;
2074 // splitTokenPriority = Integer.MAX_VALUE;
2075 // substringsStartPositions[0] = 0;
2076 // // better token means the whole line until now is the first
2078 // substringsCount = 1; // resets the count of substrings
2079 // substringsEndPositions[0] = openParenthesisPosition[0];
2080 // // substring ends on operator start
2081 // position = openParenthesisPosition[0];
2082 // // the string mustn't be cut before the closing parenthesis but
2083 // // after the opening one.
2084 // splitOperatorsCount = 1; // resets the count of split operators
2085 // splitOperators[0] = 0;
2087 // openParenthesisPositionCount--;
2090 // case TokenNameLBRACE :
2091 // case TokenNameLPAREN :
2092 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2095 // openParenthesisPosition,
2097 // (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2098 // 0, openParenthesisPositionCount);
2100 // openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2101 // if (currentToken == TokenNameLPAREN
2102 // && previousToken == TokenNameRPAREN) {
2103 // openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2106 // case TokenNameSEMICOLON :
2108 // case TokenNameCOMMA :
2110 // case TokenNameEQUAL :
2112 // if (openParenthesisPositionCount < splitTokenDepth
2113 // || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2114 // // the current token is better than the one we currently have
2115 // // (in level or in priority if same level)
2116 // // reset the substringsCount
2117 // splitTokenDepth = openParenthesisPositionCount;
2118 // splitTokenType = currentToken;
2119 // splitTokenPriority = getTokenPriority(currentToken);
2120 // substringsStartPositions[0] = 0;
2121 // // better token means the whole line until now is the first
2123 // if (separateFirstArgumentOn(firstTokenOnLine)
2124 // && openParenthesisPositionCount > 0) {
2125 // substringsCount = 2; // resets the count of substrings
2126 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2127 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2128 // substringsEndPositions[1] = splitScanner.startPosition;
2129 // splitOperatorsCount = 2; // resets the count of split operators
2130 // splitOperators[0] = 0;
2131 // splitOperators[1] = currentToken;
2132 // position = splitScanner.currentPosition;
2133 // // next substring will start from operator end
2135 // substringsCount = 1; // resets the count of substrings
2136 // substringsEndPositions[0] = splitScanner.startPosition;
2137 // // substring ends on operator start
2138 // position = splitScanner.currentPosition;
2139 // // next substring will start from operator end
2140 // splitOperatorsCount = 1; // resets the count of split operators
2141 // splitOperators[0] = currentToken;
2144 // if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2145 // && splitTokenType != TokenNameEQUAL
2146 // && currentToken != TokenNameEQUAL) {
2147 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2149 // // take only the 1st = into account.
2150 // // if another token with the same priority is found,
2151 // // push the start position of the substring and
2152 // // push the token into the stack.
2153 // // create a new array object if the current one is full.
2154 // if (substringsCount == substringsStartPositions.length) {
2157 // substringsStartPositions,
2159 // (substringsStartPositions = new int[substringsCount * 2]),
2160 // 0, substringsCount);
2161 // System.arraycopy(substringsEndPositions, 0,
2162 // (substringsEndPositions = new int[substringsCount * 2]),
2163 // 0, substringsCount);
2165 // if (splitOperatorsCount == splitOperators.length) {
2166 // System.arraycopy(splitOperators, 0,
2167 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2168 // splitOperatorsCount);
2170 // substringsStartPositions[substringsCount] = position;
2171 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2172 // // substring ends on operator start
2173 // position = splitScanner.currentPosition;
2174 // // next substring will start from operator end
2175 // splitOperators[splitOperatorsCount++] = currentToken;
2179 // case TokenNameCOLON :
2181 // // see 1FK7C5R, we only split on a colon, when it is associated
2182 // // with a question-mark.
2183 // // indeed it might appear also behind a case statement, and we do
2184 // // not to break at this point.
2185 // if ((splitOperatorsCount == 0)
2186 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2189 // case TokenNameextends :
2190 // case TokenNameimplements :
2191 // //case TokenNamethrows :
2192 // case TokenNameDOT :
2194 // case TokenNameMULTIPLY :
2196 // case TokenNameDIVIDE :
2198 // case TokenNameREMAINDER :
2200 // case TokenNamePLUS :
2201 // // + (15.17, 15.17.2)
2202 // case TokenNameMINUS :
2204 // case TokenNameLEFT_SHIFT :
2206 // case TokenNameRIGHT_SHIFT :
2208 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2209 // case TokenNameLESS :
2211 // case TokenNameLESS_EQUAL :
2213 // case TokenNameGREATER :
2215 // case TokenNameGREATER_EQUAL :
2217 // // case TokenNameinstanceof : // instanceof
2218 // case TokenNameEQUAL_EQUAL :
2219 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2220 // case TokenNameEQUAL_EQUAL_EQUAL :
2221 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2222 // case TokenNameNOT_EQUAL :
2223 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2224 // case TokenNameNOT_EQUAL_EQUAL :
2225 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2226 // case TokenNameAND :
2227 // // & (15.21, 15.21.1, 15.21.2)
2228 // case TokenNameOR :
2229 // // | (15.21, 15.21.1, 15.21.2)
2230 // case TokenNameXOR :
2231 // // ^ (15.21, 15.21.1, 15.21.2)
2232 // case TokenNameAND_AND :
2234 // case TokenNameOR_OR :
2236 // case TokenNameQUESTION :
2238 // case TokenNameMULTIPLY_EQUAL :
2240 // case TokenNameDIVIDE_EQUAL :
2242 // case TokenNameREMAINDER_EQUAL :
2244 // case TokenNamePLUS_EQUAL :
2246 // case TokenNameMINUS_EQUAL :
2248 // case TokenNameLEFT_SHIFT_EQUAL :
2250 // case TokenNameRIGHT_SHIFT_EQUAL :
2252 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2253 // case TokenNameAND_EQUAL :
2255 // case TokenNameXOR_EQUAL :
2257 // case TokenNameOR_EQUAL :
2259 // if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2260 // && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2261 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2262 // // the current token is better than the one we currently have
2263 // // (in level or in priority if same level)
2264 // // reset the substringsCount
2265 // splitTokenDepth = openParenthesisPositionCount;
2266 // splitTokenType = currentToken;
2267 // splitTokenPriority = getTokenPriority(currentToken);
2268 // substringsStartPositions[0] = 0;
2269 // // better token means the whole line until now is the first
2271 // if (separateFirstArgumentOn(firstTokenOnLine)
2272 // && openParenthesisPositionCount > 0) {
2273 // substringsCount = 2; // resets the count of substrings
2274 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2275 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2276 // substringsEndPositions[1] = splitScanner.startPosition;
2277 // splitOperatorsCount = 3; // resets the count of split operators
2278 // splitOperators[0] = 0;
2279 // splitOperators[1] = 0;
2280 // splitOperators[2] = currentToken;
2281 // position = splitScanner.currentPosition;
2282 // // next substring will start from operator end
2284 // substringsCount = 1; // resets the count of substrings
2285 // substringsEndPositions[0] = splitScanner.startPosition;
2286 // // substring ends on operator start
2287 // position = splitScanner.currentPosition;
2288 // // next substring will start from operator end
2289 // splitOperatorsCount = 2; // resets the count of split operators
2290 // splitOperators[0] = 0;
2291 // // nothing for first operand since operator will be inserted in
2292 // // front of the second operand
2293 // splitOperators[1] = currentToken;
2296 // if (openParenthesisPositionCount == splitTokenDepth
2297 // && splitTokenPriority == getTokenPriority(currentToken)) {
2298 // // if another token with the same priority is found,
2299 // // push the start position of the substring and
2300 // // push the token into the stack.
2301 // // create a new array object if the current one is full.
2302 // if (substringsCount == substringsStartPositions.length) {
2305 // substringsStartPositions,
2307 // (substringsStartPositions = new int[substringsCount * 2]),
2308 // 0, substringsCount);
2309 // System.arraycopy(substringsEndPositions, 0,
2310 // (substringsEndPositions = new int[substringsCount * 2]),
2311 // 0, substringsCount);
2313 // if (splitOperatorsCount == splitOperators.length) {
2314 // System.arraycopy(splitOperators, 0,
2315 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2316 // splitOperatorsCount);
2318 // substringsStartPositions[substringsCount] = position;
2319 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2320 // // substring ends on operator start
2321 // position = splitScanner.currentPosition;
2322 // // next substring will start from operator end
2323 // splitOperators[splitOperatorsCount++] = currentToken;
2329 // if (isComment(currentToken)) {
2330 // lastCommentStartPosition = splitScanner.startPosition;
2332 // lastCommentStartPosition = -1;
2335 // } catch (InvalidInputException e) {
2338 // // if the string cannot be split, return null.
2339 // if (splitOperatorsCount == 0)
2341 // // ## SPECIAL CASES BEGIN
2342 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2343 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2344 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2345 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) || (separateFirstArgumentOn(firstTokenOnLine)
2346 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2347 // && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2348 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2349 // // not be broken on two lines
2350 // // only one split on a top level .
2351 // // or more than one split on . and substring before open parenthesis fits
2353 // // or split inside parenthesis and first token is not a for/while/if
2354 // SplitLine sl = split(
2355 // stringToSplit.substring(lastOpenParenthesisPosition),
2356 // lastOpenParenthesisPosition);
2357 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2358 // // trim() is used to remove the extra blanks at the end of the
2359 // // substring. See PR 1FGYPI1
2360 // return new SplitLine(new int[]{0, 0}, new String[]{
2361 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2362 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2363 // offsetInGlobalLine,
2364 // lastOpenParenthesisPosition + offsetInGlobalLine});
2366 // // right substring can be split and is split on comma
2367 // // copy substrings and operators
2368 // // except if the 1st string is empty.
2369 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2370 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2371 // String[] result = new String[subStringsLength];
2372 // int[] startIndexes = new int[subStringsLength];
2373 // int operatorsLength = sl.operators.length + 1 - startIndex;
2374 // int[] operators = new int[operatorsLength];
2375 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2376 // operators[0] = 0;
2377 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2378 // 1, subStringsLength - 1);
2379 // for (int i = subStringsLength - 1; i >= 0; i--) {
2380 // startIndexes[i] += offsetInGlobalLine;
2382 // System.arraycopy(sl.substrings, startIndex, result, 1,
2383 // subStringsLength - 1);
2384 // System.arraycopy(sl.operators, startIndex, operators, 1,
2385 // operatorsLength - 1);
2386 // return new SplitLine(operators, result, startIndexes);
2389 // // if the last token is a comment and the substring before the comment fits
2391 // // split before the comment and return the result.
2392 // if (lastCommentStartPosition > -1
2393 // && lastCommentStartPosition < options.maxLineLength
2394 // && splitTokenPriority > 50) {
2395 // int end = lastCommentStartPosition;
2396 // int start = lastCommentStartPosition;
2397 // if (stringToSplit.charAt(end - 1) == ' ') {
2400 // if (start != end && stringToSplit.charAt(start) == ' ') {
2403 // return new SplitLine(new int[]{0, 0}, new String[]{
2404 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2405 // new int[]{0, start});
2407 // if (position != stringToSplit.length()) {
2408 // if (substringsCount == substringsStartPositions.length) {
2409 // System.arraycopy(substringsStartPositions, 0,
2410 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2411 // substringsCount);
2412 // System.arraycopy(substringsEndPositions, 0,
2413 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2414 // substringsCount);
2416 // // avoid empty extra substring, e.g. line terminated with a semi-colon
2417 // substringsStartPositions[substringsCount] = position;
2418 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2420 // if (splitOperatorsCount == splitOperators.length) {
2421 // System.arraycopy(splitOperators, 0,
2422 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2423 // splitOperatorsCount);
2425 // splitOperators[splitOperatorsCount] = 0;
2426 // // the last element of the stack is the position of the end of
2428 // // +1 because the substring method excludes the last character
2429 // String[] result = new String[substringsCount];
2430 // for (int i = 0; i < substringsCount; i++) {
2431 // int start = substringsStartPositions[i];
2432 // int end = substringsEndPositions[i];
2433 // if (stringToSplit.charAt(start) == ' ') {
2435 // substringsStartPositions[i]++;
2437 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2440 // result[i] = stringToSplit.substring(start, end);
2441 // substringsStartPositions[i] += offsetInGlobalLine;
2443 // if (splitOperatorsCount > substringsCount) {
2444 // System.arraycopy(substringsStartPositions, 0,
2445 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2446 // substringsCount);
2447 // System.arraycopy(substringsEndPositions, 0,
2448 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2449 // substringsCount);
2450 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2451 // substringsStartPositions[i] = position;
2452 // substringsEndPositions[i] = position;
2454 // System.arraycopy(splitOperators, 0,
2455 // (splitOperators = new int[splitOperatorsCount]), 0,
2456 // splitOperatorsCount);
2458 // System.arraycopy(substringsStartPositions, 0,
2459 // (substringsStartPositions = new int[substringsCount]), 0,
2460 // substringsCount);
2461 // System.arraycopy(substringsEndPositions, 0,
2462 // (substringsEndPositions = new int[substringsCount]), 0,
2463 // substringsCount);
2464 // System.arraycopy(splitOperators, 0,
2465 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2467 // SplitLine splitLine = new SplitLine(splitOperators, result,
2468 // substringsStartPositions);
2469 // return splitLine;
2471 private void updateMappedPositions(int startPosition) {
2472 if (positionsToMap == null) {
2475 char[] source = scanner.source;
2476 int sourceLength = source.length;
2477 while (indexToMap < positionsToMap.length
2478 && positionsToMap[indexToMap] <= startPosition) {
2479 int posToMap = positionsToMap[indexToMap];
2480 if (posToMap < 0 || posToMap >= sourceLength) {
2481 // protection against out of bounds position
2482 if (posToMap == sourceLength) {
2483 mappedPositions[indexToMap] = formattedSource.length();
2485 indexToMap = positionsToMap.length; // no more mapping
2488 if (CharOperation.isWhitespace(source[posToMap])) {
2489 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2491 if (posToMap == sourceLength - 1) {
2492 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2494 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2500 private void updateMappedPositionsWhileSplitting(int startPosition,
2502 if (mappedPositions == null || mappedPositions.length == indexInMap)
2504 while (indexInMap < mappedPositions.length
2505 && startPosition <= mappedPositions[indexInMap]
2506 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2507 mappedPositions[indexInMap] += splitDelta;
2511 private int getLength(String s, int tabDepth) {
2513 for (int i = 0; i < tabDepth; i++) {
2514 length += options.tabSize;
2516 for (int i = 0, max = s.length(); i < max; i++) {
2517 char currentChar = s.charAt(i);
2518 switch (currentChar) {
2520 length += options.tabSize;
2529 * Sets the initial indentation level
2531 * @param indentationLevel
2532 * new indentation level
2536 public void setInitialIndentationLevel(int newIndentationLevel) {
2537 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;