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
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/cpl-v05.html
10 * IBM Corporation - initial API and implementation
11 ******************************************************************************/
12 package net.sourceforge.phpdt.internal.formatter;
14 import java.io.BufferedReader;
15 import java.io.IOException;
16 import java.io.StringReader;
17 import java.util.Hashtable;
18 //import java.util.Locale;
21 //import javax.swing.text.html.Option;
23 import net.sourceforge.phpdt.core.ICodeFormatter;
24 import net.sourceforge.phpdt.core.compiler.CharOperation;
25 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
26 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
27 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
28 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
29 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
30 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
33 * <h2>How to format a piece of code ?</h2>
35 * <li>Create an instance of <code>CodeFormatter</code>
36 * <li>Use the method <code>void format(aString)</code> on this instance to
37 * format <code>aString</code>. It will return the formatted string.
40 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
41 // IContentFormatterExtension {
42 public FormatterOptions options;
45 * Represents a block in the <code>constructions</code> stack.
47 public static final TokenName BLOCK = TokenName.LBRACE;
50 * Represents a block following a control statement in the
51 * <code>constructions</code> stack.
53 public static final TokenName NONINDENT_BLOCK = TokenName.NONE_INDENT_BLOCK;
56 * Contains the formatted output.
58 StringBuffer formattedSource;
61 * Contains the current line. <br>
62 * Will be dumped at the next "newline"
64 StringBuffer currentLineBuffer;
67 * Used during the formatting to get each token.
72 * Contains the tokens responsible for the current indentation level and the
73 * blocks not closed yet.
75 private TokenName[] constructions;
78 * Index in the <code>constructions</code> array.
80 private int constructionsCount;
83 * Level of indentation of the current token (number of tab char put in
86 private int indentationLevel;
89 * Regular level of indentation of all the lines
91 private int initialIndentationLevel;
94 * Used to split a line.
99 * To remember the offset between the beginning of the line and the
100 * beginning of the comment.
102 int currentCommentOffset;
104 int currentLineIndentationLevel;
106 int maxLineSize = 30;
108 private boolean containsOpenCloseBraces;
110 private int indentationLevelForOpenCloseBraces;
113 * Collections of positions to map
115 private int[] positionsToMap;
118 * Collections of mapped positions
120 private int[] mappedPositions;
122 private int indexToMap;
124 private int indexInMap;
126 private int globalDelta;
128 private int lineDelta;
130 private int splitDelta;
132 private int beginningOfLineIndex;
134 private int multipleLineCommentCounter;
137 * Creates a new instance of Code Formatter using the given settings.
139 * @deprecated backport 1.0 internal functionality
141 public CodeFormatter(ConfigurableOption[] settings) {
142 this(convertConfigurableOptions(settings));
146 * Creates a new instance of Code Formatter using the FormattingOptions
147 * object given as argument
149 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
151 // public CodeFormatter() {
156 * Creates a new instance of Code Formatter using the given settings.
158 public CodeFormatter(Map settings) {
159 // initialize internal state
160 constructionsCount = 0;
161 constructions = new TokenName[10];
162 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
163 currentCommentOffset = -1;
164 // initialize primary and secondary scanners
165 scanner = new Scanner(true /* comment */
166 , true /* whitespace */
169 , true, /* tokenizeStrings */
170 null, null, true /* taskCaseSensitive */); // regular scanner for
172 scanner.recordLineSeparator = true;
173 scanner.ignorePHPOneLiner = true;
174 // to remind of the position of the beginning of the line.
175 splitScanner = new Scanner(true /* comment */
176 , true /* whitespace */
179 , true, /* tokenizeStrings */
180 null, null, true /* taskCaseSensitive */);
181 splitScanner.ignorePHPOneLiner = true;
182 // secondary scanner to split long lines formed by primary scanning
183 // initialize current line buffer
184 currentLineBuffer = new StringBuffer();
185 this.options = new FormatterOptions(settings);
189 * Returns true if a lineSeparator has to be inserted before
190 * <code>operator</code> false otherwise.
192 private static boolean breakLineBeforeOperator(TokenName operator) {
204 * @deprecated backport 1.0 internal functionality
206 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
207 Hashtable options = new Hashtable(10);
208 for (int i = 0; i < settings.length; i++) {
209 if (settings[i].getComponentName().equals(
210 CodeFormatter.class.getName())) {
211 String optionName = settings[i].getOptionName();
212 int valueIndex = settings[i].getCurrentValueIndex();
213 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
216 "net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
217 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
220 "net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
221 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
224 "net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
225 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
228 "net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
229 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
232 "net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
233 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
236 "net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
237 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
240 "net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
241 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
244 "net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
245 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
248 "net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
256 * Returns the end of the source code.
258 private final String copyRemainingSource() {
259 char str[] = scanner.source;
260 int startPosition = scanner.startPosition;
261 int length = str.length - startPosition;
262 StringBuffer bufr = new StringBuffer(length);
263 if (startPosition < str.length) {
264 bufr.append(str, startPosition, length);
266 return (bufr.toString());
270 * Inserts <code>tabCount</code> tab character or their equivalent number
273 private void dumpTab(int tabCount) {
274 if (options.indentWithTab) {
275 for (int j = 0; j < tabCount; j++) {
276 formattedSource.append('\t');
277 increaseSplitDelta(1);
280 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
281 formattedSource.append(' ');
282 increaseSplitDelta(1);
288 * Dumps <code>currentLineBuffer</code> into the formatted string.
290 private void flushBuffer() {
291 String currentString = currentLineBuffer.toString();
293 beginningOfLineIndex = formattedSource.length();
294 if (containsOpenCloseBraces) {
295 containsOpenCloseBraces = false;
296 outputLine(currentString,
298 indentationLevelForOpenCloseBraces,
301 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
303 outputLine(currentString,
305 currentLineIndentationLevel,
309 int scannerSourceLength = scanner.source.length;
310 if ((scannerSourceLength > 2)
311 && (scanner.startPosition < scannerSourceLength)) {
312 if (scanner.source[scannerSourceLength - 1] == '\n'
313 && scanner.source[scannerSourceLength - 2] == '\r') {
314 formattedSource.append(options.lineSeparatorSequence);
315 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
316 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
317 formattedSource.append(options.lineSeparatorSequence);
318 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
319 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
320 formattedSource.append(options.lineSeparatorSequence);
321 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
324 updateMappedPositions(scanner.startPosition);
328 * Formats the input string.
330 private void format() {
331 TokenName token = TokenName.NONE;
332 TokenName previousToken = TokenName.NONE;
333 TokenName previousCompilableToken = TokenName.NONE;
335 int indentationOffset = 0;
336 int newLinesInWhitespace = 0;
337 // number of new lines in the previous whitespace token
338 // (used to leave blank lines before comments)
339 int pendingNewLines = 0;
340 boolean expectingOpenBrace = false;
341 boolean clearNonBlockIndents = false;
342 // true if all indentations till the 1st { (usefull after } or ;)
343 boolean pendingSpace = true;
344 boolean pendingNewlineAfterParen = false;
345 // true when a cr is to be put after a ) (in conditional statements)
346 boolean inAssignment = false;
347 boolean inArrayAssignment = false;
348 boolean inThrowsClause = false;
349 boolean inClassOrInterfaceHeader = false;
350 int dollarBraceCount = 0;
351 // openBracketCount is used to count the number of open brackets not
354 int openBracketCount = 0;
355 int unarySignModifier = 0;
356 // openParenthesis[0] is used to count the parenthesis not belonging to
359 // (eg foo();). parenthesis in for (...) are count elsewhere in the
361 int openParenthesisCount = 1;
362 int[] openParenthesis = new int[10];
363 // tokenBeforeColon is used to know what token goes along with the
366 // it can be case or ?
367 int tokenBeforeColonCount = 0;
368 TokenName[] tokenBeforeColon = new TokenName[10];
369 constructionsCount = 0; // initializes the constructions count.
370 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
371 TokenName nlicsToken = ITerminalSymbols.TokenName.NONE;
372 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and
374 boolean specialElse = false;
375 // OPTION (IndentationLevel): initial indentation level may be non-zero.
376 currentLineIndentationLevel += constructionsCount;
377 // An InvalidInputException exception might cause the termination of
380 int arrayDeclarationCount = 0;
381 int[] arrayDeclarationParenthesis = new int[10];
384 // Get the next token. Catch invalid input and output it
385 // with minimal formatting, also catch end of input and
388 token = scanner.getNextToken();
390 int currentEndPosition = scanner
391 .getCurrentTokenEndPosition();
392 int currentStartPosition = scanner
393 .getCurrentTokenStartPosition();
394 System.out.print(currentStartPosition + ","
395 + currentEndPosition + ": ");
396 System.out.println(scanner.toStringAction(token));
398 // Patch for line comment
399 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
400 if (token == ITerminalSymbols.TokenName.COMMENT_LINE) {
401 int length = scanner.currentPosition;
402 loop: for (int index = length - 1; index >= 0; index--) {
403 switch (scanner.source[index]) {
406 scanner.currentPosition--;
413 } catch (InvalidInputException e) {
414 if (!handleInvalidToken(e)) {
417 token = ITerminalSymbols.TokenName.NONE;
419 if (token == Scanner.TokenName.EOF) {
421 } else if (token == Scanner.TokenName.HEREDOC) {
422 // no indentation for heredocs and HTML !
423 outputCurrentTokenWithoutIndent(Scanner.TokenName.HEREDOC, 0);
425 } else if (token == Scanner.TokenName.INLINE_HTML) {
426 // no indentation for heredocs and HTML !
427 int newLineCount = 1;
428 if (scanner.startPosition == 0) {
431 outputCurrentTokenWithoutIndent(
432 Scanner.TokenName.INLINE_HTML, newLineCount);
433 int srcLen = scanner.source.length;
434 if (scanner.currentPosition < srcLen - 1) {
440 * ## MODIFYING the indentation level before generating new
441 * lines and indentation in the output string
443 // Removes all the indentations made by statements not followed
446 // except if the current token is ELSE, CATCH or if we are in a
448 if (clearNonBlockIndents
449 && (token != Scanner.TokenName.WHITESPACE)) {
452 if (constructionsCount > 0
453 && constructions[constructionsCount - 1] == TokenName.ELSE) {
457 indentationLevel += popInclusiveUntil(TokenName.IF);
459 // case TokenName.catch :
460 // indentationLevel += popInclusiveUntil(TokenName.catch);
462 // case TokenName.finally :
463 // indentationLevel += popInclusiveUntil(TokenName.catch);
466 if (nlicsToken == TokenName.DO) {
467 indentationLevel += pop(TokenName.DO);
471 indentationLevel += popExclusiveUntilBlockOrCase();
472 // clear until a CASE, DEFAULT or BLOCK is encountered.
473 // Thus, the indentationLevel is correctly cleared
475 // in a switch/case statement or in any other situation.
477 clearNonBlockIndents = false;
479 // returns to the indentation level created by the SWITCH
481 // if the current token is a CASE or a DEFAULT
482 if (token == TokenName.CASE || token == TokenName.DEFAULT) {
483 indentationLevel += pop(TokenName.CASE);
485 // if (token == Scanner.TokenName.throws) {
486 // inThrowsClause = true;
488 if ((token == Scanner.TokenName.CLASS || token == Scanner.TokenName.INTERFACE)
489 && previousToken != Scanner.TokenName.DOT) {
490 inClassOrInterfaceHeader = true;
493 * ## APPEND newlines and indentations to the output string
495 // Do not add a new line between ELSE and IF, if the option
496 // elseIfOnSameLine is true.
497 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
498 // if (pendingNewlineAfterParen
499 // && previousCompilableToken == TokenName.else
500 // && token == TokenName.if
501 // && options.compactElseIfMode) {
502 // pendingNewlineAfterParen = false;
503 // pendingNewLines = 0;
504 // indentationLevel += pop(TokenName.else);
505 // // because else if is now one single statement,
506 // // the indentation level after it is increased by one and not
508 // // (else = 1 indent, if = 1 indent, but else if = 1 indent,
511 // Add a newline & indent to the formatted source string if
512 // a for/if-else/while statement was scanned and there is no
515 pendingNewlineAfterParen = pendingNewlineAfterParen
516 || (previousCompilableToken == TokenName.RPAREN && token == TokenName.LBRACE);
517 if (pendingNewlineAfterParen
518 && token != Scanner.TokenName.WHITESPACE) {
519 pendingNewlineAfterParen = false;
520 // Do to add a newline & indent sequence if the current
522 // open brace or a period or if the current token is a
525 // previous token is a close paren.
526 // add a new line if a parenthesis belonging to a for()
528 // has been closed and the current token is not an opening
530 if (token != TokenName.LBRACE && !isComment(token)
531 // to avoid adding new line between else and a
533 && token != TokenName.DOT
534 && !(previousCompilableToken == TokenName.RPAREN && token == TokenName.SEMICOLON)) {
536 currentLineIndentationLevel = indentationLevel;
538 pendingSpace = false;
540 if (token == TokenName.LBRACE
541 && options.newLineBeforeOpeningBraceMode) {
543 if (constructionsCount > 0
544 && constructions[constructionsCount - 1] != BLOCK
545 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
546 currentLineIndentationLevel = indentationLevel - 1;
548 currentLineIndentationLevel = indentationLevel;
551 pendingSpace = false;
555 if (token == TokenName.LBRACE
556 && options.newLineBeforeOpeningBraceMode
557 && constructionsCount > 0
558 && constructions[constructionsCount - 1] == TokenName.DO) {
560 currentLineIndentationLevel = indentationLevel - 1;
562 pendingSpace = false;
565 if (token == TokenName.LBRACE && inThrowsClause) {
566 inThrowsClause = false;
567 if (options.newLineBeforeOpeningBraceMode) {
569 currentLineIndentationLevel = indentationLevel;
571 pendingSpace = false;
575 if (token == TokenName.LBRACE && inClassOrInterfaceHeader) {
576 inClassOrInterfaceHeader = false;
577 if (options.newLineBeforeOpeningBraceMode) {
579 currentLineIndentationLevel = indentationLevel;
581 pendingSpace = false;
584 // don't linebreak empty array declarations
585 if (token == TokenName.RPAREN && arrayDeclarationCount > 0) {
586 if (previousCompilableToken == TokenName.LPAREN) {
590 // Add pending new lines to the formatted source string.
591 // Note: pending new lines are not added if the current token
592 // is a single line comment or whitespace.
593 // if the comment is between parenthesis, there is no blank line
595 // (if it's a one-line comment, a blank line is added after it).
596 if (((pendingNewLines > 0 && (!isComment(token)))
597 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
598 || (previousCompilableToken == TokenName.LBRACE && token == TokenName.RBRACE) || (newLinesInWhitespace > 0 && previousCompilableToken == TokenName.DOT))
599 && token != Scanner.TokenName.WHITESPACE) {
600 // Do not add newline & indent between an adjoining close
602 // close paren. Anonymous inner classes may use this form.
603 boolean closeBraceAndCloseParen = previousToken == TokenName.RBRACE
604 && token == TokenName.RPAREN;
605 // OPTION (NewLineInCompoundStatement): do not add newline &
607 // between close brace and else, (do) while, catch, and
609 // newlineInCompoundStatement is true.
610 boolean nlicsOption = previousToken == TokenName.RBRACE
611 && !options.newlineInControlStatementMode
612 && (token == TokenName.ELSE
613 || (token == TokenName.WHILE && nlicsToken == TokenName.DO)
614 || token == TokenName.CATCH || token == TokenName.FINALLY);
615 // Do not add a newline & indent between a close brace and
617 boolean semiColonAndCloseBrace = previousToken == TokenName.RBRACE
618 && token == TokenName.SEMICOLON;
619 // Do not add a new line & indent between a multiline
622 boolean commentAndOpenBrace = previousToken == Scanner.TokenName.COMMENT_BLOCK
623 && token == TokenName.LBRACE;
624 // Do not add a newline & indent between a close brace and a
626 // (in array assignments, for example).
627 boolean commaAndCloseBrace = previousToken == TokenName.RBRACE
628 && token == TokenName.COMMA;
629 // Add a newline and indent, if appropriate.
631 || (!commentAndOpenBrace
632 && !closeBraceAndCloseParen && !nlicsOption
633 && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
634 // if clearAllBlankLinesMode=false, leaves the blank
636 // inserted by the user
637 // if clearAllBlankLinesMode=true, removes all of then
638 // and insert only blank lines required by the
640 if (!options.clearAllBlankLinesMode) {
641 // (isComment(token))
642 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace
644 pendingNewLines = (pendingNewLines > 2) ? 2
647 if (previousCompilableToken == TokenName.LBRACE
648 && token == TokenName.RBRACE) {
649 containsOpenCloseBraces = true;
650 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
651 if (isComment(previousToken)) {
652 newLine(pendingNewLines);
655 * if (!(constructionsCount > 1 &&
656 * constructions[constructionsCount-1] ==
658 * (constructions[constructionsCount-2] ==
661 if (options.newLineInEmptyBlockMode) {
662 if (inArrayAssignment) {
663 newLine(1); // array assigment with an
666 newLine(pendingNewLines);
672 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with
675 if (!((previousToken == Scanner.TokenName.COMMENT_BLOCK || previousToken == Scanner.TokenName.COMMENT_PHPDOC) && token == TokenName.SEMICOLON)) {
676 newLine(pendingNewLines);
679 if (((previousCompilableToken == TokenName.SEMICOLON)
680 || (previousCompilableToken == TokenName.LBRACE)
681 || (previousCompilableToken == TokenName.RBRACE) || (isComment(previousToken)))
682 && (token == TokenName.RBRACE)) {
683 indentationOffset = -1;
684 indentationLevel += popExclusiveUntilBlock();
686 if (previousToken == Scanner.TokenName.COMMENT_LINE
689 currentLineIndentationLevel++;
691 currentLineIndentationLevel = indentationLevel
694 pendingSpace = false;
695 indentationOffset = 0;
698 newLinesInWhitespace = 0;
700 if (nlicsToken == TokenName.DO && token == TokenName.WHILE) {
701 nlicsToken = ITerminalSymbols.TokenName.NONE;
704 boolean phpTagAndWhitespace = previousToken == TokenName.INLINE_HTML
705 && token == TokenName.WHITESPACE;
707 // case TokenName.DOLLAR :
708 // dollarBraceCount++;
711 // case TokenName.finally :
712 expectingOpenBrace = true;
713 pendingNewlineAfterParen = true;
714 indentationLevel += pushControlStatement(token);
718 if (tokenBeforeColonCount == tokenBeforeColon.length) {
723 (tokenBeforeColon = new TokenName[tokenBeforeColonCount * 2]),
724 0, tokenBeforeColonCount);
726 tokenBeforeColon[tokenBeforeColonCount++] = TokenName.CASE;
727 indentationLevel += pushControlStatement(TokenName.CASE);
730 if (tokenBeforeColonCount == tokenBeforeColon.length) {
735 (tokenBeforeColon = new TokenName[tokenBeforeColonCount * 2]),
736 0, tokenBeforeColonCount);
738 tokenBeforeColon[tokenBeforeColonCount++] = token;
745 if (openParenthesisCount == openParenthesis.length) {
750 (openParenthesis = new int[openParenthesisCount * 2]),
751 0, openParenthesisCount);
753 openParenthesis[openParenthesisCount++] = 0;
754 expectingOpenBrace = true;
755 indentationLevel += pushControlStatement(token);
758 pendingNewlineAfterParen = true;
760 // several CATCH statements can be contiguous.
761 // a CATCH is encountered pop until first CATCH (if a CATCH
762 // follows a TRY it works the same way,
763 // as CATCH and TRY are the same token in the stack).
764 expectingOpenBrace = true;
765 indentationLevel += pushControlStatement(TokenName.CATCH);
768 expectingOpenBrace = true;
769 indentationLevel += pushControlStatement(token);
775 // if (previousToken == TokenName.synchronized) {
776 // indentationLevel += pushControlStatement(previousToken);
778 // Put a space between the previous and current token if the
779 // previous token was not a keyword, open paren, logical
780 // compliment (eg: !), semi-colon, open brace, close brace,
782 if (previousCompilableToken != TokenName.LBRACKET
783 && previousToken != TokenName.IDENTIFIER
784 && previousToken != TokenName.NONE
785 && previousToken != TokenName.NOT
786 && previousToken != TokenName.LPAREN
787 && previousToken != TokenName.TWIDDLE
788 && previousToken != TokenName.SEMICOLON
789 && previousToken != TokenName.LBRACE
790 && previousToken != TokenName.RBRACE
791 && previousToken != TokenName.SUPER) {
792 // && previousToken != TokenName.THIS) {
793 if (!options.compactArrays) {
797 // If in a for/if/while statement, increase the parenthesis
799 // for the current openParenthesisCount
800 // else increase the count for stand alone parenthesis.
801 if (openParenthesisCount > 0)
802 openParenthesis[openParenthesisCount - 1]++;
804 openParenthesis[0]++;
805 pendingSpace = false;
806 // recognize array declaration for nice output
807 if (previousCompilableToken == TokenName.ARRAY) {
808 arrayDeclarationCount++;
809 arrayDeclarationParenthesis[arrayDeclarationCount] = openParenthesis[openParenthesisCount - 1];
810 if (!options.compactArrays) {
818 // check for closing array declaration
819 if (arrayDeclarationCount > 0) {
820 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
821 if (previousCompilableToken != TokenName.LPAREN) {
822 if (!options.compactArrays) {
825 } else if (previousToken == TokenName.COMMENT_LINE
826 || previousToken == TokenName.COMMENT_BLOCK
827 || previousToken == TokenName.COMMENT_PHPDOC) {
828 // prevent to combine comment line and statement line (#1475484)
829 if (!options.compactArrays) {
833 if (!options.compactArrays) {
837 currentLineIndentationLevel = indentationLevel;
839 arrayDeclarationCount--;
842 // Decrease the parenthesis count
843 // if there is no more unclosed parenthesis,
844 // a new line and indent may be append (depending on the
847 if ((openParenthesisCount > 1)
848 && (openParenthesis[openParenthesisCount - 1] > 0)) {
849 openParenthesis[openParenthesisCount - 1]--;
850 if (openParenthesis[openParenthesisCount - 1] <= 0) {
851 pendingNewlineAfterParen = true;
852 inAssignment = false;
853 openParenthesisCount--;
856 openParenthesis[0]--;
858 pendingSpace = false;
861 if (previousCompilableToken == TokenName.DOLLAR) {
864 if ((previousCompilableToken == TokenName.RBRACKET)
865 || (previousCompilableToken == TokenName.EQUAL)) {
866 // if (previousCompilableToken == TokenName.RBRACKET)
868 inArrayAssignment = true;
869 inAssignment = false;
871 if (inArrayAssignment) {
872 indentationLevel += pushBlock();
874 // Add new line and increase indentation level after
877 indentationLevel += pushBlock();
878 inAssignment = false;
883 if (dollarBraceCount > 0) {
887 if (previousCompilableToken == TokenName.RPAREN) {
888 pendingSpace = false;
890 if (inArrayAssignment) {
891 inArrayAssignment = false;
893 indentationLevel += popInclusiveUntilBlock();
896 indentationLevel += popInclusiveUntilBlock();
897 if (previousCompilableToken == TokenName.RPAREN) {
898 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on
902 .append(options.lineSeparatorSequence);
903 increaseLineDelta(options.lineSeparatorSequence.length);
905 if (constructionsCount > 0) {
906 switch (constructions[constructionsCount - 1]) {
909 // indentationLevel += popExclusiveUntilBlock();
919 // case TokenName.synchronized :
920 clearNonBlockIndents = true;
929 pendingSpace = false;
932 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
933 // if there is no left bracket to close, the right bracket
936 pendingSpace = false;
939 pendingSpace = false;
940 if (arrayDeclarationCount > 0) {
941 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
942 // there is no left parenthesis to close in current array declaration (#1475484)
943 if (!options.compactArrays) {
950 if (!options.compactStringConcatenation) {
953 pendingSpace = false;
956 // Do not generate line terminators in the definition of
957 // the for statement.
958 // if not in this case, jump a line and reduce indentation
961 // if the block it closes belongs to a conditional statement
964 if (openParenthesisCount <= 1) {
966 if (expectingOpenBrace) {
967 clearNonBlockIndents = true;
968 expectingOpenBrace = false;
971 inAssignment = false;
972 pendingSpace = false;
976 // Do not put a space between a post-increment/decrement
977 // and the identifier being modified.
978 if (previousToken == TokenName.IDENTIFIER
979 || previousToken == TokenName.RBRACKET
980 || previousToken == TokenName.VARIABLE) {
981 pendingSpace = false;
985 // previously ADDITION
987 // Handle the unary operators plus and minus via a flag
988 if (!isLiteralToken(previousToken)
989 && previousToken != TokenName.IDENTIFIER
990 && previousToken != TokenName.RPAREN
991 && previousToken != TokenName.RBRACKET) {
992 unarySignModifier = 1;
996 // In a switch/case statement, add a newline & indent
997 // when a colon is encountered.
998 if (tokenBeforeColonCount > 0) {
999 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenName.CASE) {
1000 pendingNewLines = 1;
1002 tokenBeforeColonCount--;
1006 inAssignment = true;
1009 pendingNewLines = 1;
1010 //if (inAssignment) {
1011 // currentLineIndentationLevel++;
1013 break; // a line is always inserted after a one-line
1015 case COMMENT_PHPDOC:
1017 currentCommentOffset = getCurrentCommentOffset();
1018 pendingNewLines = 1;
1021 if (!phpTagAndWhitespace) {
1022 // Count the number of line terminators in the
1024 // line spacing can be preserved near comments.
1025 char[] source = scanner.source;
1026 newLinesInWhitespace = 0;
1027 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
1028 if (source[i] == '\r') {
1030 if (source[++i] == '\n') {
1031 newLinesInWhitespace++;
1033 newLinesInWhitespace++;
1036 newLinesInWhitespace++;
1038 } else if (source[i] == '\n') {
1039 newLinesInWhitespace++;
1042 increaseLineDelta(scanner.startPosition
1043 - scanner.currentPosition);
1046 // case TokenName.HTML :
1047 // // Add the next token to the formatted source string.
1048 // // outputCurrentToken(token);
1049 // int startPosition = scanner.startPosition;
1051 // for (int i = startPosition, max =
1052 // scanner.currentPosition; i <
1054 // char currentCharacter = scanner.source[i];
1055 // updateMappedPositions(i);
1056 // currentLineBuffer.append(currentCharacter);
1060 if ((token == TokenName.IDENTIFIER) || isLiteralToken(token)
1061 || token == TokenName.SUPER) {
1062 // || token == TokenName.this) {
1063 // Do not put a space between a unary operator
1064 // (eg: ++, --, +, -) and the identifier being modified.
1065 if (previousToken == TokenName.PLUS_PLUS
1066 || previousToken == TokenName.MINUS_MINUS
1067 || (previousToken == TokenName.MINUS_GREATER && options.compactDereferencingMode) // ->
1068 || (previousToken == TokenName.PLUS && unarySignModifier > 0)
1069 || (previousToken == TokenName.MINUS && unarySignModifier > 0)) {
1070 pendingSpace = false;
1072 unarySignModifier = 0;
1076 // Do not output whitespace tokens.
1077 if (token != Scanner.TokenName.WHITESPACE || phpTagAndWhitespace) {
1079 * Add pending space to the formatted source string. Do not
1080 * output a space under the following circumstances: 1) this
1081 * is the first pass 2) previous token is an open paren 3)
1082 * previous token is a period 4) previous token is the
1083 * logical compliment (eg: !) 5) previous token is the
1084 * bitwise compliment (eg: ~) 6) previous token is the open
1085 * bracket (eg: [) 7) in an assignment statement, if the
1086 * previous token is an open brace or the current token is a
1087 * close brace 8) previous token is a single line comment 9)
1088 * current token is a '->'
1090 if (token == TokenName.MINUS_GREATER
1091 && options.compactDereferencingMode)
1092 pendingSpace = false;
1094 boolean openAndCloseBrace = previousCompilableToken == TokenName.LBRACE
1095 && token == TokenName.RBRACE;
1097 && insertSpaceAfter(previousToken)
1098 && !(inAssignment && (previousToken == TokenName.LBRACE || token == TokenName.RBRACE))
1099 && previousToken != Scanner.TokenName.COMMENT_LINE) {
1100 if ((!(options.compactAssignmentMode && token == TokenName.EQUAL))
1101 && !openAndCloseBrace)
1104 // Add the next token to the formatted source string.
1105 outputCurrentToken(token);
1106 if (token == Scanner.TokenName.COMMENT_LINE
1107 && openParenthesisCount > 1) {
1108 pendingNewLines = 0;
1109 currentLineBuffer.append(options.lineSeparatorSequence);
1110 increaseLineDelta(options.lineSeparatorSequence.length);
1112 pendingSpace = true;
1114 // Whitespace tokens do not need to be remembered.
1115 if (token != Scanner.TokenName.WHITESPACE || phpTagAndWhitespace) {
1116 previousToken = token;
1117 if (token != Scanner.TokenName.COMMENT_BLOCK
1118 && token != Scanner.TokenName.COMMENT_LINE
1119 && token != Scanner.TokenName.COMMENT_PHPDOC) {
1120 previousCompilableToken = token;
1124 output(copyRemainingSource());
1126 // dump the last token of the source in the formatted output.
1127 } catch (InvalidInputException e) {
1128 output(copyRemainingSource());
1130 // dump the last token of the source in the formatted output.
1135 * Formats the char array <code>sourceString</code>, and returns a string
1136 * containing the formatted version.
1138 * @return the formatted ouput.
1140 public String formatSourceString(String sourceString) {
1141 char[] sourceChars = sourceString.toCharArray();
1142 formattedSource = new StringBuffer(sourceChars.length);
1143 scanner.setSource(sourceChars);
1145 return formattedSource.toString();
1149 * Formats the char array <code>sourceString</code>, and returns a string
1150 * containing the formatted version.
1153 * the string to format
1154 * @param indentationLevel
1155 * the initial indentation level
1156 * @return the formatted ouput.
1158 public String format(String string, int indentationLevel) {
1159 return format(string, indentationLevel, (int[]) null);
1163 * Formats the char array <code>sourceString</code>, and returns a string
1164 * containing the formatted version. The positions array is modified to
1165 * contain the mapped positions.
1168 * the string to format
1169 * @param indentationLevel
1170 * the initial indentation level
1172 * the array of positions to map
1173 * @return the formatted ouput.
1175 public String format(String string, int indentationLevel, int[] positions) {
1176 return this.format(string, indentationLevel, positions, null);
1179 public String format(String string, int indentationLevel, int[] positions,
1180 String lineSeparator) {
1181 if (lineSeparator != null) {
1182 this.options.setLineSeparator(lineSeparator);
1184 if (positions != null) {
1185 this.setPositionsToMap(positions);
1186 this.setInitialIndentationLevel(indentationLevel);
1187 String formattedString = this.formatSourceString(string);
1188 int[] mappedPositions = this.getMappedPositions();
1190 .arraycopy(mappedPositions, 0, positions, 0,
1192 return formattedString;
1194 this.setInitialIndentationLevel(indentationLevel);
1195 return this.formatSourceString(string);
1200 * Formats the char array <code>sourceString</code>, and returns a string
1201 * containing the formatted version. The initial indentation level is 0.
1204 * the string to format
1205 * @return the formatted ouput.
1207 public String format(String string) {
1208 return this.format(string, 0, (int[]) null);
1212 * Formats a given source string, starting indenting it at a particular
1213 * depth and using the given options
1215 * @deprecated backport 1.0 internal functionality
1217 public static String format(String sourceString,
1218 int initialIndentationLevel, ConfigurableOption[] options) {
1219 CodeFormatter formatter = new CodeFormatter(options);
1220 formatter.setInitialIndentationLevel(initialIndentationLevel);
1221 return formatter.formatSourceString(sourceString);
1225 * Returns the number of characters and tab char between the beginning of
1226 * the line and the beginning of the comment.
1228 private int getCurrentCommentOffset() {
1229 int linePtr = scanner.linePtr;
1230 // if there is no beginning of line, return 0.
1234 int beginningOfLine = scanner.lineEnds[linePtr];
1235 int currentStartPosition = scanner.startPosition;
1236 char[] source = scanner.source;
1237 // find the position of the beginning of the line containing the comment
1238 while (beginningOfLine > currentStartPosition) {
1240 beginningOfLine = scanner.lineEnds[--linePtr];
1242 beginningOfLine = 0;
1246 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1247 char currentCharacter = source[i];
1248 switch (currentCharacter) {
1250 offset += options.tabSize;
1266 * Returns an array of descriptions for the configurable options. The
1267 * descriptions may be changed and passed back to a different compiler.
1269 * @deprecated backport 1.0 internal functionality
1271 // public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1272 // String componentName = CodeFormatter.class.getName();
1273 // FormatterOptions options = new FormatterOptions();
1274 // return new ConfigurableOption[] {
1275 // new ConfigurableOption(componentName, "newline.openingBrace",
1276 // locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1278 // new ConfigurableOption(componentName,
1279 // "newline.controlStatement", locale,
1280 // options.newlineInControlStatementMode ? 0 : 1),
1282 // new ConfigurableOption(componentName, "newline.clearAll",
1283 // locale, options.clearAllBlankLinesMode ? 0 : 1),
1285 // // new ConfigurableOption(componentName, "newline.elseIf",
1287 // // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1288 // new ConfigurableOption(componentName, "newline.emptyBlock",
1289 // locale, options.newLineInEmptyBlockMode ? 0 : 1),
1291 // new ConfigurableOption(componentName, "line.split", locale,
1292 // options.maxLineLength),
1294 // new ConfigurableOption(componentName,
1295 // "style.compactAssignment", locale,
1296 // options.compactAssignmentMode ? 0 : 1),
1298 // new ConfigurableOption(componentName, "tabulation.char",
1299 // locale, options.indentWithTab ? 0 : 1),
1301 // new ConfigurableOption(componentName,
1302 // "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1307 * Returns the array of mapped positions. Returns null is no positions have
1311 * @deprecated There is no need to retrieve the mapped positions anymore.
1313 public int[] getMappedPositions() {
1314 if (null != mappedPositions) {
1315 for (int i = 0; i < mappedPositions.length; i++) {
1316 if (mappedPositions[i] >= formattedSource.length()) {
1317 mappedPositions[i] = formattedSource.length() - 1;
1321 return mappedPositions;
1325 * Returns the priority of the token given as argument <br>
1326 * The most prioritary the token is, the smallest the return value is.
1328 * @return the priority of <code>token</code>
1330 * the token of which the priority is requested
1332 // private static int getTokenPriority(int token) {
1334 // case TokenName.extends:
1335 // // case TokenName.implements :
1336 // // case TokenName.throws :
1338 // case TokenName.SEMICOLON:
1341 // case TokenName.COMMA:
1344 // case TokenName.EQUAL:
1347 // case TokenName.AND_AND:
1349 // case TokenName.OR_OR:
1352 // case TokenName.QUESTION:
1354 // case TokenName.COLON:
1356 // return 50; // it's better cutting on ?: than on ;
1357 // case TokenName.EQUAL_EQUAL:
1359 // case TokenName.EQUAL_EQUAL_EQUAL:
1361 // case TokenName.NOT_EQUAL:
1363 // case TokenName.NOT_EQUAL_EQUAL:
1366 // case TokenName.LESS:
1368 // case TokenName.LESS_EQUAL:
1370 // case TokenName.GREATER:
1372 // case TokenName.GREATER_EQUAL:
1374 // // case TokenName.instanceof : // instanceof
1376 // case TokenName.PLUS:
1378 // case TokenName.MINUS:
1381 // case TokenName.MULTIPLY:
1383 // case TokenName.DIVIDE:
1385 // case TokenName.REMAINDER:
1388 // case TokenName.LEFT_SHIFT:
1390 // case TokenName.RIGHT_SHIFT:
1392 // // case TokenName.UNSIGNED_RIGHT_SHIFT : // >>>
1394 // case TokenName.AND:
1396 // case TokenName.OR:
1398 // case TokenName.XOR:
1401 // case TokenName.MULTIPLY_EQUAL:
1403 // case TokenName.DIVIDE_EQUAL:
1405 // case TokenName.REMAINDER_EQUAL:
1407 // case TokenName.PLUS_EQUAL:
1409 // case TokenName.MINUS_EQUAL:
1411 // case TokenName.LEFT_SHIFT_EQUAL:
1413 // case TokenName.RIGHT_SHIFT_EQUAL:
1415 // // case TokenName.UNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1416 // case TokenName.AND_EQUAL:
1418 // case TokenName.XOR_EQUAL:
1420 // case TokenName.OR_EQUAL:
1422 // case TokenName.DOT_EQUAL:
1425 // case TokenName.DOT:
1429 // return Integer.MAX_VALUE;
1434 * Handles the exception raised when an invalid token is encountered.
1435 * Returns true if the exception has been handled, false otherwise.
1437 private boolean handleInvalidToken(Exception e) {
1438 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1439 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1440 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1446 private final void increaseGlobalDelta(int offset) {
1447 globalDelta += offset;
1450 private final void increaseLineDelta(int offset) {
1451 lineDelta += offset;
1454 private final void increaseSplitDelta(int offset) {
1455 splitDelta += offset;
1459 * Returns true if a space has to be inserted after <code>operator</code>
1462 private boolean insertSpaceAfter(TokenName token) {
1475 return !options.compactStringConcatenation;
1482 * Returns true if a space has to be inserted before <code>operator</code>
1483 * false otherwise. <br>
1484 * Cannot be static as it uses the code formatter options (to know if the
1485 * compact assignment mode is on).
1487 private boolean insertSpaceBefore(TokenName token) {
1490 return (!options.compactAssignmentMode);
1496 private static boolean isComment(TokenName token) {
1497 boolean result = token == Scanner.TokenName.COMMENT_BLOCK
1498 || token == Scanner.TokenName.COMMENT_LINE
1499 || token == Scanner.TokenName.COMMENT_PHPDOC;
1503 private static boolean isLiteralToken(TokenName token) {
1504 boolean result = token == TokenName.INTEGERLITERAL
1505 // || token == TokenName.LongLiteral
1506 // || token == TokenName.FloatingPointLiteral
1507 || token == TokenName.DOUBLELITERAL
1508 // || token == TokenName.CharacterLiteral
1509 || token == TokenName.STRINGDOUBLEQUOTE;
1514 * If the length of <code>oneLineBuffer</code> exceeds
1515 * <code>maxLineLength</code>, it is split and the result is dumped in
1516 * <code>formattedSource</code>
1518 * @param newLineCount
1519 * the number of new lines to append
1521 private void newLine(int newLineCount) {
1522 // format current line
1524 beginningOfLineIndex = formattedSource.length();
1525 String currentLine = currentLineBuffer.toString();
1526 if (containsOpenCloseBraces) {
1527 containsOpenCloseBraces = false;
1528 outputLine(currentLine, false, indentationLevelForOpenCloseBraces,
1529 TokenName.NONE, -1, null, 0);
1530 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1532 outputLine(currentLine, false, currentLineIndentationLevel, TokenName.NONE, -1,
1535 // dump line break(s)
1536 for (int i = 0; i < newLineCount; i++) {
1537 formattedSource.append(options.lineSeparatorSequence);
1538 increaseSplitDelta(options.lineSeparatorSequence.length);
1540 // reset formatter for next line
1541 int currentLength = currentLine.length();
1542 currentLineBuffer = new StringBuffer(
1543 currentLength > maxLineSize ? maxLineSize = currentLength
1545 increaseGlobalDelta(splitDelta);
1546 increaseGlobalDelta(lineDelta);
1548 currentLineIndentationLevel = initialIndentationLevel;
1551 private String operatorString(TokenName operator) {
1554 return "extends"; //$NON-NLS-1$
1555 // case TokenName.implements :
1556 // return "implements"; //$NON-NLS-1$
1558 // case TokenName.throws :
1559 // return "throws"; //$NON-NLS-1$
1562 return ";"; //$NON-NLS-1$
1565 return ","; //$NON-NLS-1$
1568 return "="; //$NON-NLS-1$
1571 return "&&"; //$NON-NLS-1$
1574 return "||"; //$NON-NLS-1$
1577 return "?"; //$NON-NLS-1$
1580 return ":"; //$NON-NLS-1$
1581 case PAAMAYIM_NEKUDOTAYIM:
1583 return "::"; //$NON-NLS-1$
1585 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1586 return "=="; //$NON-NLS-1$
1587 case EQUAL_EQUAL_EQUAL:
1588 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1589 return "==="; //$NON-NLS-1$
1592 return "=>"; //$NON-NLS-1$
1594 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1595 return "!="; //$NON-NLS-1$
1596 case NOT_EQUAL_EQUAL:
1597 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1598 return "!=="; //$NON-NLS-1$
1601 return "<"; //$NON-NLS-1$
1604 return "<="; //$NON-NLS-1$
1607 return ">"; //$NON-NLS-1$
1610 return ">="; //$NON-NLS-1$
1611 // case instanceof : // instanceof
1612 // return "instanceof"; //$NON-NLS-1$
1614 // + (15.17, 15.17.2)
1615 return "+"; //$NON-NLS-1$
1618 return "-"; //$NON-NLS-1$
1621 return "*"; //$NON-NLS-1$
1624 return "/"; //$NON-NLS-1$
1627 return "%"; //$NON-NLS-1$
1630 return "<<"; //$NON-NLS-1$
1633 return ">>"; //$NON-NLS-1$
1634 // case UNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1635 // return ">>>"; //$NON-NLS-1$
1637 // & (15.21, 15.21.1, 15.21.2)
1638 return "&"; //$NON-NLS-1$
1640 // | (15.21, 15.21.1, 15.21.2)
1641 return "|"; //$NON-NLS-1$
1643 // ^ (15.21, 15.21.1, 15.21.2)
1644 return "^"; //$NON-NLS-1$
1645 case MULTIPLY_EQUAL:
1647 return "*="; //$NON-NLS-1$
1650 return "/="; //$NON-NLS-1$
1651 case REMAINDER_EQUAL:
1653 return "%="; //$NON-NLS-1$
1656 return "+="; //$NON-NLS-1$
1659 return "-="; //$NON-NLS-1$
1662 return "->"; //$NON-NLS-1$
1663 case LEFT_SHIFT_EQUAL:
1665 return "<<="; //$NON-NLS-1$
1666 case RIGHT_SHIFT_EQUAL:
1668 return ">>="; //$NON-NLS-1$
1669 // case UNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1670 // return ">>>="; //$NON-NLS-1$
1673 return "&="; //$NON-NLS-1$
1676 return "^="; //$NON-NLS-1$
1679 return "|="; //$NON-NLS-1$
1682 return ".="; //$NON-NLS-1$
1685 return "."; //$NON-NLS-1$
1687 return ""; //$NON-NLS-1$
1692 * Appends <code>stringToOutput</code> to the formatted output. <br>
1693 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1695 private void output(String stringToOutput) {
1696 char currentCharacter;
1697 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1698 currentCharacter = stringToOutput.charAt(i);
1699 if (currentCharacter != '\t') {
1700 currentLineBuffer.append(currentCharacter);
1705 private void outputCurrentTokenWithoutIndent(TokenName heredoc, int newLineCount) {
1706 newLine(newLineCount);
1707 formattedSource.append(scanner.source, scanner.startPosition,
1708 scanner.currentPosition - scanner.startPosition);
1712 * Appends <code>token</code> to the formatted output. <br>
1713 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1716 private void outputCurrentToken(TokenName token) {
1717 char[] source = scanner.source;
1718 int startPosition = scanner.startPosition;
1720 case COMMENT_PHPDOC:
1723 boolean endOfLine = false;
1724 int currentCommentOffset = getCurrentCommentOffset();
1725 int beginningOfLineSpaces = 0;
1727 currentCommentOffset = getCurrentCommentOffset();
1728 beginningOfLineSpaces = 0;
1729 boolean pendingCarriageReturn = false;
1730 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1731 char currentCharacter = source[i];
1732 updateMappedPositions(i);
1733 switch (currentCharacter) {
1735 pendingCarriageReturn = true;
1739 if (pendingCarriageReturn) {
1740 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1742 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1744 pendingCarriageReturn = false;
1745 currentLineBuffer.append(options.lineSeparatorSequence);
1746 beginningOfLineSpaces = 0;
1750 if (pendingCarriageReturn) {
1751 pendingCarriageReturn = false;
1752 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1753 currentLineBuffer.append(options.lineSeparatorSequence);
1754 beginningOfLineSpaces = 0;
1758 // we remove a maximum of currentCommentOffset
1760 // are converted to space numbers).
1761 beginningOfLineSpaces += options.tabSize;
1762 if (beginningOfLineSpaces > currentCommentOffset) {
1763 currentLineBuffer.append(currentCharacter);
1765 increaseGlobalDelta(-1);
1768 currentLineBuffer.append(currentCharacter);
1772 if (pendingCarriageReturn) {
1773 pendingCarriageReturn = false;
1774 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1775 currentLineBuffer.append(options.lineSeparatorSequence);
1776 beginningOfLineSpaces = 0;
1780 // we remove a maximum of currentCommentOffset
1782 // are converted to space numbers).
1783 beginningOfLineSpaces++;
1784 if (beginningOfLineSpaces > currentCommentOffset) {
1785 currentLineBuffer.append(currentCharacter);
1787 increaseGlobalDelta(-1);
1790 currentLineBuffer.append(currentCharacter);
1794 if (pendingCarriageReturn) {
1795 pendingCarriageReturn = false;
1796 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1797 currentLineBuffer.append(options.lineSeparatorSequence);
1798 beginningOfLineSpaces = 0;
1801 beginningOfLineSpaces = 0;
1802 currentLineBuffer.append(currentCharacter);
1807 updateMappedPositions(scanner.currentPosition - 1);
1808 multipleLineCommentCounter++;
1811 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1812 char currentCharacter = source[i];
1813 updateMappedPositions(i);
1814 currentLineBuffer.append(currentCharacter);
1820 * Outputs <code>currentString</code>:<br>
1822 * <li>If its length is < maxLineLength, output
1823 * <li>Otherwise it is split.
1826 * @param currentString
1828 * @param preIndented
1829 * whether the string to output was pre-indented
1831 * number of indentation to put in front of
1832 * <code>currentString</code>
1834 * value of the operator belonging to <code>currentString</code>.
1836 private void outputLine(String currentString, boolean preIndented,
1837 int depth, TokenName operator, int substringIndex,
1838 int[] startSubstringIndexes, int offsetInGlobalLine) {
1839 boolean emptyFirstSubString = false;
1840 String operatorString = operatorString(operator);
1841 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1842 boolean placeOperatorAhead = !placeOperatorBehind;
1843 // dump prefix operator?
1844 if (placeOperatorAhead) {
1849 if (operator.compareTo (TokenName.NONE) > 0) {
1850 if (insertSpaceBefore(operator)) {
1851 formattedSource.append(' ');
1852 increaseSplitDelta(1);
1854 formattedSource.append(operatorString);
1855 increaseSplitDelta(operatorString.length());
1856 if (insertSpaceAfter(operator)
1857 && operator != TokenName.IMPLEMENTS
1858 && operator != TokenName.EXTENDS) {
1859 // && operator != TokenName.throws) {
1860 formattedSource.append(' ');
1861 increaseSplitDelta(1);
1865 SplitLine splitLine = null;
1866 if (options.maxLineLength == 0
1867 || getLength(currentString, depth) < options.maxLineLength
1868 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1869 // depending on the type of operator, outputs new line before of
1872 // indent before postfix operator
1873 // indent also when the line cannot be split
1874 if (operator == TokenName.EXTENDS || operator == TokenName.IMPLEMENTS) {
1875 // || operator == TokenName.throws) {
1876 formattedSource.append(' ');
1877 increaseSplitDelta(1);
1879 if (placeOperatorBehind) {
1884 int max = currentString.length();
1885 if (multipleLineCommentCounter != 0) {
1887 BufferedReader reader = new BufferedReader(
1888 new StringReader(currentString));
1889 String line = reader.readLine();
1890 while (line != null) {
1891 updateMappedPositionsWhileSplitting(
1892 beginningOfLineIndex, beginningOfLineIndex
1894 + options.lineSeparatorSequence.length);
1895 formattedSource.append(line);
1896 beginningOfLineIndex = beginningOfLineIndex
1898 if ((line = reader.readLine()) != null) {
1900 .append(options.lineSeparatorSequence);
1901 beginningOfLineIndex += options.lineSeparatorSequence.length;
1902 dumpTab(currentLineIndentationLevel);
1906 } catch (IOException e) {
1907 e.printStackTrace();
1910 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1911 beginningOfLineIndex + max);
1912 for (int i = 0; i < max; i++) {
1913 char currentChar = currentString.charAt(i);
1914 switch (currentChar) {
1919 // fix for 1FFYL5C: LFCOM:ALL - Incorrect
1921 // split with a comment inside a condition
1922 // a substring cannot end with a
1923 // lineSeparatorSequence,
1924 // except if it has been added by format() after a
1928 .append(options.lineSeparatorSequence);
1929 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
1935 formattedSource.append(currentChar);
1939 // update positions inside the mappedPositions table
1940 if (substringIndex != -1) {
1941 if (multipleLineCommentCounter == 0) {
1942 int startPosition = beginningOfLineIndex
1943 + startSubstringIndexes[substringIndex];
1944 updateMappedPositionsWhileSplitting(startPosition,
1945 startPosition + max);
1947 // compute the splitDelta resulting with the operator and blank
1949 if (substringIndex + 1 != startSubstringIndexes.length) {
1950 increaseSplitDelta(startSubstringIndexes[substringIndex]
1951 + max - startSubstringIndexes[substringIndex + 1]);
1954 // dump postfix operator?
1955 if (placeOperatorBehind) {
1956 if (insertSpaceBefore(operator)) {
1957 formattedSource.append(' ');
1958 if (operator.compareTo (TokenName.NONE) > 0) {
1959 increaseSplitDelta(1);
1962 formattedSource.append(operatorString);
1963 if (operator.compareTo (TokenName.NONE) > 0) {
1964 increaseSplitDelta(operatorString.length());
1969 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1970 // extends has to stand alone on a line when currentString has been
1972 if (options.maxLineLength != 0 && splitLine != null
1973 && (operator == TokenName.EXTENDS)) {
1974 // || operator == TokenName.IMPLEMENTS
1975 // || operator == TokenName.THROWS)) {
1976 formattedSource.append(options.lineSeparatorSequence);
1977 increaseSplitDelta(options.lineSeparatorSequence.length);
1980 if (operator == TokenName.EXTENDS) {
1981 // || operator == TokenName.implements
1982 // || operator == TokenName.throws) {
1983 formattedSource.append(' ');
1984 increaseSplitDelta(1);
1987 // perform actual splitting
1988 String result[] = splitLine.substrings;
1989 TokenName[] splitOperators = splitLine.operators;
1990 if (result[0].length() == 0) {
1991 // when the substring 0 is null, the substring 1 is correctly
1994 emptyFirstSubString = true;
1996 // the operator going in front of the result[0] string is the operator
1998 for (int i = 0, max = result.length; i < max; i++) {
1999 // the new depth is the current one if this is the first substring,
2000 // the current one + 1 otherwise.
2001 // if the substring is a comment, use the current indentation Level
2002 // instead of the depth
2003 // (-1 because the ouputline increases depth).
2004 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of
2007 String currentResult = result[i];
2008 if (currentResult.length() != 0 || splitOperators[i].compareTo (TokenName.NONE) > 0) {
2009 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
2010 || currentResult.startsWith("//")) //$NON-NLS-1$
2011 ? indentationLevel - 1
2013 outputLine(currentResult,
2014 i == 0 || (i == 1 && emptyFirstSubString) ? preIndented: false,
2015 i == 0 ? newDepth : newDepth + 1,
2018 splitLine.startSubstringsIndexes,
2019 currentString.indexOf(currentResult));
2021 formattedSource.append(options.lineSeparatorSequence);
2022 increaseSplitDelta(options.lineSeparatorSequence.length);
2026 if (result.length == splitOperators.length - 1) {
2027 TokenName lastOperator = splitOperators[result.length];
2028 String lastOperatorString = operatorString(lastOperator);
2029 formattedSource.append(options.lineSeparatorSequence);
2030 increaseSplitDelta(options.lineSeparatorSequence.length);
2031 if (breakLineBeforeOperator(lastOperator)) {
2033 if (lastOperator.compareTo (TokenName.NONE) > 0) {
2034 if (insertSpaceBefore(lastOperator)) {
2035 formattedSource.append(' ');
2036 increaseSplitDelta(1);
2038 formattedSource.append(lastOperatorString);
2039 increaseSplitDelta(lastOperatorString.length());
2040 if (insertSpaceAfter(lastOperator)
2041 && lastOperator != TokenName.IMPLEMENTS
2042 && lastOperator != TokenName.EXTENDS) {
2043 // && lastOperator != TokenName.throws) {
2044 formattedSource.append(' ');
2045 increaseSplitDelta(1);
2050 if (placeOperatorBehind) {
2051 if (insertSpaceBefore(operator)) {
2052 formattedSource.append(' ');
2053 increaseSplitDelta(1);
2055 formattedSource.append(operatorString);
2056 // increaseSplitDelta(operatorString.length());
2061 * Pops the top statement of the stack if it is <code>token</code>
2063 private int pop(TokenName do1) {
2065 if ((constructionsCount > 0)
2066 && (constructions[constructionsCount - 1] == do1)) {
2068 constructionsCount--;
2074 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
2075 * <code>NONINDENT_BLOCK</code>.
2077 // private int popBlock() {
2079 // if ((constructionsCount > 0)
2080 // && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
2081 // if (constructions[constructionsCount - 1] == BLOCK)
2083 // constructionsCount--;
2089 * Pops elements until the stack is empty or the top element is
2090 * <code>token</code>.<br>
2091 * Does not remove <code>token</code> from the stack.
2094 * the token to be left as the top of the stack
2096 // private int popExclusiveUntil(int token) {
2098 // int startCount = constructionsCount;
2099 // for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2100 // if (constructions[i] != NONINDENT_BLOCK)
2102 // constructionsCount--;
2108 * Pops elements until the stack is empty or the top element is a
2109 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2110 * Does not remove it from the stack.
2112 private int popExclusiveUntilBlock() {
2113 int startCount = constructionsCount;
2115 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2116 && constructions[i] != NONINDENT_BLOCK; i--) {
2117 constructionsCount--;
2124 * Pops elements until the stack is empty or the top element is a
2125 * <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
2126 * <code>CASE</code>.<br>
2127 * Does not remove it from the stack.
2129 private int popExclusiveUntilBlockOrCase() {
2130 int startCount = constructionsCount;
2132 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2133 && constructions[i] != NONINDENT_BLOCK
2134 && constructions[i] != TokenName.CASE; i--) {
2135 constructionsCount--;
2142 * Pops elements until the stack is empty or the top element is
2143 * <code>token</code>.<br>
2144 * Removes <code>token</code> from the stack too.
2147 * the token to remove from the stack
2149 private int popInclusiveUntil(TokenName token) {
2150 int startCount = constructionsCount;
2152 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2153 if (constructions[i] != NONINDENT_BLOCK)
2155 constructionsCount--;
2157 if (constructionsCount > 0) {
2158 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
2160 constructionsCount--;
2166 * Pops elements until the stack is empty or the top element is a
2167 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2168 * Does not remove it from the stack.
2170 private int popInclusiveUntilBlock() {
2171 int startCount = constructionsCount;
2173 for (int i = startCount - 1; i >= 0
2174 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
2176 constructionsCount--;
2178 if (constructionsCount > 0) {
2179 if (constructions[constructionsCount - 1] == BLOCK)
2181 constructionsCount--;
2187 * Pushes a block in the stack. <br>
2188 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
2189 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
2190 * otherwise. Creates a new bigger array if the current one is full.
2192 private int pushBlock() {
2194 if (constructionsCount == constructions.length)
2195 System.arraycopy(constructions, 0,
2196 (constructions = new TokenName[constructionsCount * 2]), 0,
2197 constructionsCount);
2198 if ((constructionsCount == 0)
2199 || (constructions[constructionsCount - 1] == BLOCK)
2200 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
2201 || (constructions[constructionsCount - 1] == TokenName.CASE)) {
2203 constructions[constructionsCount++] = BLOCK;
2205 constructions[constructionsCount++] = NONINDENT_BLOCK;
2211 * Pushes <code>token</code>.<br>
2212 * Creates a new bigger array if the current one is full.
2214 private int pushControlStatement(TokenName token) {
2215 if (constructionsCount == constructions.length)
2216 System.arraycopy(constructions, 0,
2217 (constructions = new TokenName[constructionsCount * 2]), 0,
2218 constructionsCount);
2219 constructions[constructionsCount++] = token;
2223 // private static boolean separateFirstArgumentOn(int currentToken) {
2224 // // return (currentToken == TokenName.COMMA || currentToken ==
2225 // // TokenName.SEMICOLON);
2226 // return currentToken != TokenName.if && currentToken != TokenName.LPAREN
2227 // && currentToken != TokenName.NOT
2228 // && currentToken != TokenName.while
2229 // && currentToken != TokenName.for
2230 // && currentToken != TokenName.foreach
2231 // && currentToken != TokenName.switch;
2235 * Set the positions to map. The mapped positions should be retrieved using
2236 * the getMappedPositions() method.
2240 * @deprecated Set the positions to map using the format(String, int, int[])
2243 * @see #getMappedPositions()
2245 public void setPositionsToMap(int[] positions) {
2246 positionsToMap = positions;
2249 mappedPositions = new int[positions.length];
2253 * Appends a space character to the current line buffer.
2255 private void space() {
2256 currentLineBuffer.append(' ');
2257 increaseLineDelta(1);
2261 * Splits <code>stringToSplit</code> on the top level token <br>
2262 * If there are several identical token at the same level, the string is cut
2265 * @return an object containing the operator and all the substrings or null
2266 * if the string cannot be split
2268 // public SplitLine split(String stringToSplit) {
2269 // return split(stringToSplit, 0);
2273 * Splits <code>stringToSplit</code> on the top level token <br>
2274 * If there are several identical token at the same level, the string is cut
2277 * @return an object containing the operator and all the substrings or null
2278 * if the string cannot be split
2280 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2282 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2283 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2285 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2288 // split doesn't work correct for PHP
2291 // int currentToken = 0;
2292 // int splitTokenType = 0;
2293 // int splitTokenDepth = Integer.MAX_VALUE;
2294 // int splitTokenPriority = Integer.MAX_VALUE;
2295 // int[] substringsStartPositions = new int[10];
2296 // // contains the start position of substrings
2297 // int[] substringsEndPositions = new int[10];
2298 // // contains the start position of substrings
2299 // int substringsCount = 1; // index in the substringsStartPosition
2301 // int[] splitOperators = new int[10];
2302 // // contains the start position of substrings
2303 // int splitOperatorsCount = 0; // index in the substringsStartPosition
2305 // int[] openParenthesisPosition = new int[10];
2306 // int openParenthesisPositionCount = 0;
2307 // int position = 0;
2308 // int lastOpenParenthesisPosition = -1;
2309 // // used to remember the position of the 1st open parenthesis
2310 // // needed for a pattern like: A.B(C); we want formatted like A.B(
2312 // // setup the scanner with a new source
2313 // int lastCommentStartPosition = -1;
2314 // // to remember the start position of the last comment
2315 // int firstTokenOnLine = -1;
2316 // // to remember the first token of the line
2317 // int previousToken = -1;
2318 // // to remember the previous token.
2319 // splitScanner.setSource(stringToSplit.toCharArray());
2321 // // start the loop
2323 // // takes the next token
2325 // if (currentToken != Scanner.TokenName.WHITESPACE)
2326 // previousToken = currentToken;
2327 // currentToken = splitScanner.getNextToken();
2328 // if (Scanner.DEBUG) {
2329 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2330 // int currentStartPosition = splitScanner
2331 // .getCurrentTokenStartPosition();
2332 // System.out.print(currentStartPosition + "," + currentEndPosition
2334 // System.out.println(scanner.toStringAction(currentToken));
2336 // } catch (InvalidInputException e) {
2337 // if (!handleInvalidToken(e))
2339 // currentToken = 0;
2340 // // this value is not modify when an exception is raised.
2342 // if (currentToken == TokenName.EOF)
2344 // if (firstTokenOnLine == -1) {
2345 // firstTokenOnLine = currentToken;
2347 // switch (currentToken) {
2348 // case TokenName.RBRACE :
2349 // case TokenName.RPAREN :
2350 // if (openParenthesisPositionCount > 0) {
2351 // if (openParenthesisPositionCount == 1
2352 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2353 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2354 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2355 // || (splitTokenDepth > openParenthesisPositionCount &&
2356 // openParenthesisPositionCount == 1)) {
2357 // splitTokenType = 0;
2358 // splitTokenDepth = openParenthesisPositionCount;
2359 // splitTokenPriority = Integer.MAX_VALUE;
2360 // substringsStartPositions[0] = 0;
2361 // // better token means the whole line until now is the first
2363 // substringsCount = 1; // resets the count of substrings
2364 // substringsEndPositions[0] = openParenthesisPosition[0];
2365 // // substring ends on operator start
2366 // position = openParenthesisPosition[0];
2367 // // the string mustn't be cut before the closing parenthesis but
2368 // // after the opening one.
2369 // splitOperatorsCount = 1; // resets the count of split operators
2370 // splitOperators[0] = 0;
2372 // openParenthesisPositionCount--;
2375 // case TokenName.LBRACE :
2376 // case TokenName.LPAREN :
2377 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2380 // openParenthesisPosition,
2382 // (openParenthesisPosition = new int[openParenthesisPositionCount *
2384 // 0, openParenthesisPositionCount);
2386 // openParenthesisPosition[openParenthesisPositionCount++] =
2387 // splitScanner.currentPosition;
2388 // if (currentToken == TokenName.LPAREN
2389 // && previousToken == TokenName.RPAREN) {
2390 // openParenthesisPosition[openParenthesisPositionCount - 1] =
2391 // splitScanner.startPosition;
2394 // case TokenName.SEMICOLON :
2396 // case TokenName.COMMA :
2398 // case TokenName.EQUAL :
2400 // if (openParenthesisPositionCount < splitTokenDepth
2401 // || (openParenthesisPositionCount == splitTokenDepth &&
2402 // splitTokenPriority > getTokenPriority(currentToken))) {
2403 // // the current token is better than the one we currently have
2404 // // (in level or in priority if same level)
2405 // // reset the substringsCount
2406 // splitTokenDepth = openParenthesisPositionCount;
2407 // splitTokenType = currentToken;
2408 // splitTokenPriority = getTokenPriority(currentToken);
2409 // substringsStartPositions[0] = 0;
2410 // // better token means the whole line until now is the first
2412 // if (separateFirstArgumentOn(firstTokenOnLine)
2413 // && openParenthesisPositionCount > 0) {
2414 // substringsCount = 2; // resets the count of substrings
2415 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2417 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2419 // substringsEndPositions[1] = splitScanner.startPosition;
2420 // splitOperatorsCount = 2; // resets the count of split operators
2421 // splitOperators[0] = 0;
2422 // splitOperators[1] = currentToken;
2423 // position = splitScanner.currentPosition;
2424 // // next substring will start from operator end
2426 // substringsCount = 1; // resets the count of substrings
2427 // substringsEndPositions[0] = splitScanner.startPosition;
2428 // // substring ends on operator start
2429 // position = splitScanner.currentPosition;
2430 // // next substring will start from operator end
2431 // splitOperatorsCount = 1; // resets the count of split operators
2432 // splitOperators[0] = currentToken;
2435 // if ((openParenthesisPositionCount == splitTokenDepth &&
2436 // splitTokenPriority == getTokenPriority(currentToken))
2437 // && splitTokenType != TokenName.EQUAL
2438 // && currentToken != TokenName.EQUAL) {
2439 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2441 // // take only the 1st = into account.
2442 // // if another token with the same priority is found,
2443 // // push the start position of the substring and
2444 // // push the token into the stack.
2445 // // create a new array object if the current one is full.
2446 // if (substringsCount == substringsStartPositions.length) {
2449 // substringsStartPositions,
2451 // (substringsStartPositions = new int[substringsCount * 2]),
2452 // 0, substringsCount);
2453 // System.arraycopy(substringsEndPositions, 0,
2454 // (substringsEndPositions = new int[substringsCount * 2]),
2455 // 0, substringsCount);
2457 // if (splitOperatorsCount == splitOperators.length) {
2458 // System.arraycopy(splitOperators, 0,
2459 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2460 // splitOperatorsCount);
2462 // substringsStartPositions[substringsCount] = position;
2463 // substringsEndPositions[substringsCount++] =
2464 // splitScanner.startPosition;
2465 // // substring ends on operator start
2466 // position = splitScanner.currentPosition;
2467 // // next substring will start from operator end
2468 // splitOperators[splitOperatorsCount++] = currentToken;
2472 // case TokenName.COLON :
2474 // // see 1FK7C5R, we only split on a colon, when it is associated
2475 // // with a question-mark.
2476 // // indeed it might appear also behind a case statement, and we do
2477 // // not to break at this point.
2478 // if ((splitOperatorsCount == 0)
2479 // || splitOperators[splitOperatorsCount - 1] != TokenName.QUESTION) {
2482 // case TokenName.extends :
2483 // case TokenName.implements :
2484 // //case TokenName.throws :
2485 // case TokenName.DOT :
2487 // case TokenName.MULTIPLY :
2489 // case TokenName.DIVIDE :
2491 // case TokenName.REMAINDER :
2493 // case TokenName.PLUS :
2494 // // + (15.17, 15.17.2)
2495 // case TokenName.MINUS :
2497 // case TokenName.LEFT_SHIFT :
2499 // case TokenName.RIGHT_SHIFT :
2501 // // case TokenName.UNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2502 // case TokenName.LESS :
2504 // case TokenName.LESS_EQUAL :
2506 // case TokenName.GREATER :
2508 // case TokenName.GREATER_EQUAL :
2510 // // case TokenName.instanceof : // instanceof
2511 // case TokenName.EQUAL_EQUAL :
2512 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2513 // case TokenName.EQUAL_EQUAL_EQUAL :
2514 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2515 // case TokenName.NOT_EQUAL :
2516 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2517 // case TokenName.NOT_EQUAL_EQUAL :
2518 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2519 // case TokenName.AND :
2520 // // & (15.21, 15.21.1, 15.21.2)
2521 // case TokenName.OR :
2522 // // | (15.21, 15.21.1, 15.21.2)
2523 // case TokenName.XOR :
2524 // // ^ (15.21, 15.21.1, 15.21.2)
2525 // case TokenName.AND_AND :
2527 // case TokenName.OR_OR :
2529 // case TokenName.QUESTION :
2531 // case TokenName.MULTIPLY_EQUAL :
2533 // case TokenName.DIVIDE_EQUAL :
2535 // case TokenName.REMAINDER_EQUAL :
2537 // case TokenName.PLUS_EQUAL :
2539 // case TokenName.MINUS_EQUAL :
2541 // case TokenName.LEFT_SHIFT_EQUAL :
2543 // case TokenName.RIGHT_SHIFT_EQUAL :
2545 // // case TokenName.UNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2546 // case TokenName.AND_EQUAL :
2548 // case TokenName.XOR_EQUAL :
2550 // case TokenName.OR_EQUAL :
2552 // if ((openParenthesisPositionCount < splitTokenDepth ||
2553 // (openParenthesisPositionCount == splitTokenDepth &&
2554 // splitTokenPriority
2555 // > getTokenPriority(currentToken)))
2556 // && !((currentToken == TokenName.PLUS || currentToken ==
2557 // TokenName.MINUS) && (previousToken == TokenName.LBRACE
2558 // || previousToken == TokenName.LBRACKET || splitScanner.startPosition
2560 // // the current token is better than the one we currently have
2561 // // (in level or in priority if same level)
2562 // // reset the substringsCount
2563 // splitTokenDepth = openParenthesisPositionCount;
2564 // splitTokenType = currentToken;
2565 // splitTokenPriority = getTokenPriority(currentToken);
2566 // substringsStartPositions[0] = 0;
2567 // // better token means the whole line until now is the first
2569 // if (separateFirstArgumentOn(firstTokenOnLine)
2570 // && openParenthesisPositionCount > 0) {
2571 // substringsCount = 2; // resets the count of substrings
2572 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2574 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2576 // substringsEndPositions[1] = splitScanner.startPosition;
2577 // splitOperatorsCount = 3; // resets the count of split operators
2578 // splitOperators[0] = 0;
2579 // splitOperators[1] = 0;
2580 // splitOperators[2] = currentToken;
2581 // position = splitScanner.currentPosition;
2582 // // next substring will start from operator end
2584 // substringsCount = 1; // resets the count of substrings
2585 // substringsEndPositions[0] = splitScanner.startPosition;
2586 // // substring ends on operator start
2587 // position = splitScanner.currentPosition;
2588 // // next substring will start from operator end
2589 // splitOperatorsCount = 2; // resets the count of split operators
2590 // splitOperators[0] = 0;
2591 // // nothing for first operand since operator will be inserted in
2592 // // front of the second operand
2593 // splitOperators[1] = currentToken;
2596 // if (openParenthesisPositionCount == splitTokenDepth
2597 // && splitTokenPriority == getTokenPriority(currentToken)) {
2598 // // if another token with the same priority is found,
2599 // // push the start position of the substring and
2600 // // push the token into the stack.
2601 // // create a new array object if the current one is full.
2602 // if (substringsCount == substringsStartPositions.length) {
2605 // substringsStartPositions,
2607 // (substringsStartPositions = new int[substringsCount * 2]),
2608 // 0, substringsCount);
2609 // System.arraycopy(substringsEndPositions, 0,
2610 // (substringsEndPositions = new int[substringsCount * 2]),
2611 // 0, substringsCount);
2613 // if (splitOperatorsCount == splitOperators.length) {
2614 // System.arraycopy(splitOperators, 0,
2615 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2616 // splitOperatorsCount);
2618 // substringsStartPositions[substringsCount] = position;
2619 // substringsEndPositions[substringsCount++] =
2620 // splitScanner.startPosition;
2621 // // substring ends on operator start
2622 // position = splitScanner.currentPosition;
2623 // // next substring will start from operator end
2624 // splitOperators[splitOperatorsCount++] = currentToken;
2630 // if (isComment(currentToken)) {
2631 // lastCommentStartPosition = splitScanner.startPosition;
2633 // lastCommentStartPosition = -1;
2636 // } catch (InvalidInputException e) {
2639 // // if the string cannot be split, return null.
2640 // if (splitOperatorsCount == 0)
2642 // // ## SPECIAL CASES BEGIN
2643 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenName.DOT
2644 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2645 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenName.DOT
2646 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 &&
2647 // lastOpenParenthesisPosition <= options.maxLineLength) ||
2648 // (separateFirstArgumentOn(firstTokenOnLine)
2649 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2650 // && (lastOpenParenthesisPosition < splitScanner.source.length &&
2651 // splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2652 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis
2654 // // not be broken on two lines
2655 // // only one split on a top level .
2656 // // or more than one split on . and substring before open parenthesis
2659 // // or split inside parenthesis and first token is not a for/while/if
2660 // SplitLine sl = split(
2661 // stringToSplit.substring(lastOpenParenthesisPosition),
2662 // lastOpenParenthesisPosition);
2663 // if (sl == null || sl.operators[0] != TokenName.COMMA) {
2664 // // trim() is used to remove the extra blanks at the end of the
2665 // // substring. See PR 1FGYPI1
2666 // return new SplitLine(new int[]{0, 0}, new String[]{
2667 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2668 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2669 // offsetInGlobalLine,
2670 // lastOpenParenthesisPosition + offsetInGlobalLine});
2672 // // right substring can be split and is split on comma
2673 // // copy substrings and operators
2674 // // except if the 1st string is empty.
2675 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2676 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2677 // String[] result = new String[subStringsLength];
2678 // int[] startIndexes = new int[subStringsLength];
2679 // int operatorsLength = sl.operators.length + 1 - startIndex;
2680 // int[] operators = new int[operatorsLength];
2681 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2682 // operators[0] = 0;
2683 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2684 // 1, subStringsLength - 1);
2685 // for (int i = subStringsLength - 1; i >= 0; i--) {
2686 // startIndexes[i] += offsetInGlobalLine;
2688 // System.arraycopy(sl.substrings, startIndex, result, 1,
2689 // subStringsLength - 1);
2690 // System.arraycopy(sl.operators, startIndex, operators, 1,
2691 // operatorsLength - 1);
2692 // return new SplitLine(operators, result, startIndexes);
2695 // // if the last token is a comment and the substring before the
2698 // // split before the comment and return the result.
2699 // if (lastCommentStartPosition > -1
2700 // && lastCommentStartPosition < options.maxLineLength
2701 // && splitTokenPriority > 50) {
2702 // int end = lastCommentStartPosition;
2703 // int start = lastCommentStartPosition;
2704 // if (stringToSplit.charAt(end - 1) == ' ') {
2707 // if (start != end && stringToSplit.charAt(start) == ' ') {
2710 // return new SplitLine(new int[]{0, 0}, new String[]{
2711 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2712 // new int[]{0, start});
2714 // if (position != stringToSplit.length()) {
2715 // if (substringsCount == substringsStartPositions.length) {
2716 // System.arraycopy(substringsStartPositions, 0,
2717 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2718 // substringsCount);
2719 // System.arraycopy(substringsEndPositions, 0,
2720 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2721 // substringsCount);
2723 // // avoid empty extra substring, e.g. line terminated with a
2725 // substringsStartPositions[substringsCount] = position;
2726 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2728 // if (splitOperatorsCount == splitOperators.length) {
2729 // System.arraycopy(splitOperators, 0,
2730 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2731 // splitOperatorsCount);
2733 // splitOperators[splitOperatorsCount] = 0;
2734 // // the last element of the stack is the position of the end of
2736 // // +1 because the substring method excludes the last character
2737 // String[] result = new String[substringsCount];
2738 // for (int i = 0; i < substringsCount; i++) {
2739 // int start = substringsStartPositions[i];
2740 // int end = substringsEndPositions[i];
2741 // if (stringToSplit.charAt(start) == ' ') {
2743 // substringsStartPositions[i]++;
2745 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2748 // result[i] = stringToSplit.substring(start, end);
2749 // substringsStartPositions[i] += offsetInGlobalLine;
2751 // if (splitOperatorsCount > substringsCount) {
2752 // System.arraycopy(substringsStartPositions, 0,
2753 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2754 // substringsCount);
2755 // System.arraycopy(substringsEndPositions, 0,
2756 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2757 // substringsCount);
2758 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2759 // substringsStartPositions[i] = position;
2760 // substringsEndPositions[i] = position;
2762 // System.arraycopy(splitOperators, 0,
2763 // (splitOperators = new int[splitOperatorsCount]), 0,
2764 // splitOperatorsCount);
2766 // System.arraycopy(substringsStartPositions, 0,
2767 // (substringsStartPositions = new int[substringsCount]), 0,
2768 // substringsCount);
2769 // System.arraycopy(substringsEndPositions, 0,
2770 // (substringsEndPositions = new int[substringsCount]), 0,
2771 // substringsCount);
2772 // System.arraycopy(splitOperators, 0,
2773 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2775 // SplitLine splitLine = new SplitLine(splitOperators, result,
2776 // substringsStartPositions);
2777 // return splitLine;
2780 private void updateMappedPositions(int startPosition) {
2781 if (positionsToMap == null) {
2784 char[] source = scanner.source;
2785 int sourceLength = source.length;
2786 while (indexToMap < positionsToMap.length
2787 && positionsToMap[indexToMap] <= startPosition) {
2788 int posToMap = positionsToMap[indexToMap];
2789 if (posToMap < 0 || posToMap >= sourceLength) {
2790 // protection against out of bounds position
2791 if (posToMap == sourceLength) {
2792 mappedPositions[indexToMap] = formattedSource.length();
2794 indexToMap = positionsToMap.length; // no more mapping
2797 if (CharOperation.isWhitespace(source[posToMap])) {
2798 mappedPositions[indexToMap] = startPosition + globalDelta
2801 if (posToMap == sourceLength - 1) {
2802 mappedPositions[indexToMap] = startPosition + globalDelta
2805 mappedPositions[indexToMap] = posToMap + globalDelta
2813 private void updateMappedPositionsWhileSplitting(int startPosition,
2815 if (mappedPositions == null || mappedPositions.length == indexInMap)
2817 while (indexInMap < mappedPositions.length
2818 && startPosition <= mappedPositions[indexInMap]
2819 && mappedPositions[indexInMap] < endPosition
2820 && indexInMap < indexToMap) {
2821 mappedPositions[indexInMap] += splitDelta;
2826 private int getLength(String s, int tabDepth) {
2828 for (int i = 0; i < tabDepth; i++) {
2829 length += options.tabSize;
2831 for (int i = 0, max = s.length(); i < max; i++) {
2832 char currentChar = s.charAt(i);
2833 switch (currentChar) {
2835 length += options.tabSize;
2845 * Sets the initial indentation level
2847 * @param indentationLevel
2848 * new indentation level
2852 public void setInitialIndentationLevel(int newIndentationLevel) {
2853 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;