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 int BLOCK = ITerminalSymbols.TokenNameLBRACE;
50 * Represents a block following a control statement in the
51 * <code>constructions</code> stack.
53 public static final int NONINDENT_BLOCK = -100;
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 int[] 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 int[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(int operator) {
195 case TokenNameSEMICOLON:
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, false,
297 indentationLevelForOpenCloseBraces, 0, -1, null, 0);
298 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
300 outputLine(currentString, false, currentLineIndentationLevel, 0,
303 int scannerSourceLength = scanner.source.length;
304 if ((scannerSourceLength > 2)
305 && (scanner.startPosition < scannerSourceLength)) {
306 if (scanner.source[scannerSourceLength - 1] == '\n'
307 && scanner.source[scannerSourceLength - 2] == '\r') {
308 formattedSource.append(options.lineSeparatorSequence);
309 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
310 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
311 formattedSource.append(options.lineSeparatorSequence);
312 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
313 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
314 formattedSource.append(options.lineSeparatorSequence);
315 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
318 updateMappedPositions(scanner.startPosition);
322 * Formats the input string.
324 private void format() {
326 int previousToken = 0;
327 int previousCompilableToken = 0;
328 int indentationOffset = 0;
329 int newLinesInWhitespace = 0;
330 // number of new lines in the previous whitespace token
331 // (used to leave blank lines before comments)
332 int pendingNewLines = 0;
333 boolean expectingOpenBrace = false;
334 boolean clearNonBlockIndents = false;
335 // true if all indentations till the 1st { (usefull after } or ;)
336 boolean pendingSpace = true;
337 boolean pendingNewlineAfterParen = false;
338 // true when a cr is to be put after a ) (in conditional statements)
339 boolean inAssignment = false;
340 boolean inArrayAssignment = false;
341 boolean inThrowsClause = false;
342 boolean inClassOrInterfaceHeader = false;
343 int dollarBraceCount = 0;
344 // openBracketCount is used to count the number of open brackets not
347 int openBracketCount = 0;
348 int unarySignModifier = 0;
349 // openParenthesis[0] is used to count the parenthesis not belonging to
352 // (eg foo();). parenthesis in for (...) are count elsewhere in the
354 int openParenthesisCount = 1;
355 int[] openParenthesis = new int[10];
356 // tokenBeforeColon is used to know what token goes along with the
359 // it can be case or ?
360 int tokenBeforeColonCount = 0;
361 int[] tokenBeforeColon = new int[10];
362 constructionsCount = 0; // initializes the constructions count.
363 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
365 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and
367 boolean specialElse = false;
368 // OPTION (IndentationLevel): initial indentation level may be non-zero.
369 currentLineIndentationLevel += constructionsCount;
370 // An InvalidInputException exception might cause the termination of
373 int arrayDeclarationCount = 0;
374 int[] arrayDeclarationParenthesis = new int[10];
377 // Get the next token. Catch invalid input and output it
378 // with minimal formatting, also catch end of input and
381 token = scanner.getNextToken();
383 int currentEndPosition = scanner
384 .getCurrentTokenEndPosition();
385 int currentStartPosition = scanner
386 .getCurrentTokenStartPosition();
387 System.out.print(currentStartPosition + ","
388 + currentEndPosition + ": ");
389 System.out.println(scanner.toStringAction(token));
391 // Patch for line comment
392 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
393 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
394 int length = scanner.currentPosition;
395 loop: for (int index = length - 1; index >= 0; index--) {
396 switch (scanner.source[index]) {
399 scanner.currentPosition--;
406 } catch (InvalidInputException e) {
407 if (!handleInvalidToken(e)) {
412 if (token == Scanner.TokenNameEOF) {
414 } else if (token == Scanner.TokenNameHEREDOC) {
415 // no indentation for heredocs and HTML !
416 outputCurrentTokenWithoutIndent(Scanner.TokenNameHEREDOC, 0);
418 } else if (token == Scanner.TokenNameINLINE_HTML) {
419 // no indentation for heredocs and HTML !
420 int newLineCount = 1;
421 if (scanner.startPosition == 0) {
424 outputCurrentTokenWithoutIndent(
425 Scanner.TokenNameINLINE_HTML, newLineCount);
426 int srcLen = scanner.source.length;
427 if (scanner.currentPosition < srcLen - 1) {
433 * ## MODIFYING the indentation level before generating new
434 * lines and indentation in the output string
436 // Removes all the indentations made by statements not followed
439 // except if the current token is ELSE, CATCH or if we are in a
441 if (clearNonBlockIndents
442 && (token != Scanner.TokenNameWHITESPACE)) {
445 if (constructionsCount > 0
446 && constructions[constructionsCount - 1] == TokenNameelse) {
450 indentationLevel += popInclusiveUntil(TokenNameif);
452 // case TokenNamecatch :
453 // indentationLevel += popInclusiveUntil(TokenNamecatch);
455 // case TokenNamefinally :
456 // indentationLevel += popInclusiveUntil(TokenNamecatch);
459 if (nlicsToken == TokenNamedo) {
460 indentationLevel += pop(TokenNamedo);
464 indentationLevel += popExclusiveUntilBlockOrCase();
465 // clear until a CASE, DEFAULT or BLOCK is encountered.
466 // Thus, the indentationLevel is correctly cleared
468 // in a switch/case statement or in any other situation.
470 clearNonBlockIndents = false;
472 // returns to the indentation level created by the SWITCH
474 // if the current token is a CASE or a DEFAULT
475 if (token == TokenNamecase || token == TokenNamedefault) {
476 indentationLevel += pop(TokenNamecase);
478 // if (token == Scanner.TokenNamethrows) {
479 // inThrowsClause = true;
481 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface)
482 && previousToken != Scanner.TokenNameDOT) {
483 inClassOrInterfaceHeader = true;
486 * ## APPEND newlines and indentations to the output string
488 // Do not add a new line between ELSE and IF, if the option
489 // elseIfOnSameLine is true.
490 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
491 // if (pendingNewlineAfterParen
492 // && previousCompilableToken == TokenNameelse
493 // && token == TokenNameif
494 // && options.compactElseIfMode) {
495 // pendingNewlineAfterParen = false;
496 // pendingNewLines = 0;
497 // indentationLevel += pop(TokenNameelse);
498 // // because else if is now one single statement,
499 // // the indentation level after it is increased by one and not
501 // // (else = 1 indent, if = 1 indent, but else if = 1 indent,
504 // Add a newline & indent to the formatted source string if
505 // a for/if-else/while statement was scanned and there is no
508 pendingNewlineAfterParen = pendingNewlineAfterParen
509 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
510 if (pendingNewlineAfterParen
511 && token != Scanner.TokenNameWHITESPACE) {
512 pendingNewlineAfterParen = false;
513 // Do to add a newline & indent sequence if the current
515 // open brace or a period or if the current token is a
518 // previous token is a close paren.
519 // add a new line if a parenthesis belonging to a for()
521 // has been closed and the current token is not an opening
523 if (token != TokenNameLBRACE && !isComment(token)
524 // to avoid adding new line between else and a
526 && token != TokenNameDOT
527 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
529 currentLineIndentationLevel = indentationLevel;
531 pendingSpace = false;
533 if (token == TokenNameLBRACE
534 && options.newLineBeforeOpeningBraceMode) {
536 if (constructionsCount > 0
537 && constructions[constructionsCount - 1] != BLOCK
538 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
539 currentLineIndentationLevel = indentationLevel - 1;
541 currentLineIndentationLevel = indentationLevel;
544 pendingSpace = false;
548 if (token == TokenNameLBRACE
549 && options.newLineBeforeOpeningBraceMode
550 && constructionsCount > 0
551 && constructions[constructionsCount - 1] == TokenNamedo) {
553 currentLineIndentationLevel = indentationLevel - 1;
555 pendingSpace = false;
558 if (token == TokenNameLBRACE && inThrowsClause) {
559 inThrowsClause = false;
560 if (options.newLineBeforeOpeningBraceMode) {
562 currentLineIndentationLevel = indentationLevel;
564 pendingSpace = false;
568 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
569 inClassOrInterfaceHeader = false;
570 if (options.newLineBeforeOpeningBraceMode) {
572 currentLineIndentationLevel = indentationLevel;
574 pendingSpace = false;
577 // don't linebreak empty array declarations
578 if (token == TokenNameRPAREN && arrayDeclarationCount > 0) {
579 if (previousCompilableToken == TokenNameLPAREN) {
583 // Add pending new lines to the formatted source string.
584 // Note: pending new lines are not added if the current token
585 // is a single line comment or whitespace.
586 // if the comment is between parenthesis, there is no blank line
588 // (if it's a one-line comment, a blank line is added after it).
589 if (((pendingNewLines > 0 && (!isComment(token)))
590 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
591 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) || (newLinesInWhitespace > 0 && previousCompilableToken == TokenNameDOT))
592 && token != Scanner.TokenNameWHITESPACE) {
593 // Do not add newline & indent between an adjoining close
595 // close paren. Anonymous inner classes may use this form.
596 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
597 && token == TokenNameRPAREN;
598 // OPTION (NewLineInCompoundStatement): do not add newline &
600 // between close brace and else, (do) while, catch, and
602 // newlineInCompoundStatement is true.
603 boolean nlicsOption = previousToken == TokenNameRBRACE
604 && !options.newlineInControlStatementMode
605 && (token == TokenNameelse
606 || (token == TokenNamewhile && nlicsToken == TokenNamedo)
607 || token == TokenNamecatch || token == TokenNamefinally);
608 // Do not add a newline & indent between a close brace and
610 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
611 && token == TokenNameSEMICOLON;
612 // Do not add a new line & indent between a multiline
615 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
616 && token == TokenNameLBRACE;
617 // Do not add a newline & indent between a close brace and a
619 // (in array assignments, for example).
620 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
621 && token == TokenNameCOMMA;
622 // Add a newline and indent, if appropriate.
624 || (!commentAndOpenBrace
625 && !closeBraceAndCloseParen && !nlicsOption
626 && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
627 // if clearAllBlankLinesMode=false, leaves the blank
629 // inserted by the user
630 // if clearAllBlankLinesMode=true, removes all of then
631 // and insert only blank lines required by the
633 if (!options.clearAllBlankLinesMode) {
634 // (isComment(token))
635 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace
637 pendingNewLines = (pendingNewLines > 2) ? 2
640 if (previousCompilableToken == TokenNameLBRACE
641 && token == TokenNameRBRACE) {
642 containsOpenCloseBraces = true;
643 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
644 if (isComment(previousToken)) {
645 newLine(pendingNewLines);
648 * if (!(constructionsCount > 1 &&
649 * constructions[constructionsCount-1] ==
651 * (constructions[constructionsCount-2] ==
654 if (options.newLineInEmptyBlockMode) {
655 if (inArrayAssignment) {
656 newLine(1); // array assigment with an
659 newLine(pendingNewLines);
665 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with
668 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
669 newLine(pendingNewLines);
672 if (((previousCompilableToken == TokenNameSEMICOLON)
673 || (previousCompilableToken == TokenNameLBRACE)
674 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
675 && (token == TokenNameRBRACE)) {
676 indentationOffset = -1;
677 indentationLevel += popExclusiveUntilBlock();
679 if (previousToken == Scanner.TokenNameCOMMENT_LINE
682 currentLineIndentationLevel++;
684 currentLineIndentationLevel = indentationLevel
687 pendingSpace = false;
688 indentationOffset = 0;
691 newLinesInWhitespace = 0;
693 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
697 boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML
698 && token == TokenNameWHITESPACE;
700 // case TokenNameDOLLAR :
701 // dollarBraceCount++;
704 // case TokenNamefinally :
705 expectingOpenBrace = true;
706 pendingNewlineAfterParen = true;
707 indentationLevel += pushControlStatement(token);
710 case TokenNamedefault:
711 if (tokenBeforeColonCount == tokenBeforeColon.length) {
716 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
717 0, tokenBeforeColonCount);
719 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
720 indentationLevel += pushControlStatement(TokenNamecase);
722 case TokenNameQUESTION:
723 if (tokenBeforeColonCount == tokenBeforeColon.length) {
728 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
729 0, tokenBeforeColonCount);
731 tokenBeforeColon[tokenBeforeColonCount++] = token;
733 case TokenNameswitch:
735 case TokenNameforeach:
738 if (openParenthesisCount == openParenthesis.length) {
743 (openParenthesis = new int[openParenthesisCount * 2]),
744 0, openParenthesisCount);
746 openParenthesis[openParenthesisCount++] = 0;
747 expectingOpenBrace = true;
748 indentationLevel += pushControlStatement(token);
751 pendingNewlineAfterParen = true;
753 // several CATCH statements can be contiguous.
754 // a CATCH is encountered pop until first CATCH (if a CATCH
755 // follows a TRY it works the same way,
756 // as CATCH and TRY are the same token in the stack).
757 expectingOpenBrace = true;
758 indentationLevel += pushControlStatement(TokenNamecatch);
761 expectingOpenBrace = true;
762 indentationLevel += pushControlStatement(token);
767 case TokenNameLPAREN:
768 // if (previousToken == TokenNamesynchronized) {
769 // indentationLevel += pushControlStatement(previousToken);
771 // Put a space between the previous and current token if the
772 // previous token was not a keyword, open paren, logical
773 // compliment (eg: !), semi-colon, open brace, close brace,
775 if (previousCompilableToken != TokenNameLBRACKET
776 && previousToken != TokenNameIdentifier
777 && previousToken != 0
778 && previousToken != TokenNameNOT
779 && previousToken != TokenNameLPAREN
780 && previousToken != TokenNameTWIDDLE
781 && previousToken != TokenNameSEMICOLON
782 && previousToken != TokenNameLBRACE
783 && previousToken != TokenNameRBRACE
784 && previousToken != TokenNamesuper) {
785 // && previousToken != TokenNamethis) {
786 if (!options.compactArrays) {
790 // If in a for/if/while statement, increase the parenthesis
792 // for the current openParenthesisCount
793 // else increase the count for stand alone parenthesis.
794 if (openParenthesisCount > 0)
795 openParenthesis[openParenthesisCount - 1]++;
797 openParenthesis[0]++;
798 pendingSpace = false;
799 // recognize array declaration for nice output
800 if (previousCompilableToken == TokenNamearray) {
801 arrayDeclarationCount++;
802 arrayDeclarationParenthesis[arrayDeclarationCount] = openParenthesis[openParenthesisCount - 1];
803 if (!options.compactArrays) {
810 case TokenNameRPAREN:
811 // check for closing array declaration
812 if (arrayDeclarationCount > 0) {
813 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
814 if (previousCompilableToken != TokenNameLPAREN) {
815 if (!options.compactArrays) {
818 } else if (previousToken == TokenNameCOMMENT_LINE
819 || previousToken == TokenNameCOMMENT_BLOCK
820 || previousToken == TokenNameCOMMENT_PHPDOC) {
821 // prevent to combine comment line and statement line (#1475484)
822 if (!options.compactArrays) {
826 if (!options.compactArrays) {
830 currentLineIndentationLevel = indentationLevel;
832 arrayDeclarationCount--;
835 // Decrease the parenthesis count
836 // if there is no more unclosed parenthesis,
837 // a new line and indent may be append (depending on the
840 if ((openParenthesisCount > 1)
841 && (openParenthesis[openParenthesisCount - 1] > 0)) {
842 openParenthesis[openParenthesisCount - 1]--;
843 if (openParenthesis[openParenthesisCount - 1] <= 0) {
844 pendingNewlineAfterParen = true;
845 inAssignment = false;
846 openParenthesisCount--;
849 openParenthesis[0]--;
851 pendingSpace = false;
853 case TokenNameLBRACE:
854 if (previousCompilableToken == TokenNameDOLLAR) {
857 if ((previousCompilableToken == TokenNameRBRACKET)
858 || (previousCompilableToken == TokenNameEQUAL)) {
859 // if (previousCompilableToken == TokenNameRBRACKET)
861 inArrayAssignment = true;
862 inAssignment = false;
864 if (inArrayAssignment) {
865 indentationLevel += pushBlock();
867 // Add new line and increase indentation level after
870 indentationLevel += pushBlock();
871 inAssignment = false;
875 case TokenNameRBRACE:
876 if (dollarBraceCount > 0) {
880 if (previousCompilableToken == TokenNameRPAREN) {
881 pendingSpace = false;
883 if (inArrayAssignment) {
884 inArrayAssignment = false;
886 indentationLevel += popInclusiveUntilBlock();
889 indentationLevel += popInclusiveUntilBlock();
890 if (previousCompilableToken == TokenNameRPAREN) {
891 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on
895 .append(options.lineSeparatorSequence);
896 increaseLineDelta(options.lineSeparatorSequence.length);
898 if (constructionsCount > 0) {
899 switch (constructions[constructionsCount - 1]) {
901 case TokenNameforeach:
902 // indentationLevel += popExclusiveUntilBlock();
904 case TokenNameswitch:
909 case TokenNamefinally:
912 // case TokenNamesynchronized :
913 clearNonBlockIndents = true;
920 case TokenNameLBRACKET:
922 pendingSpace = false;
924 case TokenNameRBRACKET:
925 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
926 // if there is no left bracket to close, the right bracket
929 pendingSpace = false;
932 pendingSpace = false;
933 if (arrayDeclarationCount > 0) {
934 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
935 // there is no left parenthesis to close in current array declaration (#1475484)
936 if (!options.compactArrays) {
943 if (!options.compactStringConcatenation) {
946 pendingSpace = false;
948 case TokenNameSEMICOLON:
949 // Do not generate line terminators in the definition of
950 // the for statement.
951 // if not in this case, jump a line and reduce indentation
954 // if the block it closes belongs to a conditional statement
957 if (openParenthesisCount <= 1) {
959 if (expectingOpenBrace) {
960 clearNonBlockIndents = true;
961 expectingOpenBrace = false;
964 inAssignment = false;
965 pendingSpace = false;
967 case TokenNamePLUS_PLUS:
968 case TokenNameMINUS_MINUS:
969 // Do not put a space between a post-increment/decrement
970 // and the identifier being modified.
971 if (previousToken == TokenNameIdentifier
972 || previousToken == TokenNameRBRACKET
973 || previousToken == TokenNameVariable) {
974 pendingSpace = false;
978 // previously ADDITION
980 // Handle the unary operators plus and minus via a flag
981 if (!isLiteralToken(previousToken)
982 && previousToken != TokenNameIdentifier
983 && previousToken != TokenNameRPAREN
984 && previousToken != TokenNameRBRACKET) {
985 unarySignModifier = 1;
989 // In a switch/case statement, add a newline & indent
990 // when a colon is encountered.
991 if (tokenBeforeColonCount > 0) {
992 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
995 tokenBeforeColonCount--;
1001 case Scanner.TokenNameCOMMENT_LINE:
1002 pendingNewLines = 1;
1003 //if (inAssignment) {
1004 // currentLineIndentationLevel++;
1006 break; // a line is always inserted after a one-line
1008 case Scanner.TokenNameCOMMENT_PHPDOC:
1009 case Scanner.TokenNameCOMMENT_BLOCK:
1010 currentCommentOffset = getCurrentCommentOffset();
1011 pendingNewLines = 1;
1013 case Scanner.TokenNameWHITESPACE:
1014 if (!phpTagAndWhitespace) {
1015 // Count the number of line terminators in the
1017 // line spacing can be preserved near comments.
1018 char[] source = scanner.source;
1019 newLinesInWhitespace = 0;
1020 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
1021 if (source[i] == '\r') {
1023 if (source[++i] == '\n') {
1024 newLinesInWhitespace++;
1026 newLinesInWhitespace++;
1029 newLinesInWhitespace++;
1031 } else if (source[i] == '\n') {
1032 newLinesInWhitespace++;
1035 increaseLineDelta(scanner.startPosition
1036 - scanner.currentPosition);
1039 // case TokenNameHTML :
1040 // // Add the next token to the formatted source string.
1041 // // outputCurrentToken(token);
1042 // int startPosition = scanner.startPosition;
1044 // for (int i = startPosition, max =
1045 // scanner.currentPosition; i <
1047 // char currentCharacter = scanner.source[i];
1048 // updateMappedPositions(i);
1049 // currentLineBuffer.append(currentCharacter);
1053 if ((token == TokenNameIdentifier) || isLiteralToken(token)
1054 || token == TokenNamesuper) {
1055 // || token == TokenNamethis) {
1056 // Do not put a space between a unary operator
1057 // (eg: ++, --, +, -) and the identifier being modified.
1058 if (previousToken == TokenNamePLUS_PLUS
1059 || previousToken == TokenNameMINUS_MINUS
1060 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
1061 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
1062 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
1063 pendingSpace = false;
1065 unarySignModifier = 0;
1069 // Do not output whitespace tokens.
1070 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1072 * Add pending space to the formatted source string. Do not
1073 * output a space under the following circumstances: 1) this
1074 * is the first pass 2) previous token is an open paren 3)
1075 * previous token is a period 4) previous token is the
1076 * logical compliment (eg: !) 5) previous token is the
1077 * bitwise compliment (eg: ~) 6) previous token is the open
1078 * bracket (eg: [) 7) in an assignment statement, if the
1079 * previous token is an open brace or the current token is a
1080 * close brace 8) previous token is a single line comment 9)
1081 * current token is a '->'
1083 if (token == TokenNameMINUS_GREATER
1084 && options.compactDereferencingMode)
1085 pendingSpace = false;
1087 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
1088 && token == TokenNameRBRACE;
1090 && insertSpaceAfter(previousToken)
1091 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
1092 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
1093 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
1094 && !openAndCloseBrace)
1097 // Add the next token to the formatted source string.
1098 outputCurrentToken(token);
1099 if (token == Scanner.TokenNameCOMMENT_LINE
1100 && openParenthesisCount > 1) {
1101 pendingNewLines = 0;
1102 currentLineBuffer.append(options.lineSeparatorSequence);
1103 increaseLineDelta(options.lineSeparatorSequence.length);
1105 pendingSpace = true;
1107 // Whitespace tokens do not need to be remembered.
1108 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1109 previousToken = token;
1110 if (token != Scanner.TokenNameCOMMENT_BLOCK
1111 && token != Scanner.TokenNameCOMMENT_LINE
1112 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
1113 previousCompilableToken = token;
1117 output(copyRemainingSource());
1119 // dump the last token of the source in the formatted output.
1120 } catch (InvalidInputException e) {
1121 output(copyRemainingSource());
1123 // dump the last token of the source in the formatted output.
1128 * Formats the char array <code>sourceString</code>, and returns a string
1129 * containing the formatted version.
1131 * @return the formatted ouput.
1133 public String formatSourceString(String sourceString) {
1134 char[] sourceChars = sourceString.toCharArray();
1135 formattedSource = new StringBuffer(sourceChars.length);
1136 scanner.setSource(sourceChars);
1138 return formattedSource.toString();
1142 * Formats the char array <code>sourceString</code>, and returns a string
1143 * containing the formatted version.
1146 * the string to format
1147 * @param indentationLevel
1148 * the initial indentation level
1149 * @return the formatted ouput.
1151 public String format(String string, int indentationLevel) {
1152 return format(string, indentationLevel, (int[]) null);
1156 * Formats the char array <code>sourceString</code>, and returns a string
1157 * containing the formatted version. The positions array is modified to
1158 * contain the mapped positions.
1161 * the string to format
1162 * @param indentationLevel
1163 * the initial indentation level
1165 * the array of positions to map
1166 * @return the formatted ouput.
1168 public String format(String string, int indentationLevel, int[] positions) {
1169 return this.format(string, indentationLevel, positions, null);
1172 public String format(String string, int indentationLevel, int[] positions,
1173 String lineSeparator) {
1174 if (lineSeparator != null) {
1175 this.options.setLineSeparator(lineSeparator);
1177 if (positions != null) {
1178 this.setPositionsToMap(positions);
1179 this.setInitialIndentationLevel(indentationLevel);
1180 String formattedString = this.formatSourceString(string);
1181 int[] mappedPositions = this.getMappedPositions();
1183 .arraycopy(mappedPositions, 0, positions, 0,
1185 return formattedString;
1187 this.setInitialIndentationLevel(indentationLevel);
1188 return this.formatSourceString(string);
1193 * Formats the char array <code>sourceString</code>, and returns a string
1194 * containing the formatted version. The initial indentation level is 0.
1197 * the string to format
1198 * @return the formatted ouput.
1200 public String format(String string) {
1201 return this.format(string, 0, (int[]) null);
1205 * Formats a given source string, starting indenting it at a particular
1206 * depth and using the given options
1208 * @deprecated backport 1.0 internal functionality
1210 public static String format(String sourceString,
1211 int initialIndentationLevel, ConfigurableOption[] options) {
1212 CodeFormatter formatter = new CodeFormatter(options);
1213 formatter.setInitialIndentationLevel(initialIndentationLevel);
1214 return formatter.formatSourceString(sourceString);
1218 * Returns the number of characters and tab char between the beginning of
1219 * the line and the beginning of the comment.
1221 private int getCurrentCommentOffset() {
1222 int linePtr = scanner.linePtr;
1223 // if there is no beginning of line, return 0.
1227 int beginningOfLine = scanner.lineEnds[linePtr];
1228 int currentStartPosition = scanner.startPosition;
1229 char[] source = scanner.source;
1230 // find the position of the beginning of the line containing the comment
1231 while (beginningOfLine > currentStartPosition) {
1233 beginningOfLine = scanner.lineEnds[--linePtr];
1235 beginningOfLine = 0;
1239 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1240 char currentCharacter = source[i];
1241 switch (currentCharacter) {
1243 offset += options.tabSize;
1259 * Returns an array of descriptions for the configurable options. The
1260 * descriptions may be changed and passed back to a different compiler.
1262 * @deprecated backport 1.0 internal functionality
1264 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1265 String componentName = CodeFormatter.class.getName();
1266 FormatterOptions options = new FormatterOptions();
1267 return new ConfigurableOption[] {
1268 new ConfigurableOption(componentName, "newline.openingBrace",
1269 locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1271 new ConfigurableOption(componentName,
1272 "newline.controlStatement", locale,
1273 options.newlineInControlStatementMode ? 0 : 1),
1275 new ConfigurableOption(componentName, "newline.clearAll",
1276 locale, options.clearAllBlankLinesMode ? 0 : 1),
1278 // new ConfigurableOption(componentName, "newline.elseIf",
1280 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1281 new ConfigurableOption(componentName, "newline.emptyBlock",
1282 locale, options.newLineInEmptyBlockMode ? 0 : 1),
1284 new ConfigurableOption(componentName, "line.split", locale,
1285 options.maxLineLength),
1287 new ConfigurableOption(componentName,
1288 "style.compactAssignment", locale,
1289 options.compactAssignmentMode ? 0 : 1),
1291 new ConfigurableOption(componentName, "tabulation.char",
1292 locale, options.indentWithTab ? 0 : 1),
1294 new ConfigurableOption(componentName,
1295 "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1300 * Returns the array of mapped positions. Returns null is no positions have
1304 * @deprecated There is no need to retrieve the mapped positions anymore.
1306 public int[] getMappedPositions() {
1307 if (null != mappedPositions) {
1308 for (int i = 0; i < mappedPositions.length; i++) {
1309 if (mappedPositions[i] >= formattedSource.length()) {
1310 mappedPositions[i] = formattedSource.length() - 1;
1314 return mappedPositions;
1318 * Returns the priority of the token given as argument <br>
1319 * The most prioritary the token is, the smallest the return value is.
1321 * @return the priority of <code>token</code>
1323 * the token of which the priority is requested
1325 private static int getTokenPriority(int token) {
1327 case TokenNameextends:
1328 // case TokenNameimplements :
1329 // case TokenNamethrows :
1331 case TokenNameSEMICOLON:
1334 case TokenNameCOMMA:
1337 case TokenNameEQUAL:
1340 case TokenNameAND_AND:
1342 case TokenNameOR_OR:
1345 case TokenNameQUESTION:
1347 case TokenNameCOLON:
1349 return 50; // it's better cutting on ?: than on ;
1350 case TokenNameEQUAL_EQUAL:
1352 case TokenNameEQUAL_EQUAL_EQUAL:
1354 case TokenNameNOT_EQUAL:
1356 case TokenNameNOT_EQUAL_EQUAL:
1361 case TokenNameLESS_EQUAL:
1363 case TokenNameGREATER:
1365 case TokenNameGREATER_EQUAL:
1367 // case TokenNameinstanceof : // instanceof
1371 case TokenNameMINUS:
1374 case TokenNameMULTIPLY:
1376 case TokenNameDIVIDE:
1378 case TokenNameREMAINDER:
1381 case TokenNameLEFT_SHIFT:
1383 case TokenNameRIGHT_SHIFT:
1385 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1394 case TokenNameMULTIPLY_EQUAL:
1396 case TokenNameDIVIDE_EQUAL:
1398 case TokenNameREMAINDER_EQUAL:
1400 case TokenNamePLUS_EQUAL:
1402 case TokenNameMINUS_EQUAL:
1404 case TokenNameLEFT_SHIFT_EQUAL:
1406 case TokenNameRIGHT_SHIFT_EQUAL:
1408 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1409 case TokenNameAND_EQUAL:
1411 case TokenNameXOR_EQUAL:
1413 case TokenNameOR_EQUAL:
1415 case TokenNameDOT_EQUAL:
1422 return Integer.MAX_VALUE;
1427 * Handles the exception raised when an invalid token is encountered.
1428 * Returns true if the exception has been handled, false otherwise.
1430 private boolean handleInvalidToken(Exception e) {
1431 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1432 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1433 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1439 private final void increaseGlobalDelta(int offset) {
1440 globalDelta += offset;
1443 private final void increaseLineDelta(int offset) {
1444 lineDelta += offset;
1447 private final void increaseSplitDelta(int offset) {
1448 splitDelta += offset;
1452 * Returns true if a space has to be inserted after <code>operator</code>
1455 private boolean insertSpaceAfter(int token) {
1457 case TokenNameLPAREN:
1459 case TokenNameTWIDDLE:
1462 case TokenNameWHITESPACE:
1463 case TokenNameLBRACKET:
1464 case TokenNameDOLLAR:
1465 case Scanner.TokenNameCOMMENT_LINE:
1468 return !options.compactStringConcatenation;
1475 * Returns true if a space has to be inserted before <code>operator</code>
1476 * false otherwise. <br>
1477 * Cannot be static as it uses the code formatter options (to know if the
1478 * compact assignment mode is on).
1480 private boolean insertSpaceBefore(int token) {
1482 case TokenNameEQUAL:
1483 return (!options.compactAssignmentMode);
1489 private static boolean isComment(int token) {
1490 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1491 || token == Scanner.TokenNameCOMMENT_LINE
1492 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1496 private static boolean isLiteralToken(int token) {
1497 boolean result = token == TokenNameIntegerLiteral
1498 // || token == TokenNameLongLiteral
1499 // || token == TokenNameFloatingPointLiteral
1500 || token == TokenNameDoubleLiteral
1501 // || token == TokenNameCharacterLiteral
1502 || token == TokenNameStringDoubleQuote;
1507 * If the length of <code>oneLineBuffer</code> exceeds
1508 * <code>maxLineLength</code>, it is split and the result is dumped in
1509 * <code>formattedSource</code>
1511 * @param newLineCount
1512 * the number of new lines to append
1514 private void newLine(int newLineCount) {
1515 // format current line
1517 beginningOfLineIndex = formattedSource.length();
1518 String currentLine = currentLineBuffer.toString();
1519 if (containsOpenCloseBraces) {
1520 containsOpenCloseBraces = false;
1521 outputLine(currentLine, false, indentationLevelForOpenCloseBraces,
1523 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1525 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1,
1528 // dump line break(s)
1529 for (int i = 0; i < newLineCount; i++) {
1530 formattedSource.append(options.lineSeparatorSequence);
1531 increaseSplitDelta(options.lineSeparatorSequence.length);
1533 // reset formatter for next line
1534 int currentLength = currentLine.length();
1535 currentLineBuffer = new StringBuffer(
1536 currentLength > maxLineSize ? maxLineSize = currentLength
1538 increaseGlobalDelta(splitDelta);
1539 increaseGlobalDelta(lineDelta);
1541 currentLineIndentationLevel = initialIndentationLevel;
1544 private String operatorString(int operator) {
1546 case TokenNameextends:
1547 return "extends"; //$NON-NLS-1$
1548 // case TokenNameimplements :
1549 // return "implements"; //$NON-NLS-1$
1551 // case TokenNamethrows :
1552 // return "throws"; //$NON-NLS-1$
1553 case TokenNameSEMICOLON:
1555 return ";"; //$NON-NLS-1$
1556 case TokenNameCOMMA:
1558 return ","; //$NON-NLS-1$
1559 case TokenNameEQUAL:
1561 return "="; //$NON-NLS-1$
1562 case TokenNameAND_AND:
1564 return "&&"; //$NON-NLS-1$
1565 case TokenNameOR_OR:
1567 return "||"; //$NON-NLS-1$
1568 case TokenNameQUESTION:
1570 return "?"; //$NON-NLS-1$
1571 case TokenNameCOLON:
1573 return ":"; //$NON-NLS-1$
1574 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1576 return "::"; //$NON-NLS-1$
1577 case TokenNameEQUAL_EQUAL:
1578 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1579 return "=="; //$NON-NLS-1$
1580 case TokenNameEQUAL_EQUAL_EQUAL:
1581 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1582 return "==="; //$NON-NLS-1$
1583 case TokenNameEQUAL_GREATER:
1585 return "=>"; //$NON-NLS-1$
1586 case TokenNameNOT_EQUAL:
1587 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1588 return "!="; //$NON-NLS-1$
1589 case TokenNameNOT_EQUAL_EQUAL:
1590 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1591 return "!=="; //$NON-NLS-1$
1594 return "<"; //$NON-NLS-1$
1595 case TokenNameLESS_EQUAL:
1597 return "<="; //$NON-NLS-1$
1598 case TokenNameGREATER:
1600 return ">"; //$NON-NLS-1$
1601 case TokenNameGREATER_EQUAL:
1603 return ">="; //$NON-NLS-1$
1604 // case TokenNameinstanceof : // instanceof
1605 // return "instanceof"; //$NON-NLS-1$
1607 // + (15.17, 15.17.2)
1608 return "+"; //$NON-NLS-1$
1609 case TokenNameMINUS:
1611 return "-"; //$NON-NLS-1$
1612 case TokenNameMULTIPLY:
1614 return "*"; //$NON-NLS-1$
1615 case TokenNameDIVIDE:
1617 return "/"; //$NON-NLS-1$
1618 case TokenNameREMAINDER:
1620 return "%"; //$NON-NLS-1$
1621 case TokenNameLEFT_SHIFT:
1623 return "<<"; //$NON-NLS-1$
1624 case TokenNameRIGHT_SHIFT:
1626 return ">>"; //$NON-NLS-1$
1627 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1628 // return ">>>"; //$NON-NLS-1$
1630 // & (15.21, 15.21.1, 15.21.2)
1631 return "&"; //$NON-NLS-1$
1633 // | (15.21, 15.21.1, 15.21.2)
1634 return "|"; //$NON-NLS-1$
1636 // ^ (15.21, 15.21.1, 15.21.2)
1637 return "^"; //$NON-NLS-1$
1638 case TokenNameMULTIPLY_EQUAL:
1640 return "*="; //$NON-NLS-1$
1641 case TokenNameDIVIDE_EQUAL:
1643 return "/="; //$NON-NLS-1$
1644 case TokenNameREMAINDER_EQUAL:
1646 return "%="; //$NON-NLS-1$
1647 case TokenNamePLUS_EQUAL:
1649 return "+="; //$NON-NLS-1$
1650 case TokenNameMINUS_EQUAL:
1652 return "-="; //$NON-NLS-1$
1653 case TokenNameMINUS_GREATER:
1655 return "->"; //$NON-NLS-1$
1656 case TokenNameLEFT_SHIFT_EQUAL:
1658 return "<<="; //$NON-NLS-1$
1659 case TokenNameRIGHT_SHIFT_EQUAL:
1661 return ">>="; //$NON-NLS-1$
1662 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1663 // return ">>>="; //$NON-NLS-1$
1664 case TokenNameAND_EQUAL:
1666 return "&="; //$NON-NLS-1$
1667 case TokenNameXOR_EQUAL:
1669 return "^="; //$NON-NLS-1$
1670 case TokenNameOR_EQUAL:
1672 return "|="; //$NON-NLS-1$
1673 case TokenNameDOT_EQUAL:
1675 return ".="; //$NON-NLS-1$
1678 return "."; //$NON-NLS-1$
1680 return ""; //$NON-NLS-1$
1685 * Appends <code>stringToOutput</code> to the formatted output. <br>
1686 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1688 private void output(String stringToOutput) {
1689 char currentCharacter;
1690 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1691 currentCharacter = stringToOutput.charAt(i);
1692 if (currentCharacter != '\t') {
1693 currentLineBuffer.append(currentCharacter);
1698 private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
1699 newLine(newLineCount);
1700 formattedSource.append(scanner.source, scanner.startPosition,
1701 scanner.currentPosition - scanner.startPosition);
1705 * Appends <code>token</code> to the formatted output. <br>
1706 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1709 private void outputCurrentToken(int token) {
1710 char[] source = scanner.source;
1711 int startPosition = scanner.startPosition;
1713 case Scanner.TokenNameCOMMENT_PHPDOC:
1714 case Scanner.TokenNameCOMMENT_BLOCK:
1715 case Scanner.TokenNameCOMMENT_LINE:
1716 boolean endOfLine = false;
1717 int currentCommentOffset = getCurrentCommentOffset();
1718 int beginningOfLineSpaces = 0;
1720 currentCommentOffset = getCurrentCommentOffset();
1721 beginningOfLineSpaces = 0;
1722 boolean pendingCarriageReturn = false;
1723 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1724 char currentCharacter = source[i];
1725 updateMappedPositions(i);
1726 switch (currentCharacter) {
1728 pendingCarriageReturn = true;
1732 if (pendingCarriageReturn) {
1733 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1735 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1737 pendingCarriageReturn = false;
1738 currentLineBuffer.append(options.lineSeparatorSequence);
1739 beginningOfLineSpaces = 0;
1743 if (pendingCarriageReturn) {
1744 pendingCarriageReturn = false;
1745 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1746 currentLineBuffer.append(options.lineSeparatorSequence);
1747 beginningOfLineSpaces = 0;
1751 // we remove a maximum of currentCommentOffset
1753 // are converted to space numbers).
1754 beginningOfLineSpaces += options.tabSize;
1755 if (beginningOfLineSpaces > currentCommentOffset) {
1756 currentLineBuffer.append(currentCharacter);
1758 increaseGlobalDelta(-1);
1761 currentLineBuffer.append(currentCharacter);
1765 if (pendingCarriageReturn) {
1766 pendingCarriageReturn = false;
1767 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1768 currentLineBuffer.append(options.lineSeparatorSequence);
1769 beginningOfLineSpaces = 0;
1773 // we remove a maximum of currentCommentOffset
1775 // are converted to space numbers).
1776 beginningOfLineSpaces++;
1777 if (beginningOfLineSpaces > currentCommentOffset) {
1778 currentLineBuffer.append(currentCharacter);
1780 increaseGlobalDelta(-1);
1783 currentLineBuffer.append(currentCharacter);
1787 if (pendingCarriageReturn) {
1788 pendingCarriageReturn = false;
1789 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1790 currentLineBuffer.append(options.lineSeparatorSequence);
1791 beginningOfLineSpaces = 0;
1794 beginningOfLineSpaces = 0;
1795 currentLineBuffer.append(currentCharacter);
1800 updateMappedPositions(scanner.currentPosition - 1);
1801 multipleLineCommentCounter++;
1804 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1805 char currentCharacter = source[i];
1806 updateMappedPositions(i);
1807 currentLineBuffer.append(currentCharacter);
1813 * Outputs <code>currentString</code>:<br>
1815 * <li>If its length is < maxLineLength, output
1816 * <li>Otherwise it is split.
1819 * @param currentString
1821 * @param preIndented
1822 * whether the string to output was pre-indented
1824 * number of indentation to put in front of
1825 * <code>currentString</code>
1827 * value of the operator belonging to <code>currentString</code>.
1829 private void outputLine(String currentString, boolean preIndented,
1830 int depth, int operator, int substringIndex,
1831 int[] startSubstringIndexes, int offsetInGlobalLine) {
1832 boolean emptyFirstSubString = false;
1833 String operatorString = operatorString(operator);
1834 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1835 boolean placeOperatorAhead = !placeOperatorBehind;
1836 // dump prefix operator?
1837 if (placeOperatorAhead) {
1842 if (operator != 0) {
1843 if (insertSpaceBefore(operator)) {
1844 formattedSource.append(' ');
1845 increaseSplitDelta(1);
1847 formattedSource.append(operatorString);
1848 increaseSplitDelta(operatorString.length());
1849 if (insertSpaceAfter(operator)
1850 && operator != TokenNameimplements
1851 && operator != TokenNameextends) {
1852 // && operator != TokenNamethrows) {
1853 formattedSource.append(' ');
1854 increaseSplitDelta(1);
1858 SplitLine splitLine = null;
1859 if (options.maxLineLength == 0
1860 || getLength(currentString, depth) < options.maxLineLength
1861 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1862 // depending on the type of operator, outputs new line before of
1865 // indent before postfix operator
1866 // indent also when the line cannot be split
1867 if (operator == TokenNameextends || operator == TokenNameimplements) {
1868 // || operator == TokenNamethrows) {
1869 formattedSource.append(' ');
1870 increaseSplitDelta(1);
1872 if (placeOperatorBehind) {
1877 int max = currentString.length();
1878 if (multipleLineCommentCounter != 0) {
1880 BufferedReader reader = new BufferedReader(
1881 new StringReader(currentString));
1882 String line = reader.readLine();
1883 while (line != null) {
1884 updateMappedPositionsWhileSplitting(
1885 beginningOfLineIndex, beginningOfLineIndex
1887 + options.lineSeparatorSequence.length);
1888 formattedSource.append(line);
1889 beginningOfLineIndex = beginningOfLineIndex
1891 if ((line = reader.readLine()) != null) {
1893 .append(options.lineSeparatorSequence);
1894 beginningOfLineIndex += options.lineSeparatorSequence.length;
1895 dumpTab(currentLineIndentationLevel);
1899 } catch (IOException e) {
1900 e.printStackTrace();
1903 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1904 beginningOfLineIndex + max);
1905 for (int i = 0; i < max; i++) {
1906 char currentChar = currentString.charAt(i);
1907 switch (currentChar) {
1912 // fix for 1FFYL5C: LFCOM:ALL - Incorrect
1914 // split with a comment inside a condition
1915 // a substring cannot end with a
1916 // lineSeparatorSequence,
1917 // except if it has been added by format() after a
1921 .append(options.lineSeparatorSequence);
1922 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
1928 formattedSource.append(currentChar);
1932 // update positions inside the mappedPositions table
1933 if (substringIndex != -1) {
1934 if (multipleLineCommentCounter == 0) {
1935 int startPosition = beginningOfLineIndex
1936 + startSubstringIndexes[substringIndex];
1937 updateMappedPositionsWhileSplitting(startPosition,
1938 startPosition + max);
1940 // compute the splitDelta resulting with the operator and blank
1942 if (substringIndex + 1 != startSubstringIndexes.length) {
1943 increaseSplitDelta(startSubstringIndexes[substringIndex]
1944 + max - startSubstringIndexes[substringIndex + 1]);
1947 // dump postfix operator?
1948 if (placeOperatorBehind) {
1949 if (insertSpaceBefore(operator)) {
1950 formattedSource.append(' ');
1951 if (operator != 0) {
1952 increaseSplitDelta(1);
1955 formattedSource.append(operatorString);
1956 if (operator != 0) {
1957 increaseSplitDelta(operatorString.length());
1962 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1963 // extends has to stand alone on a line when currentString has been
1965 if (options.maxLineLength != 0 && splitLine != null
1966 && (operator == TokenNameextends)) {
1967 // || operator == TokenNameimplements
1968 // || operator == TokenNamethrows)) {
1969 formattedSource.append(options.lineSeparatorSequence);
1970 increaseSplitDelta(options.lineSeparatorSequence.length);
1973 if (operator == TokenNameextends) {
1974 // || operator == TokenNameimplements
1975 // || operator == TokenNamethrows) {
1976 formattedSource.append(' ');
1977 increaseSplitDelta(1);
1980 // perform actual splitting
1981 String result[] = splitLine.substrings;
1982 int[] splitOperators = splitLine.operators;
1983 if (result[0].length() == 0) {
1984 // when the substring 0 is null, the substring 1 is correctly
1987 emptyFirstSubString = true;
1989 // the operator going in front of the result[0] string is the operator
1991 for (int i = 0, max = result.length; i < max; i++) {
1992 // the new depth is the current one if this is the first substring,
1993 // the current one + 1 otherwise.
1994 // if the substring is a comment, use the current indentation Level
1995 // instead of the depth
1996 // (-1 because the ouputline increases depth).
1997 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of
2000 String currentResult = result[i];
2001 if (currentResult.length() != 0 || splitOperators[i] != 0) {
2002 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
2003 || currentResult.startsWith("//")) //$NON-NLS-1$
2004 ? indentationLevel - 1
2006 outputLine(currentResult, i == 0
2007 || (i == 1 && emptyFirstSubString) ? preIndented
2008 : false, i == 0 ? newDepth : newDepth + 1,
2009 splitOperators[i], i, splitLine.startSubstringsIndexes,
2010 currentString.indexOf(currentResult));
2012 formattedSource.append(options.lineSeparatorSequence);
2013 increaseSplitDelta(options.lineSeparatorSequence.length);
2017 if (result.length == splitOperators.length - 1) {
2018 int lastOperator = splitOperators[result.length];
2019 String lastOperatorString = operatorString(lastOperator);
2020 formattedSource.append(options.lineSeparatorSequence);
2021 increaseSplitDelta(options.lineSeparatorSequence.length);
2022 if (breakLineBeforeOperator(lastOperator)) {
2024 if (lastOperator != 0) {
2025 if (insertSpaceBefore(lastOperator)) {
2026 formattedSource.append(' ');
2027 increaseSplitDelta(1);
2029 formattedSource.append(lastOperatorString);
2030 increaseSplitDelta(lastOperatorString.length());
2031 if (insertSpaceAfter(lastOperator)
2032 && lastOperator != TokenNameimplements
2033 && lastOperator != TokenNameextends) {
2034 // && lastOperator != TokenNamethrows) {
2035 formattedSource.append(' ');
2036 increaseSplitDelta(1);
2041 if (placeOperatorBehind) {
2042 if (insertSpaceBefore(operator)) {
2043 formattedSource.append(' ');
2044 increaseSplitDelta(1);
2046 formattedSource.append(operatorString);
2047 // increaseSplitDelta(operatorString.length());
2052 * Pops the top statement of the stack if it is <code>token</code>
2054 private int pop(int token) {
2056 if ((constructionsCount > 0)
2057 && (constructions[constructionsCount - 1] == token)) {
2059 constructionsCount--;
2065 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
2066 * <code>NONINDENT_BLOCK</code>.
2068 private int popBlock() {
2070 if ((constructionsCount > 0)
2071 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
2072 if (constructions[constructionsCount - 1] == BLOCK)
2074 constructionsCount--;
2080 * Pops elements until the stack is empty or the top element is
2081 * <code>token</code>.<br>
2082 * Does not remove <code>token</code> from the stack.
2085 * the token to be left as the top of the stack
2087 private int popExclusiveUntil(int token) {
2089 int startCount = constructionsCount;
2090 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2091 if (constructions[i] != NONINDENT_BLOCK)
2093 constructionsCount--;
2099 * Pops elements until the stack is empty or the top element is a
2100 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2101 * Does not remove it from the stack.
2103 private int popExclusiveUntilBlock() {
2104 int startCount = constructionsCount;
2106 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2107 && constructions[i] != NONINDENT_BLOCK; i--) {
2108 constructionsCount--;
2115 * Pops elements until the stack is empty or the top element is a
2116 * <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
2117 * <code>CASE</code>.<br>
2118 * Does not remove it from the stack.
2120 private int popExclusiveUntilBlockOrCase() {
2121 int startCount = constructionsCount;
2123 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2124 && constructions[i] != NONINDENT_BLOCK
2125 && constructions[i] != TokenNamecase; i--) {
2126 constructionsCount--;
2133 * Pops elements until the stack is empty or the top element is
2134 * <code>token</code>.<br>
2135 * Removes <code>token</code> from the stack too.
2138 * the token to remove from the stack
2140 private int popInclusiveUntil(int token) {
2141 int startCount = constructionsCount;
2143 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2144 if (constructions[i] != NONINDENT_BLOCK)
2146 constructionsCount--;
2148 if (constructionsCount > 0) {
2149 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
2151 constructionsCount--;
2157 * Pops elements until the stack is empty or the top element is a
2158 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2159 * Does not remove it from the stack.
2161 private int popInclusiveUntilBlock() {
2162 int startCount = constructionsCount;
2164 for (int i = startCount - 1; i >= 0
2165 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
2167 constructionsCount--;
2169 if (constructionsCount > 0) {
2170 if (constructions[constructionsCount - 1] == BLOCK)
2172 constructionsCount--;
2178 * Pushes a block in the stack. <br>
2179 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
2180 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
2181 * otherwise. Creates a new bigger array if the current one is full.
2183 private int pushBlock() {
2185 if (constructionsCount == constructions.length)
2186 System.arraycopy(constructions, 0,
2187 (constructions = new int[constructionsCount * 2]), 0,
2188 constructionsCount);
2189 if ((constructionsCount == 0)
2190 || (constructions[constructionsCount - 1] == BLOCK)
2191 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
2192 || (constructions[constructionsCount - 1] == TokenNamecase)) {
2194 constructions[constructionsCount++] = BLOCK;
2196 constructions[constructionsCount++] = NONINDENT_BLOCK;
2202 * Pushes <code>token</code>.<br>
2203 * Creates a new bigger array if the current one is full.
2205 private int pushControlStatement(int token) {
2206 if (constructionsCount == constructions.length)
2207 System.arraycopy(constructions, 0,
2208 (constructions = new int[constructionsCount * 2]), 0,
2209 constructionsCount);
2210 constructions[constructionsCount++] = token;
2214 private static boolean separateFirstArgumentOn(int currentToken) {
2215 // return (currentToken == TokenNameCOMMA || currentToken ==
2216 // TokenNameSEMICOLON);
2217 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
2218 && currentToken != TokenNameNOT
2219 && currentToken != TokenNamewhile
2220 && currentToken != TokenNamefor
2221 && currentToken != TokenNameforeach
2222 && currentToken != TokenNameswitch;
2226 * Set the positions to map. The mapped positions should be retrieved using
2227 * the getMappedPositions() method.
2231 * @deprecated Set the positions to map using the format(String, int, int[])
2234 * @see #getMappedPositions()
2236 public void setPositionsToMap(int[] positions) {
2237 positionsToMap = positions;
2240 mappedPositions = new int[positions.length];
2244 * Appends a space character to the current line buffer.
2246 private void space() {
2247 currentLineBuffer.append(' ');
2248 increaseLineDelta(1);
2252 * Splits <code>stringToSplit</code> on the top level token <br>
2253 * If there are several identical token at the same level, the string is cut
2256 * @return an object containing the operator and all the substrings or null
2257 * if the string cannot be split
2259 public SplitLine split(String stringToSplit) {
2260 return split(stringToSplit, 0);
2264 * Splits <code>stringToSplit</code> on the top level token <br>
2265 * If there are several identical token at the same level, the string is cut
2268 * @return an object containing the operator and all the substrings or null
2269 * if the string cannot be split
2271 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2273 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2274 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2276 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2279 // split doesn't work correct for PHP
2282 // int currentToken = 0;
2283 // int splitTokenType = 0;
2284 // int splitTokenDepth = Integer.MAX_VALUE;
2285 // int splitTokenPriority = Integer.MAX_VALUE;
2286 // int[] substringsStartPositions = new int[10];
2287 // // contains the start position of substrings
2288 // int[] substringsEndPositions = new int[10];
2289 // // contains the start position of substrings
2290 // int substringsCount = 1; // index in the substringsStartPosition
2292 // int[] splitOperators = new int[10];
2293 // // contains the start position of substrings
2294 // int splitOperatorsCount = 0; // index in the substringsStartPosition
2296 // int[] openParenthesisPosition = new int[10];
2297 // int openParenthesisPositionCount = 0;
2298 // int position = 0;
2299 // int lastOpenParenthesisPosition = -1;
2300 // // used to remember the position of the 1st open parenthesis
2301 // // needed for a pattern like: A.B(C); we want formatted like A.B(
2303 // // setup the scanner with a new source
2304 // int lastCommentStartPosition = -1;
2305 // // to remember the start position of the last comment
2306 // int firstTokenOnLine = -1;
2307 // // to remember the first token of the line
2308 // int previousToken = -1;
2309 // // to remember the previous token.
2310 // splitScanner.setSource(stringToSplit.toCharArray());
2312 // // start the loop
2314 // // takes the next token
2316 // if (currentToken != Scanner.TokenNameWHITESPACE)
2317 // previousToken = currentToken;
2318 // currentToken = splitScanner.getNextToken();
2319 // if (Scanner.DEBUG) {
2320 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2321 // int currentStartPosition = splitScanner
2322 // .getCurrentTokenStartPosition();
2323 // System.out.print(currentStartPosition + "," + currentEndPosition
2325 // System.out.println(scanner.toStringAction(currentToken));
2327 // } catch (InvalidInputException e) {
2328 // if (!handleInvalidToken(e))
2330 // currentToken = 0;
2331 // // this value is not modify when an exception is raised.
2333 // if (currentToken == TokenNameEOF)
2335 // if (firstTokenOnLine == -1) {
2336 // firstTokenOnLine = currentToken;
2338 // switch (currentToken) {
2339 // case TokenNameRBRACE :
2340 // case TokenNameRPAREN :
2341 // if (openParenthesisPositionCount > 0) {
2342 // if (openParenthesisPositionCount == 1
2343 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2344 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2345 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2346 // || (splitTokenDepth > openParenthesisPositionCount &&
2347 // openParenthesisPositionCount == 1)) {
2348 // splitTokenType = 0;
2349 // splitTokenDepth = openParenthesisPositionCount;
2350 // splitTokenPriority = Integer.MAX_VALUE;
2351 // substringsStartPositions[0] = 0;
2352 // // better token means the whole line until now is the first
2354 // substringsCount = 1; // resets the count of substrings
2355 // substringsEndPositions[0] = openParenthesisPosition[0];
2356 // // substring ends on operator start
2357 // position = openParenthesisPosition[0];
2358 // // the string mustn't be cut before the closing parenthesis but
2359 // // after the opening one.
2360 // splitOperatorsCount = 1; // resets the count of split operators
2361 // splitOperators[0] = 0;
2363 // openParenthesisPositionCount--;
2366 // case TokenNameLBRACE :
2367 // case TokenNameLPAREN :
2368 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2371 // openParenthesisPosition,
2373 // (openParenthesisPosition = new int[openParenthesisPositionCount *
2375 // 0, openParenthesisPositionCount);
2377 // openParenthesisPosition[openParenthesisPositionCount++] =
2378 // splitScanner.currentPosition;
2379 // if (currentToken == TokenNameLPAREN
2380 // && previousToken == TokenNameRPAREN) {
2381 // openParenthesisPosition[openParenthesisPositionCount - 1] =
2382 // splitScanner.startPosition;
2385 // case TokenNameSEMICOLON :
2387 // case TokenNameCOMMA :
2389 // case TokenNameEQUAL :
2391 // if (openParenthesisPositionCount < splitTokenDepth
2392 // || (openParenthesisPositionCount == splitTokenDepth &&
2393 // splitTokenPriority > getTokenPriority(currentToken))) {
2394 // // the current token is better than the one we currently have
2395 // // (in level or in priority if same level)
2396 // // reset the substringsCount
2397 // splitTokenDepth = openParenthesisPositionCount;
2398 // splitTokenType = currentToken;
2399 // splitTokenPriority = getTokenPriority(currentToken);
2400 // substringsStartPositions[0] = 0;
2401 // // better token means the whole line until now is the first
2403 // if (separateFirstArgumentOn(firstTokenOnLine)
2404 // && openParenthesisPositionCount > 0) {
2405 // substringsCount = 2; // resets the count of substrings
2406 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2408 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2410 // substringsEndPositions[1] = splitScanner.startPosition;
2411 // splitOperatorsCount = 2; // resets the count of split operators
2412 // splitOperators[0] = 0;
2413 // splitOperators[1] = currentToken;
2414 // position = splitScanner.currentPosition;
2415 // // next substring will start from operator end
2417 // substringsCount = 1; // resets the count of substrings
2418 // substringsEndPositions[0] = splitScanner.startPosition;
2419 // // substring ends on operator start
2420 // position = splitScanner.currentPosition;
2421 // // next substring will start from operator end
2422 // splitOperatorsCount = 1; // resets the count of split operators
2423 // splitOperators[0] = currentToken;
2426 // if ((openParenthesisPositionCount == splitTokenDepth &&
2427 // splitTokenPriority == getTokenPriority(currentToken))
2428 // && splitTokenType != TokenNameEQUAL
2429 // && currentToken != TokenNameEQUAL) {
2430 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2432 // // take only the 1st = into account.
2433 // // if another token with the same priority is found,
2434 // // push the start position of the substring and
2435 // // push the token into the stack.
2436 // // create a new array object if the current one is full.
2437 // if (substringsCount == substringsStartPositions.length) {
2440 // substringsStartPositions,
2442 // (substringsStartPositions = new int[substringsCount * 2]),
2443 // 0, substringsCount);
2444 // System.arraycopy(substringsEndPositions, 0,
2445 // (substringsEndPositions = new int[substringsCount * 2]),
2446 // 0, substringsCount);
2448 // if (splitOperatorsCount == splitOperators.length) {
2449 // System.arraycopy(splitOperators, 0,
2450 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2451 // splitOperatorsCount);
2453 // substringsStartPositions[substringsCount] = position;
2454 // substringsEndPositions[substringsCount++] =
2455 // splitScanner.startPosition;
2456 // // substring ends on operator start
2457 // position = splitScanner.currentPosition;
2458 // // next substring will start from operator end
2459 // splitOperators[splitOperatorsCount++] = currentToken;
2463 // case TokenNameCOLON :
2465 // // see 1FK7C5R, we only split on a colon, when it is associated
2466 // // with a question-mark.
2467 // // indeed it might appear also behind a case statement, and we do
2468 // // not to break at this point.
2469 // if ((splitOperatorsCount == 0)
2470 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2473 // case TokenNameextends :
2474 // case TokenNameimplements :
2475 // //case TokenNamethrows :
2476 // case TokenNameDOT :
2478 // case TokenNameMULTIPLY :
2480 // case TokenNameDIVIDE :
2482 // case TokenNameREMAINDER :
2484 // case TokenNamePLUS :
2485 // // + (15.17, 15.17.2)
2486 // case TokenNameMINUS :
2488 // case TokenNameLEFT_SHIFT :
2490 // case TokenNameRIGHT_SHIFT :
2492 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2493 // case TokenNameLESS :
2495 // case TokenNameLESS_EQUAL :
2497 // case TokenNameGREATER :
2499 // case TokenNameGREATER_EQUAL :
2501 // // case TokenNameinstanceof : // instanceof
2502 // case TokenNameEQUAL_EQUAL :
2503 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2504 // case TokenNameEQUAL_EQUAL_EQUAL :
2505 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2506 // case TokenNameNOT_EQUAL :
2507 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2508 // case TokenNameNOT_EQUAL_EQUAL :
2509 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2510 // case TokenNameAND :
2511 // // & (15.21, 15.21.1, 15.21.2)
2512 // case TokenNameOR :
2513 // // | (15.21, 15.21.1, 15.21.2)
2514 // case TokenNameXOR :
2515 // // ^ (15.21, 15.21.1, 15.21.2)
2516 // case TokenNameAND_AND :
2518 // case TokenNameOR_OR :
2520 // case TokenNameQUESTION :
2522 // case TokenNameMULTIPLY_EQUAL :
2524 // case TokenNameDIVIDE_EQUAL :
2526 // case TokenNameREMAINDER_EQUAL :
2528 // case TokenNamePLUS_EQUAL :
2530 // case TokenNameMINUS_EQUAL :
2532 // case TokenNameLEFT_SHIFT_EQUAL :
2534 // case TokenNameRIGHT_SHIFT_EQUAL :
2536 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2537 // case TokenNameAND_EQUAL :
2539 // case TokenNameXOR_EQUAL :
2541 // case TokenNameOR_EQUAL :
2543 // if ((openParenthesisPositionCount < splitTokenDepth ||
2544 // (openParenthesisPositionCount == splitTokenDepth &&
2545 // splitTokenPriority
2546 // > getTokenPriority(currentToken)))
2547 // && !((currentToken == TokenNamePLUS || currentToken ==
2548 // TokenNameMINUS) && (previousToken == TokenNameLBRACE
2549 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition
2551 // // the current token is better than the one we currently have
2552 // // (in level or in priority if same level)
2553 // // reset the substringsCount
2554 // splitTokenDepth = openParenthesisPositionCount;
2555 // splitTokenType = currentToken;
2556 // splitTokenPriority = getTokenPriority(currentToken);
2557 // substringsStartPositions[0] = 0;
2558 // // better token means the whole line until now is the first
2560 // if (separateFirstArgumentOn(firstTokenOnLine)
2561 // && openParenthesisPositionCount > 0) {
2562 // substringsCount = 2; // resets the count of substrings
2563 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2565 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2567 // substringsEndPositions[1] = splitScanner.startPosition;
2568 // splitOperatorsCount = 3; // resets the count of split operators
2569 // splitOperators[0] = 0;
2570 // splitOperators[1] = 0;
2571 // splitOperators[2] = currentToken;
2572 // position = splitScanner.currentPosition;
2573 // // next substring will start from operator end
2575 // substringsCount = 1; // resets the count of substrings
2576 // substringsEndPositions[0] = splitScanner.startPosition;
2577 // // substring ends on operator start
2578 // position = splitScanner.currentPosition;
2579 // // next substring will start from operator end
2580 // splitOperatorsCount = 2; // resets the count of split operators
2581 // splitOperators[0] = 0;
2582 // // nothing for first operand since operator will be inserted in
2583 // // front of the second operand
2584 // splitOperators[1] = currentToken;
2587 // if (openParenthesisPositionCount == splitTokenDepth
2588 // && splitTokenPriority == getTokenPriority(currentToken)) {
2589 // // if another token with the same priority is found,
2590 // // push the start position of the substring and
2591 // // push the token into the stack.
2592 // // create a new array object if the current one is full.
2593 // if (substringsCount == substringsStartPositions.length) {
2596 // substringsStartPositions,
2598 // (substringsStartPositions = new int[substringsCount * 2]),
2599 // 0, substringsCount);
2600 // System.arraycopy(substringsEndPositions, 0,
2601 // (substringsEndPositions = new int[substringsCount * 2]),
2602 // 0, substringsCount);
2604 // if (splitOperatorsCount == splitOperators.length) {
2605 // System.arraycopy(splitOperators, 0,
2606 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2607 // splitOperatorsCount);
2609 // substringsStartPositions[substringsCount] = position;
2610 // substringsEndPositions[substringsCount++] =
2611 // splitScanner.startPosition;
2612 // // substring ends on operator start
2613 // position = splitScanner.currentPosition;
2614 // // next substring will start from operator end
2615 // splitOperators[splitOperatorsCount++] = currentToken;
2621 // if (isComment(currentToken)) {
2622 // lastCommentStartPosition = splitScanner.startPosition;
2624 // lastCommentStartPosition = -1;
2627 // } catch (InvalidInputException e) {
2630 // // if the string cannot be split, return null.
2631 // if (splitOperatorsCount == 0)
2633 // // ## SPECIAL CASES BEGIN
2634 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2635 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2636 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2637 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 &&
2638 // lastOpenParenthesisPosition <= options.maxLineLength) ||
2639 // (separateFirstArgumentOn(firstTokenOnLine)
2640 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2641 // && (lastOpenParenthesisPosition < splitScanner.source.length &&
2642 // splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2643 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis
2645 // // not be broken on two lines
2646 // // only one split on a top level .
2647 // // or more than one split on . and substring before open parenthesis
2650 // // or split inside parenthesis and first token is not a for/while/if
2651 // SplitLine sl = split(
2652 // stringToSplit.substring(lastOpenParenthesisPosition),
2653 // lastOpenParenthesisPosition);
2654 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2655 // // trim() is used to remove the extra blanks at the end of the
2656 // // substring. See PR 1FGYPI1
2657 // return new SplitLine(new int[]{0, 0}, new String[]{
2658 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2659 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2660 // offsetInGlobalLine,
2661 // lastOpenParenthesisPosition + offsetInGlobalLine});
2663 // // right substring can be split and is split on comma
2664 // // copy substrings and operators
2665 // // except if the 1st string is empty.
2666 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2667 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2668 // String[] result = new String[subStringsLength];
2669 // int[] startIndexes = new int[subStringsLength];
2670 // int operatorsLength = sl.operators.length + 1 - startIndex;
2671 // int[] operators = new int[operatorsLength];
2672 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2673 // operators[0] = 0;
2674 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2675 // 1, subStringsLength - 1);
2676 // for (int i = subStringsLength - 1; i >= 0; i--) {
2677 // startIndexes[i] += offsetInGlobalLine;
2679 // System.arraycopy(sl.substrings, startIndex, result, 1,
2680 // subStringsLength - 1);
2681 // System.arraycopy(sl.operators, startIndex, operators, 1,
2682 // operatorsLength - 1);
2683 // return new SplitLine(operators, result, startIndexes);
2686 // // if the last token is a comment and the substring before the
2689 // // split before the comment and return the result.
2690 // if (lastCommentStartPosition > -1
2691 // && lastCommentStartPosition < options.maxLineLength
2692 // && splitTokenPriority > 50) {
2693 // int end = lastCommentStartPosition;
2694 // int start = lastCommentStartPosition;
2695 // if (stringToSplit.charAt(end - 1) == ' ') {
2698 // if (start != end && stringToSplit.charAt(start) == ' ') {
2701 // return new SplitLine(new int[]{0, 0}, new String[]{
2702 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2703 // new int[]{0, start});
2705 // if (position != stringToSplit.length()) {
2706 // if (substringsCount == substringsStartPositions.length) {
2707 // System.arraycopy(substringsStartPositions, 0,
2708 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2709 // substringsCount);
2710 // System.arraycopy(substringsEndPositions, 0,
2711 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2712 // substringsCount);
2714 // // avoid empty extra substring, e.g. line terminated with a
2716 // substringsStartPositions[substringsCount] = position;
2717 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2719 // if (splitOperatorsCount == splitOperators.length) {
2720 // System.arraycopy(splitOperators, 0,
2721 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2722 // splitOperatorsCount);
2724 // splitOperators[splitOperatorsCount] = 0;
2725 // // the last element of the stack is the position of the end of
2727 // // +1 because the substring method excludes the last character
2728 // String[] result = new String[substringsCount];
2729 // for (int i = 0; i < substringsCount; i++) {
2730 // int start = substringsStartPositions[i];
2731 // int end = substringsEndPositions[i];
2732 // if (stringToSplit.charAt(start) == ' ') {
2734 // substringsStartPositions[i]++;
2736 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2739 // result[i] = stringToSplit.substring(start, end);
2740 // substringsStartPositions[i] += offsetInGlobalLine;
2742 // if (splitOperatorsCount > substringsCount) {
2743 // System.arraycopy(substringsStartPositions, 0,
2744 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2745 // substringsCount);
2746 // System.arraycopy(substringsEndPositions, 0,
2747 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2748 // substringsCount);
2749 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2750 // substringsStartPositions[i] = position;
2751 // substringsEndPositions[i] = position;
2753 // System.arraycopy(splitOperators, 0,
2754 // (splitOperators = new int[splitOperatorsCount]), 0,
2755 // splitOperatorsCount);
2757 // System.arraycopy(substringsStartPositions, 0,
2758 // (substringsStartPositions = new int[substringsCount]), 0,
2759 // substringsCount);
2760 // System.arraycopy(substringsEndPositions, 0,
2761 // (substringsEndPositions = new int[substringsCount]), 0,
2762 // substringsCount);
2763 // System.arraycopy(splitOperators, 0,
2764 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2766 // SplitLine splitLine = new SplitLine(splitOperators, result,
2767 // substringsStartPositions);
2768 // return splitLine;
2771 private void updateMappedPositions(int startPosition) {
2772 if (positionsToMap == null) {
2775 char[] source = scanner.source;
2776 int sourceLength = source.length;
2777 while (indexToMap < positionsToMap.length
2778 && positionsToMap[indexToMap] <= startPosition) {
2779 int posToMap = positionsToMap[indexToMap];
2780 if (posToMap < 0 || posToMap >= sourceLength) {
2781 // protection against out of bounds position
2782 if (posToMap == sourceLength) {
2783 mappedPositions[indexToMap] = formattedSource.length();
2785 indexToMap = positionsToMap.length; // no more mapping
2788 if (CharOperation.isWhitespace(source[posToMap])) {
2789 mappedPositions[indexToMap] = startPosition + globalDelta
2792 if (posToMap == sourceLength - 1) {
2793 mappedPositions[indexToMap] = startPosition + globalDelta
2796 mappedPositions[indexToMap] = posToMap + globalDelta
2804 private void updateMappedPositionsWhileSplitting(int startPosition,
2806 if (mappedPositions == null || mappedPositions.length == indexInMap)
2808 while (indexInMap < mappedPositions.length
2809 && startPosition <= mappedPositions[indexInMap]
2810 && mappedPositions[indexInMap] < endPosition
2811 && indexInMap < indexToMap) {
2812 mappedPositions[indexInMap] += splitDelta;
2817 private int getLength(String s, int tabDepth) {
2819 for (int i = 0; i < tabDepth; i++) {
2820 length += options.tabSize;
2822 for (int i = 0, max = s.length(); i < max; i++) {
2823 char currentChar = s.charAt(i);
2824 switch (currentChar) {
2826 length += options.tabSize;
2836 * Sets the initial indentation level
2838 * @param indentationLevel
2839 * new indentation level
2843 public void setInitialIndentationLevel(int newIndentationLevel) {
2844 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;