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 net.sourceforge.phpdt.core.ICodeFormatter;
22 import net.sourceforge.phpdt.core.compiler.CharOperation;
23 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
24 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
25 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
26 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
27 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
28 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
31 * <h2>How to format a piece of code ?</h2>
33 * <li>Create an instance of <code>CodeFormatter</code>
34 * <li>Use the method <code>void format(aString)</code> on this instance to
35 * format <code>aString</code>. It will return the formatted string.
38 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
39 // IContentFormatterExtension {
40 public FormatterOptions options;
43 * Represents a block in the <code>constructions</code> stack.
45 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
48 * Represents a block following a control statement in the
49 * <code>constructions</code> stack.
51 public static final int NONINDENT_BLOCK = -100;
54 * Contains the formatted output.
56 StringBuffer formattedSource;
59 * Contains the current line. <br>
60 * Will be dumped at the next "newline"
62 StringBuffer currentLineBuffer;
65 * Used during the formatting to get each token.
70 * Contains the tokens responsible for the current indentation level and the
71 * blocks not closed yet.
73 private int[] constructions;
76 * Index in the <code>constructions</code> array.
78 private int constructionsCount;
81 * Level of indentation of the current token (number of tab char put in
84 private int indentationLevel;
87 * Regular level of indentation of all the lines
89 private int initialIndentationLevel;
92 * Used to split a line.
97 * To remember the offset between the beginning of the line and the
98 * beginning of the comment.
100 int currentCommentOffset;
102 int currentLineIndentationLevel;
104 int maxLineSize = 30;
106 private boolean containsOpenCloseBraces;
108 private int indentationLevelForOpenCloseBraces;
111 * Collections of positions to map
113 private int[] positionsToMap;
116 * Collections of mapped positions
118 private int[] mappedPositions;
120 private int indexToMap;
122 private int indexInMap;
124 private int globalDelta;
126 private int lineDelta;
128 private int splitDelta;
130 private int beginningOfLineIndex;
132 private int multipleLineCommentCounter;
135 * Creates a new instance of Code Formatter using the given settings.
137 * @deprecated backport 1.0 internal functionality
139 public CodeFormatter(ConfigurableOption[] settings) {
140 this(convertConfigurableOptions(settings));
144 * Creates a new instance of Code Formatter using the FormattingOptions
145 * object given as argument
147 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
149 public CodeFormatter() {
154 * Creates a new instance of Code Formatter using the given settings.
156 public CodeFormatter(Map settings) {
157 // initialize internal state
158 constructionsCount = 0;
159 constructions = new int[10];
160 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
161 currentCommentOffset = -1;
162 // initialize primary and secondary scanners
163 scanner = new Scanner(true /* comment */
164 , true /* whitespace */
167 , true, /* tokenizeStrings */
168 null, null, true /* taskCaseSensitive */); // regular scanner for
170 scanner.recordLineSeparator = true;
171 scanner.ignorePHPOneLiner = true;
172 // to remind of the position of the beginning of the line.
173 splitScanner = new Scanner(true /* comment */
174 , true /* whitespace */
177 , true, /* tokenizeStrings */
178 null, null, true /* taskCaseSensitive */);
179 splitScanner.ignorePHPOneLiner = true;
180 // secondary scanner to split long lines formed by primary scanning
181 // initialize current line buffer
182 currentLineBuffer = new StringBuffer();
183 this.options = new FormatterOptions(settings);
187 * Returns true if a lineSeparator has to be inserted before
188 * <code>operator</code> false otherwise.
190 private static boolean breakLineBeforeOperator(int operator) {
193 case TokenNameSEMICOLON:
202 * @deprecated backport 1.0 internal functionality
204 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
205 Hashtable options = new Hashtable(10);
206 for (int i = 0; i < settings.length; i++) {
207 if (settings[i].getComponentName().equals(
208 CodeFormatter.class.getName())) {
209 String optionName = settings[i].getOptionName();
210 int valueIndex = settings[i].getCurrentValueIndex();
211 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
214 "net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
215 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
218 "net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
219 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
222 "net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
223 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
226 "net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
227 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
230 "net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
231 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
234 "net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
235 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
238 "net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
239 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
242 "net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
243 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
246 "net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
254 * Returns the end of the source code.
256 private final String copyRemainingSource() {
257 char str[] = scanner.source;
258 int startPosition = scanner.startPosition;
259 int length = str.length - startPosition;
260 StringBuffer bufr = new StringBuffer(length);
261 if (startPosition < str.length) {
262 bufr.append(str, startPosition, length);
264 return (bufr.toString());
268 * Inserts <code>tabCount</code> tab character or their equivalent number
271 private void dumpTab(int tabCount) {
272 if (options.indentWithTab) {
273 for (int j = 0; j < tabCount; j++) {
274 formattedSource.append('\t');
275 increaseSplitDelta(1);
278 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
279 formattedSource.append(' ');
280 increaseSplitDelta(1);
286 * Dumps <code>currentLineBuffer</code> into the formatted string.
288 private void flushBuffer() {
289 String currentString = currentLineBuffer.toString();
291 beginningOfLineIndex = formattedSource.length();
292 if (containsOpenCloseBraces) {
293 containsOpenCloseBraces = false;
294 outputLine(currentString, false,
295 indentationLevelForOpenCloseBraces, 0, -1, null, 0);
296 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
298 outputLine(currentString, false, currentLineIndentationLevel, 0,
301 int scannerSourceLength = scanner.source.length;
302 if ((scannerSourceLength > 2)
303 && (scanner.startPosition < scannerSourceLength)) {
304 if (scanner.source[scannerSourceLength - 1] == '\n'
305 && scanner.source[scannerSourceLength - 2] == '\r') {
306 formattedSource.append(options.lineSeparatorSequence);
307 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
308 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
309 formattedSource.append(options.lineSeparatorSequence);
310 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
311 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
312 formattedSource.append(options.lineSeparatorSequence);
313 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
316 updateMappedPositions(scanner.startPosition);
320 * Formats the input string.
322 private void format() {
324 int previousToken = 0;
325 int previousCompilableToken = 0;
326 int indentationOffset = 0;
327 int newLinesInWhitespace = 0;
328 // number of new lines in the previous whitespace token
329 // (used to leave blank lines before comments)
330 int pendingNewLines = 0;
331 boolean expectingOpenBrace = false;
332 boolean clearNonBlockIndents = false;
333 // true if all indentations till the 1st { (usefull after } or ;)
334 boolean pendingSpace = true;
335 boolean pendingNewlineAfterParen = false;
336 // true when a cr is to be put after a ) (in conditional statements)
337 boolean inAssignment = false;
338 boolean inArrayAssignment = false;
339 boolean inThrowsClause = false;
340 boolean inClassOrInterfaceHeader = false;
341 int dollarBraceCount = 0;
342 // openBracketCount is used to count the number of open brackets not
345 int openBracketCount = 0;
346 int unarySignModifier = 0;
347 // openParenthesis[0] is used to count the parenthesis not belonging to
350 // (eg foo();). parenthesis in for (...) are count elsewhere in the
352 int openParenthesisCount = 1;
353 int[] openParenthesis = new int[10];
354 // tokenBeforeColon is used to know what token goes along with the
357 // it can be case or ?
358 int tokenBeforeColonCount = 0;
359 int[] tokenBeforeColon = new int[10];
360 constructionsCount = 0; // initializes the constructions count.
361 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
363 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and
365 boolean specialElse = false;
366 // OPTION (IndentationLevel): initial indentation level may be non-zero.
367 currentLineIndentationLevel += constructionsCount;
368 // An InvalidInputException exception might cause the termination of
371 int arrayDeclarationCount = 0;
372 int[] arrayDeclarationParenthesis = new int[10];
375 // Get the next token. Catch invalid input and output it
376 // with minimal formatting, also catch end of input and
379 token = scanner.getNextToken();
381 int currentEndPosition = scanner
382 .getCurrentTokenEndPosition();
383 int currentStartPosition = scanner
384 .getCurrentTokenStartPosition();
385 System.out.print(currentStartPosition + ","
386 + currentEndPosition + ": ");
387 System.out.println(scanner.toStringAction(token));
389 // Patch for line comment
390 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
391 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
392 int length = scanner.currentPosition;
393 loop: for (int index = length - 1; index >= 0; index--) {
394 switch (scanner.source[index]) {
397 scanner.currentPosition--;
404 } catch (InvalidInputException e) {
405 if (!handleInvalidToken(e)) {
410 if (token == Scanner.TokenNameEOF) {
412 } else if (token == Scanner.TokenNameHEREDOC) {
413 // no indentation for heredocs and HTML !
414 outputCurrentTokenWithoutIndent(Scanner.TokenNameHEREDOC, 0);
416 } else if (token == Scanner.TokenNameINLINE_HTML) {
417 // no indentation for heredocs and HTML !
418 int newLineCount = 1;
419 if (scanner.startPosition == 0) {
422 outputCurrentTokenWithoutIndent(
423 Scanner.TokenNameINLINE_HTML, newLineCount);
424 int srcLen = scanner.source.length;
425 if (scanner.currentPosition < srcLen - 1) {
431 * ## MODIFYING the indentation level before generating new
432 * lines and indentation in the output string
434 // Removes all the indentations made by statements not followed
437 // except if the current token is ELSE, CATCH or if we are in a
439 if (clearNonBlockIndents
440 && (token != Scanner.TokenNameWHITESPACE)) {
443 if (constructionsCount > 0
444 && constructions[constructionsCount - 1] == TokenNameelse) {
448 indentationLevel += popInclusiveUntil(TokenNameif);
450 // case TokenNamecatch :
451 // indentationLevel += popInclusiveUntil(TokenNamecatch);
453 // case TokenNamefinally :
454 // indentationLevel += popInclusiveUntil(TokenNamecatch);
457 if (nlicsToken == TokenNamedo) {
458 indentationLevel += pop(TokenNamedo);
462 indentationLevel += popExclusiveUntilBlockOrCase();
463 // clear until a CASE, DEFAULT or BLOCK is encountered.
464 // Thus, the indentationLevel is correctly cleared
466 // in a switch/case statement or in any other situation.
468 clearNonBlockIndents = false;
470 // returns to the indentation level created by the SWITCH
472 // if the current token is a CASE or a DEFAULT
473 if (token == TokenNamecase || token == TokenNamedefault) {
474 indentationLevel += pop(TokenNamecase);
476 // if (token == Scanner.TokenNamethrows) {
477 // inThrowsClause = true;
479 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface)
480 && previousToken != Scanner.TokenNameDOT) {
481 inClassOrInterfaceHeader = true;
484 * ## APPEND newlines and indentations to the output string
486 // Do not add a new line between ELSE and IF, if the option
487 // elseIfOnSameLine is true.
488 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
489 // if (pendingNewlineAfterParen
490 // && previousCompilableToken == TokenNameelse
491 // && token == TokenNameif
492 // && options.compactElseIfMode) {
493 // pendingNewlineAfterParen = false;
494 // pendingNewLines = 0;
495 // indentationLevel += pop(TokenNameelse);
496 // // because else if is now one single statement,
497 // // the indentation level after it is increased by one and not
499 // // (else = 1 indent, if = 1 indent, but else if = 1 indent,
502 // Add a newline & indent to the formatted source string if
503 // a for/if-else/while statement was scanned and there is no
506 pendingNewlineAfterParen = pendingNewlineAfterParen
507 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
508 if (pendingNewlineAfterParen
509 && token != Scanner.TokenNameWHITESPACE) {
510 pendingNewlineAfterParen = false;
511 // Do to add a newline & indent sequence if the current
513 // open brace or a period or if the current token is a
516 // previous token is a close paren.
517 // add a new line if a parenthesis belonging to a for()
519 // has been closed and the current token is not an opening
521 if (token != TokenNameLBRACE && !isComment(token)
522 // to avoid adding new line between else and a
524 && token != TokenNameDOT
525 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
527 currentLineIndentationLevel = indentationLevel;
529 pendingSpace = false;
531 if (token == TokenNameLBRACE
532 && options.newLineBeforeOpeningBraceMode) {
534 if (constructionsCount > 0
535 && constructions[constructionsCount - 1] != BLOCK
536 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
537 currentLineIndentationLevel = indentationLevel - 1;
539 currentLineIndentationLevel = indentationLevel;
542 pendingSpace = false;
546 if (token == TokenNameLBRACE
547 && options.newLineBeforeOpeningBraceMode
548 && constructionsCount > 0
549 && constructions[constructionsCount - 1] == TokenNamedo) {
551 currentLineIndentationLevel = indentationLevel - 1;
553 pendingSpace = false;
556 if (token == TokenNameLBRACE && inThrowsClause) {
557 inThrowsClause = false;
558 if (options.newLineBeforeOpeningBraceMode) {
560 currentLineIndentationLevel = indentationLevel;
562 pendingSpace = false;
566 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
567 inClassOrInterfaceHeader = false;
568 if (options.newLineBeforeOpeningBraceMode) {
570 currentLineIndentationLevel = indentationLevel;
572 pendingSpace = false;
575 // don't linebreak empty array declarations
576 if (token == TokenNameRPAREN && arrayDeclarationCount > 0) {
577 if (previousCompilableToken == TokenNameLPAREN) {
581 // Add pending new lines to the formatted source string.
582 // Note: pending new lines are not added if the current token
583 // is a single line comment or whitespace.
584 // if the comment is between parenthesis, there is no blank line
586 // (if it's a one-line comment, a blank line is added after it).
587 if (((pendingNewLines > 0 && (!isComment(token)))
588 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
589 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) || (newLinesInWhitespace > 0 && previousCompilableToken == TokenNameDOT))
590 && token != Scanner.TokenNameWHITESPACE) {
591 // Do not add newline & indent between an adjoining close
593 // close paren. Anonymous inner classes may use this form.
594 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
595 && token == TokenNameRPAREN;
596 // OPTION (NewLineInCompoundStatement): do not add newline &
598 // between close brace and else, (do) while, catch, and
600 // newlineInCompoundStatement is true.
601 boolean nlicsOption = previousToken == TokenNameRBRACE
602 && !options.newlineInControlStatementMode
603 && (token == TokenNameelse
604 || (token == TokenNamewhile && nlicsToken == TokenNamedo)
605 || token == TokenNamecatch || token == TokenNamefinally);
606 // Do not add a newline & indent between a close brace and
608 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
609 && token == TokenNameSEMICOLON;
610 // Do not add a new line & indent between a multiline
613 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
614 && token == TokenNameLBRACE;
615 // Do not add a newline & indent between a close brace and a
617 // (in array assignments, for example).
618 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
619 && token == TokenNameCOMMA;
620 // Add a newline and indent, if appropriate.
622 || (!commentAndOpenBrace
623 && !closeBraceAndCloseParen && !nlicsOption
624 && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
625 // if clearAllBlankLinesMode=false, leaves the blank
627 // inserted by the user
628 // if clearAllBlankLinesMode=true, removes all of then
629 // and insert only blank lines required by the
631 if (!options.clearAllBlankLinesMode) {
632 // (isComment(token))
633 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace
635 pendingNewLines = (pendingNewLines > 2) ? 2
638 if (previousCompilableToken == TokenNameLBRACE
639 && token == TokenNameRBRACE) {
640 containsOpenCloseBraces = true;
641 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
642 if (isComment(previousToken)) {
643 newLine(pendingNewLines);
646 * if (!(constructionsCount > 1 &&
647 * constructions[constructionsCount-1] ==
649 * (constructions[constructionsCount-2] ==
652 if (options.newLineInEmptyBlockMode) {
653 if (inArrayAssignment) {
654 newLine(1); // array assigment with an
657 newLine(pendingNewLines);
663 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with
666 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
667 newLine(pendingNewLines);
670 if (((previousCompilableToken == TokenNameSEMICOLON)
671 || (previousCompilableToken == TokenNameLBRACE)
672 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
673 && (token == TokenNameRBRACE)) {
674 indentationOffset = -1;
675 indentationLevel += popExclusiveUntilBlock();
677 if (previousToken == Scanner.TokenNameCOMMENT_LINE
680 currentLineIndentationLevel++;
682 currentLineIndentationLevel = indentationLevel
685 pendingSpace = false;
686 indentationOffset = 0;
689 newLinesInWhitespace = 0;
691 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
695 boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML
696 && token == TokenNameWHITESPACE;
698 // case TokenNameDOLLAR :
699 // dollarBraceCount++;
702 // case TokenNamefinally :
703 expectingOpenBrace = true;
704 pendingNewlineAfterParen = true;
705 indentationLevel += pushControlStatement(token);
708 case TokenNamedefault:
709 if (tokenBeforeColonCount == tokenBeforeColon.length) {
714 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
715 0, tokenBeforeColonCount);
717 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
718 indentationLevel += pushControlStatement(TokenNamecase);
720 case TokenNameQUESTION:
721 if (tokenBeforeColonCount == tokenBeforeColon.length) {
726 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
727 0, tokenBeforeColonCount);
729 tokenBeforeColon[tokenBeforeColonCount++] = token;
731 case TokenNameswitch:
733 case TokenNameforeach:
736 if (openParenthesisCount == openParenthesis.length) {
741 (openParenthesis = new int[openParenthesisCount * 2]),
742 0, openParenthesisCount);
744 openParenthesis[openParenthesisCount++] = 0;
745 expectingOpenBrace = true;
746 indentationLevel += pushControlStatement(token);
749 pendingNewlineAfterParen = true;
751 // several CATCH statements can be contiguous.
752 // a CATCH is encountered pop until first CATCH (if a CATCH
753 // follows a TRY it works the same way,
754 // as CATCH and TRY are the same token in the stack).
755 expectingOpenBrace = true;
756 indentationLevel += pushControlStatement(TokenNamecatch);
759 expectingOpenBrace = true;
760 indentationLevel += pushControlStatement(token);
765 case TokenNameLPAREN:
766 // if (previousToken == TokenNamesynchronized) {
767 // indentationLevel += pushControlStatement(previousToken);
769 // Put a space between the previous and current token if the
770 // previous token was not a keyword, open paren, logical
771 // compliment (eg: !), semi-colon, open brace, close brace,
773 if (previousCompilableToken != TokenNameLBRACKET
774 && previousToken != TokenNameIdentifier
775 && previousToken != 0
776 && previousToken != TokenNameNOT
777 && previousToken != TokenNameLPAREN
778 && previousToken != TokenNameTWIDDLE
779 && previousToken != TokenNameSEMICOLON
780 && previousToken != TokenNameLBRACE
781 && previousToken != TokenNameRBRACE
782 && previousToken != TokenNamesuper) {
783 // && previousToken != TokenNamethis) {
786 // If in a for/if/while statement, increase the parenthesis
788 // for the current openParenthesisCount
789 // else increase the count for stand alone parenthesis.
790 if (openParenthesisCount > 0)
791 openParenthesis[openParenthesisCount - 1]++;
793 openParenthesis[0]++;
794 pendingSpace = false;
795 // recognize array declaration for nice output
796 if (previousCompilableToken == TokenNamearray) {
797 arrayDeclarationCount++;
798 arrayDeclarationParenthesis[arrayDeclarationCount] = openParenthesis[openParenthesisCount - 1];
804 case TokenNameRPAREN:
805 // check for closing array declaration
806 if (arrayDeclarationCount > 0) {
807 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
808 if (previousCompilableToken != TokenNameLPAREN) {
810 } else if (previousToken == TokenNameCOMMENT_LINE
811 || previousToken == TokenNameCOMMENT_BLOCK
812 || previousToken == TokenNameCOMMENT_PHPDOC) {
813 // prevent to combine comment line and statement line (#1475484)
817 currentLineIndentationLevel = indentationLevel;
819 arrayDeclarationCount--;
822 // Decrease the parenthesis count
823 // if there is no more unclosed parenthesis,
824 // a new line and indent may be append (depending on the
827 if ((openParenthesisCount > 1)
828 && (openParenthesis[openParenthesisCount - 1] > 0)) {
829 openParenthesis[openParenthesisCount - 1]--;
830 if (openParenthesis[openParenthesisCount - 1] <= 0) {
831 pendingNewlineAfterParen = true;
832 inAssignment = false;
833 openParenthesisCount--;
836 openParenthesis[0]--;
838 pendingSpace = false;
840 case TokenNameLBRACE:
841 if (previousCompilableToken == TokenNameDOLLAR) {
844 if ((previousCompilableToken == TokenNameRBRACKET)
845 || (previousCompilableToken == TokenNameEQUAL)) {
846 // if (previousCompilableToken == TokenNameRBRACKET)
848 inArrayAssignment = true;
849 inAssignment = false;
851 if (inArrayAssignment) {
852 indentationLevel += pushBlock();
854 // Add new line and increase indentation level after
857 indentationLevel += pushBlock();
858 inAssignment = false;
862 case TokenNameRBRACE:
863 if (dollarBraceCount > 0) {
867 if (previousCompilableToken == TokenNameRPAREN) {
868 pendingSpace = false;
870 if (inArrayAssignment) {
871 inArrayAssignment = false;
873 indentationLevel += popInclusiveUntilBlock();
876 indentationLevel += popInclusiveUntilBlock();
877 if (previousCompilableToken == TokenNameRPAREN) {
878 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on
882 .append(options.lineSeparatorSequence);
883 increaseLineDelta(options.lineSeparatorSequence.length);
885 if (constructionsCount > 0) {
886 switch (constructions[constructionsCount - 1]) {
888 case TokenNameforeach:
889 // indentationLevel += popExclusiveUntilBlock();
891 case TokenNameswitch:
896 case TokenNamefinally:
899 // case TokenNamesynchronized :
900 clearNonBlockIndents = true;
907 case TokenNameLBRACKET:
909 pendingSpace = false;
911 case TokenNameRBRACKET:
912 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
913 // if there is no left bracket to close, the right bracket
916 pendingSpace = false;
919 pendingSpace = false;
920 if (arrayDeclarationCount > 0) {
921 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount - 1]) {
922 // there is no left parenthesis to close in current array declaration (#1475484)
929 pendingSpace = false;
931 case TokenNameSEMICOLON:
932 // Do not generate line terminators in the definition of
933 // the for statement.
934 // if not in this case, jump a line and reduce indentation
937 // if the block it closes belongs to a conditional statement
940 if (openParenthesisCount <= 1) {
942 if (expectingOpenBrace) {
943 clearNonBlockIndents = true;
944 expectingOpenBrace = false;
947 inAssignment = false;
948 pendingSpace = false;
950 case TokenNamePLUS_PLUS:
951 case TokenNameMINUS_MINUS:
952 // Do not put a space between a post-increment/decrement
953 // and the identifier being modified.
954 if (previousToken == TokenNameIdentifier
955 || previousToken == TokenNameRBRACKET
956 || previousToken == TokenNameVariable) {
957 pendingSpace = false;
961 // previously ADDITION
963 // Handle the unary operators plus and minus via a flag
964 if (!isLiteralToken(previousToken)
965 && previousToken != TokenNameIdentifier
966 && previousToken != TokenNameRPAREN
967 && previousToken != TokenNameRBRACKET) {
968 unarySignModifier = 1;
972 // In a switch/case statement, add a newline & indent
973 // when a colon is encountered.
974 if (tokenBeforeColonCount > 0) {
975 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
978 tokenBeforeColonCount--;
984 case Scanner.TokenNameCOMMENT_LINE:
986 //if (inAssignment) {
987 // currentLineIndentationLevel++;
989 break; // a line is always inserted after a one-line
991 case Scanner.TokenNameCOMMENT_PHPDOC:
992 case Scanner.TokenNameCOMMENT_BLOCK:
993 currentCommentOffset = getCurrentCommentOffset();
996 case Scanner.TokenNameWHITESPACE:
997 if (!phpTagAndWhitespace) {
998 // Count the number of line terminators in the
1000 // line spacing can be preserved near comments.
1001 char[] source = scanner.source;
1002 newLinesInWhitespace = 0;
1003 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
1004 if (source[i] == '\r') {
1006 if (source[++i] == '\n') {
1007 newLinesInWhitespace++;
1009 newLinesInWhitespace++;
1012 newLinesInWhitespace++;
1014 } else if (source[i] == '\n') {
1015 newLinesInWhitespace++;
1018 increaseLineDelta(scanner.startPosition
1019 - scanner.currentPosition);
1022 // case TokenNameHTML :
1023 // // Add the next token to the formatted source string.
1024 // // outputCurrentToken(token);
1025 // int startPosition = scanner.startPosition;
1027 // for (int i = startPosition, max =
1028 // scanner.currentPosition; i <
1030 // char currentCharacter = scanner.source[i];
1031 // updateMappedPositions(i);
1032 // currentLineBuffer.append(currentCharacter);
1036 if ((token == TokenNameIdentifier) || isLiteralToken(token)
1037 || token == TokenNamesuper) {
1038 // || token == TokenNamethis) {
1039 // Do not put a space between a unary operator
1040 // (eg: ++, --, +, -) and the identifier being modified.
1041 if (previousToken == TokenNamePLUS_PLUS
1042 || previousToken == TokenNameMINUS_MINUS
1043 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
1044 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
1045 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
1046 pendingSpace = false;
1048 unarySignModifier = 0;
1052 // Do not output whitespace tokens.
1053 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1055 * Add pending space to the formatted source string. Do not
1056 * output a space under the following circumstances: 1) this
1057 * is the first pass 2) previous token is an open paren 3)
1058 * previous token is a period 4) previous token is the
1059 * logical compliment (eg: !) 5) previous token is the
1060 * bitwise compliment (eg: ~) 6) previous token is the open
1061 * bracket (eg: [) 7) in an assignment statement, if the
1062 * previous token is an open brace or the current token is a
1063 * close brace 8) previous token is a single line comment 9)
1064 * current token is a '->'
1066 if (token == TokenNameMINUS_GREATER
1067 && options.compactDereferencingMode)
1068 pendingSpace = false;
1070 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
1071 && token == TokenNameRBRACE;
1073 && insertSpaceAfter(previousToken)
1074 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
1075 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
1076 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
1077 && !openAndCloseBrace)
1080 // Add the next token to the formatted source string.
1081 outputCurrentToken(token);
1082 if (token == Scanner.TokenNameCOMMENT_LINE
1083 && openParenthesisCount > 1) {
1084 pendingNewLines = 0;
1085 currentLineBuffer.append(options.lineSeparatorSequence);
1086 increaseLineDelta(options.lineSeparatorSequence.length);
1088 pendingSpace = true;
1090 // Whitespace tokens do not need to be remembered.
1091 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1092 previousToken = token;
1093 if (token != Scanner.TokenNameCOMMENT_BLOCK
1094 && token != Scanner.TokenNameCOMMENT_LINE
1095 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
1096 previousCompilableToken = token;
1100 output(copyRemainingSource());
1102 // dump the last token of the source in the formatted output.
1103 } catch (InvalidInputException e) {
1104 output(copyRemainingSource());
1106 // dump the last token of the source in the formatted output.
1111 * Formats the char array <code>sourceString</code>, and returns a string
1112 * containing the formatted version.
1114 * @return the formatted ouput.
1116 public String formatSourceString(String sourceString) {
1117 char[] sourceChars = sourceString.toCharArray();
1118 formattedSource = new StringBuffer(sourceChars.length);
1119 scanner.setSource(sourceChars);
1121 return formattedSource.toString();
1125 * Formats the char array <code>sourceString</code>, and returns a string
1126 * containing the formatted version.
1129 * the string to format
1130 * @param indentationLevel
1131 * the initial indentation level
1132 * @return the formatted ouput.
1134 public String format(String string, int indentationLevel) {
1135 return format(string, indentationLevel, (int[]) null);
1139 * Formats the char array <code>sourceString</code>, and returns a string
1140 * containing the formatted version. The positions array is modified to
1141 * contain the mapped positions.
1144 * the string to format
1145 * @param indentationLevel
1146 * the initial indentation level
1148 * the array of positions to map
1149 * @return the formatted ouput.
1151 public String format(String string, int indentationLevel, int[] positions) {
1152 return this.format(string, indentationLevel, positions, null);
1155 public String format(String string, int indentationLevel, int[] positions,
1156 String lineSeparator) {
1157 if (lineSeparator != null) {
1158 this.options.setLineSeparator(lineSeparator);
1160 if (positions != null) {
1161 this.setPositionsToMap(positions);
1162 this.setInitialIndentationLevel(indentationLevel);
1163 String formattedString = this.formatSourceString(string);
1164 int[] mappedPositions = this.getMappedPositions();
1166 .arraycopy(mappedPositions, 0, positions, 0,
1168 return formattedString;
1170 this.setInitialIndentationLevel(indentationLevel);
1171 return this.formatSourceString(string);
1176 * Formats the char array <code>sourceString</code>, and returns a string
1177 * containing the formatted version. The initial indentation level is 0.
1180 * the string to format
1181 * @return the formatted ouput.
1183 public String format(String string) {
1184 return this.format(string, 0, (int[]) null);
1188 * Formats a given source string, starting indenting it at a particular
1189 * depth and using the given options
1191 * @deprecated backport 1.0 internal functionality
1193 public static String format(String sourceString,
1194 int initialIndentationLevel, ConfigurableOption[] options) {
1195 CodeFormatter formatter = new CodeFormatter(options);
1196 formatter.setInitialIndentationLevel(initialIndentationLevel);
1197 return formatter.formatSourceString(sourceString);
1201 * Returns the number of characters and tab char between the beginning of
1202 * the line and the beginning of the comment.
1204 private int getCurrentCommentOffset() {
1205 int linePtr = scanner.linePtr;
1206 // if there is no beginning of line, return 0.
1210 int beginningOfLine = scanner.lineEnds[linePtr];
1211 int currentStartPosition = scanner.startPosition;
1212 char[] source = scanner.source;
1213 // find the position of the beginning of the line containing the comment
1214 while (beginningOfLine > currentStartPosition) {
1216 beginningOfLine = scanner.lineEnds[--linePtr];
1218 beginningOfLine = 0;
1222 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1223 char currentCharacter = source[i];
1224 switch (currentCharacter) {
1226 offset += options.tabSize;
1242 * Returns an array of descriptions for the configurable options. The
1243 * descriptions may be changed and passed back to a different compiler.
1245 * @deprecated backport 1.0 internal functionality
1247 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1248 String componentName = CodeFormatter.class.getName();
1249 FormatterOptions options = new FormatterOptions();
1250 return new ConfigurableOption[] {
1251 new ConfigurableOption(componentName, "newline.openingBrace",
1252 locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1254 new ConfigurableOption(componentName,
1255 "newline.controlStatement", locale,
1256 options.newlineInControlStatementMode ? 0 : 1),
1258 new ConfigurableOption(componentName, "newline.clearAll",
1259 locale, options.clearAllBlankLinesMode ? 0 : 1),
1261 // new ConfigurableOption(componentName, "newline.elseIf",
1263 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1264 new ConfigurableOption(componentName, "newline.emptyBlock",
1265 locale, options.newLineInEmptyBlockMode ? 0 : 1),
1267 new ConfigurableOption(componentName, "line.split", locale,
1268 options.maxLineLength),
1270 new ConfigurableOption(componentName,
1271 "style.compactAssignment", locale,
1272 options.compactAssignmentMode ? 0 : 1),
1274 new ConfigurableOption(componentName, "tabulation.char",
1275 locale, options.indentWithTab ? 0 : 1),
1277 new ConfigurableOption(componentName,
1278 "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1283 * Returns the array of mapped positions. Returns null is no positions have
1287 * @deprecated There is no need to retrieve the mapped positions anymore.
1289 public int[] getMappedPositions() {
1290 if (null != mappedPositions) {
1291 for (int i = 0; i < mappedPositions.length; i++) {
1292 if (mappedPositions[i] >= formattedSource.length()) {
1293 mappedPositions[i] = formattedSource.length() - 1;
1297 return mappedPositions;
1301 * Returns the priority of the token given as argument <br>
1302 * The most prioritary the token is, the smallest the return value is.
1304 * @return the priority of <code>token</code>
1306 * the token of which the priority is requested
1308 private static int getTokenPriority(int token) {
1310 case TokenNameextends:
1311 // case TokenNameimplements :
1312 // case TokenNamethrows :
1314 case TokenNameSEMICOLON:
1317 case TokenNameCOMMA:
1320 case TokenNameEQUAL:
1323 case TokenNameAND_AND:
1325 case TokenNameOR_OR:
1328 case TokenNameQUESTION:
1330 case TokenNameCOLON:
1332 return 50; // it's better cutting on ?: than on ;
1333 case TokenNameEQUAL_EQUAL:
1335 case TokenNameEQUAL_EQUAL_EQUAL:
1337 case TokenNameNOT_EQUAL:
1339 case TokenNameNOT_EQUAL_EQUAL:
1344 case TokenNameLESS_EQUAL:
1346 case TokenNameGREATER:
1348 case TokenNameGREATER_EQUAL:
1350 // case TokenNameinstanceof : // instanceof
1354 case TokenNameMINUS:
1357 case TokenNameMULTIPLY:
1359 case TokenNameDIVIDE:
1361 case TokenNameREMAINDER:
1364 case TokenNameLEFT_SHIFT:
1366 case TokenNameRIGHT_SHIFT:
1368 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1377 case TokenNameMULTIPLY_EQUAL:
1379 case TokenNameDIVIDE_EQUAL:
1381 case TokenNameREMAINDER_EQUAL:
1383 case TokenNamePLUS_EQUAL:
1385 case TokenNameMINUS_EQUAL:
1387 case TokenNameLEFT_SHIFT_EQUAL:
1389 case TokenNameRIGHT_SHIFT_EQUAL:
1391 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1392 case TokenNameAND_EQUAL:
1394 case TokenNameXOR_EQUAL:
1396 case TokenNameOR_EQUAL:
1398 case TokenNameDOT_EQUAL:
1405 return Integer.MAX_VALUE;
1410 * Handles the exception raised when an invalid token is encountered.
1411 * Returns true if the exception has been handled, false otherwise.
1413 private boolean handleInvalidToken(Exception e) {
1414 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1415 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1416 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1422 private final void increaseGlobalDelta(int offset) {
1423 globalDelta += offset;
1426 private final void increaseLineDelta(int offset) {
1427 lineDelta += offset;
1430 private final void increaseSplitDelta(int offset) {
1431 splitDelta += offset;
1435 * Returns true if a space has to be inserted after <code>operator</code>
1438 private boolean insertSpaceAfter(int token) {
1440 case TokenNameLPAREN:
1442 case TokenNameTWIDDLE:
1445 case TokenNameWHITESPACE:
1446 case TokenNameLBRACKET:
1447 case TokenNameDOLLAR:
1448 case Scanner.TokenNameCOMMENT_LINE:
1456 * Returns true if a space has to be inserted before <code>operator</code>
1457 * false otherwise. <br>
1458 * Cannot be static as it uses the code formatter options (to know if the
1459 * compact assignment mode is on).
1461 private boolean insertSpaceBefore(int token) {
1463 case TokenNameEQUAL:
1464 return (!options.compactAssignmentMode);
1470 private static boolean isComment(int token) {
1471 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1472 || token == Scanner.TokenNameCOMMENT_LINE
1473 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1477 private static boolean isLiteralToken(int token) {
1478 boolean result = token == TokenNameIntegerLiteral
1479 // || token == TokenNameLongLiteral
1480 // || token == TokenNameFloatingPointLiteral
1481 || token == TokenNameDoubleLiteral
1482 // || token == TokenNameCharacterLiteral
1483 || token == TokenNameStringDoubleQuote;
1488 * If the length of <code>oneLineBuffer</code> exceeds
1489 * <code>maxLineLength</code>, it is split and the result is dumped in
1490 * <code>formattedSource</code>
1492 * @param newLineCount
1493 * the number of new lines to append
1495 private void newLine(int newLineCount) {
1496 // format current line
1498 beginningOfLineIndex = formattedSource.length();
1499 String currentLine = currentLineBuffer.toString();
1500 if (containsOpenCloseBraces) {
1501 containsOpenCloseBraces = false;
1502 outputLine(currentLine, false, indentationLevelForOpenCloseBraces,
1504 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1506 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1,
1509 // dump line break(s)
1510 for (int i = 0; i < newLineCount; i++) {
1511 formattedSource.append(options.lineSeparatorSequence);
1512 increaseSplitDelta(options.lineSeparatorSequence.length);
1514 // reset formatter for next line
1515 int currentLength = currentLine.length();
1516 currentLineBuffer = new StringBuffer(
1517 currentLength > maxLineSize ? maxLineSize = currentLength
1519 increaseGlobalDelta(splitDelta);
1520 increaseGlobalDelta(lineDelta);
1522 currentLineIndentationLevel = initialIndentationLevel;
1525 private String operatorString(int operator) {
1527 case TokenNameextends:
1528 return "extends"; //$NON-NLS-1$
1529 // case TokenNameimplements :
1530 // return "implements"; //$NON-NLS-1$
1532 // case TokenNamethrows :
1533 // return "throws"; //$NON-NLS-1$
1534 case TokenNameSEMICOLON:
1536 return ";"; //$NON-NLS-1$
1537 case TokenNameCOMMA:
1539 return ","; //$NON-NLS-1$
1540 case TokenNameEQUAL:
1542 return "="; //$NON-NLS-1$
1543 case TokenNameAND_AND:
1545 return "&&"; //$NON-NLS-1$
1546 case TokenNameOR_OR:
1548 return "||"; //$NON-NLS-1$
1549 case TokenNameQUESTION:
1551 return "?"; //$NON-NLS-1$
1552 case TokenNameCOLON:
1554 return ":"; //$NON-NLS-1$
1555 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1557 return "::"; //$NON-NLS-1$
1558 case TokenNameEQUAL_EQUAL:
1559 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1560 return "=="; //$NON-NLS-1$
1561 case TokenNameEQUAL_EQUAL_EQUAL:
1562 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1563 return "==="; //$NON-NLS-1$
1564 case TokenNameEQUAL_GREATER:
1566 return "=>"; //$NON-NLS-1$
1567 case TokenNameNOT_EQUAL:
1568 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1569 return "!="; //$NON-NLS-1$
1570 case TokenNameNOT_EQUAL_EQUAL:
1571 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1572 return "!=="; //$NON-NLS-1$
1575 return "<"; //$NON-NLS-1$
1576 case TokenNameLESS_EQUAL:
1578 return "<="; //$NON-NLS-1$
1579 case TokenNameGREATER:
1581 return ">"; //$NON-NLS-1$
1582 case TokenNameGREATER_EQUAL:
1584 return ">="; //$NON-NLS-1$
1585 // case TokenNameinstanceof : // instanceof
1586 // return "instanceof"; //$NON-NLS-1$
1588 // + (15.17, 15.17.2)
1589 return "+"; //$NON-NLS-1$
1590 case TokenNameMINUS:
1592 return "-"; //$NON-NLS-1$
1593 case TokenNameMULTIPLY:
1595 return "*"; //$NON-NLS-1$
1596 case TokenNameDIVIDE:
1598 return "/"; //$NON-NLS-1$
1599 case TokenNameREMAINDER:
1601 return "%"; //$NON-NLS-1$
1602 case TokenNameLEFT_SHIFT:
1604 return "<<"; //$NON-NLS-1$
1605 case TokenNameRIGHT_SHIFT:
1607 return ">>"; //$NON-NLS-1$
1608 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1609 // return ">>>"; //$NON-NLS-1$
1611 // & (15.21, 15.21.1, 15.21.2)
1612 return "&"; //$NON-NLS-1$
1614 // | (15.21, 15.21.1, 15.21.2)
1615 return "|"; //$NON-NLS-1$
1617 // ^ (15.21, 15.21.1, 15.21.2)
1618 return "^"; //$NON-NLS-1$
1619 case TokenNameMULTIPLY_EQUAL:
1621 return "*="; //$NON-NLS-1$
1622 case TokenNameDIVIDE_EQUAL:
1624 return "/="; //$NON-NLS-1$
1625 case TokenNameREMAINDER_EQUAL:
1627 return "%="; //$NON-NLS-1$
1628 case TokenNamePLUS_EQUAL:
1630 return "+="; //$NON-NLS-1$
1631 case TokenNameMINUS_EQUAL:
1633 return "-="; //$NON-NLS-1$
1634 case TokenNameMINUS_GREATER:
1636 return "->"; //$NON-NLS-1$
1637 case TokenNameLEFT_SHIFT_EQUAL:
1639 return "<<="; //$NON-NLS-1$
1640 case TokenNameRIGHT_SHIFT_EQUAL:
1642 return ">>="; //$NON-NLS-1$
1643 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1644 // return ">>>="; //$NON-NLS-1$
1645 case TokenNameAND_EQUAL:
1647 return "&="; //$NON-NLS-1$
1648 case TokenNameXOR_EQUAL:
1650 return "^="; //$NON-NLS-1$
1651 case TokenNameOR_EQUAL:
1653 return "|="; //$NON-NLS-1$
1654 case TokenNameDOT_EQUAL:
1656 return ".="; //$NON-NLS-1$
1659 return "."; //$NON-NLS-1$
1661 return ""; //$NON-NLS-1$
1666 * Appends <code>stringToOutput</code> to the formatted output. <br>
1667 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1669 private void output(String stringToOutput) {
1670 char currentCharacter;
1671 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1672 currentCharacter = stringToOutput.charAt(i);
1673 if (currentCharacter != '\t') {
1674 currentLineBuffer.append(currentCharacter);
1679 private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
1680 newLine(newLineCount);
1681 formattedSource.append(scanner.source, scanner.startPosition,
1682 scanner.currentPosition - scanner.startPosition);
1686 * Appends <code>token</code> to the formatted output. <br>
1687 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1690 private void outputCurrentToken(int token) {
1691 char[] source = scanner.source;
1692 int startPosition = scanner.startPosition;
1694 case Scanner.TokenNameCOMMENT_PHPDOC:
1695 case Scanner.TokenNameCOMMENT_BLOCK:
1696 case Scanner.TokenNameCOMMENT_LINE:
1697 boolean endOfLine = false;
1698 int currentCommentOffset = getCurrentCommentOffset();
1699 int beginningOfLineSpaces = 0;
1701 currentCommentOffset = getCurrentCommentOffset();
1702 beginningOfLineSpaces = 0;
1703 boolean pendingCarriageReturn = false;
1704 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1705 char currentCharacter = source[i];
1706 updateMappedPositions(i);
1707 switch (currentCharacter) {
1709 pendingCarriageReturn = true;
1713 if (pendingCarriageReturn) {
1714 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1716 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1718 pendingCarriageReturn = false;
1719 currentLineBuffer.append(options.lineSeparatorSequence);
1720 beginningOfLineSpaces = 0;
1724 if (pendingCarriageReturn) {
1725 pendingCarriageReturn = false;
1726 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1727 currentLineBuffer.append(options.lineSeparatorSequence);
1728 beginningOfLineSpaces = 0;
1732 // we remove a maximum of currentCommentOffset
1734 // are converted to space numbers).
1735 beginningOfLineSpaces += options.tabSize;
1736 if (beginningOfLineSpaces > currentCommentOffset) {
1737 currentLineBuffer.append(currentCharacter);
1739 increaseGlobalDelta(-1);
1742 currentLineBuffer.append(currentCharacter);
1746 if (pendingCarriageReturn) {
1747 pendingCarriageReturn = false;
1748 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1749 currentLineBuffer.append(options.lineSeparatorSequence);
1750 beginningOfLineSpaces = 0;
1754 // we remove a maximum of currentCommentOffset
1756 // are converted to space numbers).
1757 beginningOfLineSpaces++;
1758 if (beginningOfLineSpaces > currentCommentOffset) {
1759 currentLineBuffer.append(currentCharacter);
1761 increaseGlobalDelta(-1);
1764 currentLineBuffer.append(currentCharacter);
1768 if (pendingCarriageReturn) {
1769 pendingCarriageReturn = false;
1770 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1771 currentLineBuffer.append(options.lineSeparatorSequence);
1772 beginningOfLineSpaces = 0;
1775 beginningOfLineSpaces = 0;
1776 currentLineBuffer.append(currentCharacter);
1781 updateMappedPositions(scanner.currentPosition - 1);
1782 multipleLineCommentCounter++;
1785 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1786 char currentCharacter = source[i];
1787 updateMappedPositions(i);
1788 currentLineBuffer.append(currentCharacter);
1794 * Outputs <code>currentString</code>:<br>
1796 * <li>If its length is < maxLineLength, output
1797 * <li>Otherwise it is split.
1800 * @param currentString
1802 * @param preIndented
1803 * whether the string to output was pre-indented
1805 * number of indentation to put in front of
1806 * <code>currentString</code>
1808 * value of the operator belonging to <code>currentString</code>.
1810 private void outputLine(String currentString, boolean preIndented,
1811 int depth, int operator, int substringIndex,
1812 int[] startSubstringIndexes, int offsetInGlobalLine) {
1813 boolean emptyFirstSubString = false;
1814 String operatorString = operatorString(operator);
1815 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1816 boolean placeOperatorAhead = !placeOperatorBehind;
1817 // dump prefix operator?
1818 if (placeOperatorAhead) {
1823 if (operator != 0) {
1824 if (insertSpaceBefore(operator)) {
1825 formattedSource.append(' ');
1826 increaseSplitDelta(1);
1828 formattedSource.append(operatorString);
1829 increaseSplitDelta(operatorString.length());
1830 if (insertSpaceAfter(operator)
1831 && operator != TokenNameimplements
1832 && operator != TokenNameextends) {
1833 // && operator != TokenNamethrows) {
1834 formattedSource.append(' ');
1835 increaseSplitDelta(1);
1839 SplitLine splitLine = null;
1840 if (options.maxLineLength == 0
1841 || getLength(currentString, depth) < options.maxLineLength
1842 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1843 // depending on the type of operator, outputs new line before of
1846 // indent before postfix operator
1847 // indent also when the line cannot be split
1848 if (operator == TokenNameextends || operator == TokenNameimplements) {
1849 // || operator == TokenNamethrows) {
1850 formattedSource.append(' ');
1851 increaseSplitDelta(1);
1853 if (placeOperatorBehind) {
1858 int max = currentString.length();
1859 if (multipleLineCommentCounter != 0) {
1861 BufferedReader reader = new BufferedReader(
1862 new StringReader(currentString));
1863 String line = reader.readLine();
1864 while (line != null) {
1865 updateMappedPositionsWhileSplitting(
1866 beginningOfLineIndex, beginningOfLineIndex
1868 + options.lineSeparatorSequence.length);
1869 formattedSource.append(line);
1870 beginningOfLineIndex = beginningOfLineIndex
1872 if ((line = reader.readLine()) != null) {
1874 .append(options.lineSeparatorSequence);
1875 beginningOfLineIndex += options.lineSeparatorSequence.length;
1876 dumpTab(currentLineIndentationLevel);
1880 } catch (IOException e) {
1881 e.printStackTrace();
1884 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1885 beginningOfLineIndex + max);
1886 for (int i = 0; i < max; i++) {
1887 char currentChar = currentString.charAt(i);
1888 switch (currentChar) {
1893 // fix for 1FFYL5C: LFCOM:ALL - Incorrect
1895 // split with a comment inside a condition
1896 // a substring cannot end with a
1897 // lineSeparatorSequence,
1898 // except if it has been added by format() after a
1902 .append(options.lineSeparatorSequence);
1903 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
1909 formattedSource.append(currentChar);
1913 // update positions inside the mappedPositions table
1914 if (substringIndex != -1) {
1915 if (multipleLineCommentCounter == 0) {
1916 int startPosition = beginningOfLineIndex
1917 + startSubstringIndexes[substringIndex];
1918 updateMappedPositionsWhileSplitting(startPosition,
1919 startPosition + max);
1921 // compute the splitDelta resulting with the operator and blank
1923 if (substringIndex + 1 != startSubstringIndexes.length) {
1924 increaseSplitDelta(startSubstringIndexes[substringIndex]
1925 + max - startSubstringIndexes[substringIndex + 1]);
1928 // dump postfix operator?
1929 if (placeOperatorBehind) {
1930 if (insertSpaceBefore(operator)) {
1931 formattedSource.append(' ');
1932 if (operator != 0) {
1933 increaseSplitDelta(1);
1936 formattedSource.append(operatorString);
1937 if (operator != 0) {
1938 increaseSplitDelta(operatorString.length());
1943 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1944 // extends has to stand alone on a line when currentString has been
1946 if (options.maxLineLength != 0 && splitLine != null
1947 && (operator == TokenNameextends)) {
1948 // || operator == TokenNameimplements
1949 // || operator == TokenNamethrows)) {
1950 formattedSource.append(options.lineSeparatorSequence);
1951 increaseSplitDelta(options.lineSeparatorSequence.length);
1954 if (operator == TokenNameextends) {
1955 // || operator == TokenNameimplements
1956 // || operator == TokenNamethrows) {
1957 formattedSource.append(' ');
1958 increaseSplitDelta(1);
1961 // perform actual splitting
1962 String result[] = splitLine.substrings;
1963 int[] splitOperators = splitLine.operators;
1964 if (result[0].length() == 0) {
1965 // when the substring 0 is null, the substring 1 is correctly
1968 emptyFirstSubString = true;
1970 // the operator going in front of the result[0] string is the operator
1972 for (int i = 0, max = result.length; i < max; i++) {
1973 // the new depth is the current one if this is the first substring,
1974 // the current one + 1 otherwise.
1975 // if the substring is a comment, use the current indentation Level
1976 // instead of the depth
1977 // (-1 because the ouputline increases depth).
1978 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of
1981 String currentResult = result[i];
1982 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1983 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1984 || currentResult.startsWith("//")) //$NON-NLS-1$
1985 ? indentationLevel - 1
1987 outputLine(currentResult, i == 0
1988 || (i == 1 && emptyFirstSubString) ? preIndented
1989 : false, i == 0 ? newDepth : newDepth + 1,
1990 splitOperators[i], i, splitLine.startSubstringsIndexes,
1991 currentString.indexOf(currentResult));
1993 formattedSource.append(options.lineSeparatorSequence);
1994 increaseSplitDelta(options.lineSeparatorSequence.length);
1998 if (result.length == splitOperators.length - 1) {
1999 int lastOperator = splitOperators[result.length];
2000 String lastOperatorString = operatorString(lastOperator);
2001 formattedSource.append(options.lineSeparatorSequence);
2002 increaseSplitDelta(options.lineSeparatorSequence.length);
2003 if (breakLineBeforeOperator(lastOperator)) {
2005 if (lastOperator != 0) {
2006 if (insertSpaceBefore(lastOperator)) {
2007 formattedSource.append(' ');
2008 increaseSplitDelta(1);
2010 formattedSource.append(lastOperatorString);
2011 increaseSplitDelta(lastOperatorString.length());
2012 if (insertSpaceAfter(lastOperator)
2013 && lastOperator != TokenNameimplements
2014 && lastOperator != TokenNameextends) {
2015 // && lastOperator != TokenNamethrows) {
2016 formattedSource.append(' ');
2017 increaseSplitDelta(1);
2022 if (placeOperatorBehind) {
2023 if (insertSpaceBefore(operator)) {
2024 formattedSource.append(' ');
2025 increaseSplitDelta(1);
2027 formattedSource.append(operatorString);
2028 // increaseSplitDelta(operatorString.length());
2033 * Pops the top statement of the stack if it is <code>token</code>
2035 private int pop(int token) {
2037 if ((constructionsCount > 0)
2038 && (constructions[constructionsCount - 1] == token)) {
2040 constructionsCount--;
2046 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
2047 * <code>NONINDENT_BLOCK</code>.
2049 private int popBlock() {
2051 if ((constructionsCount > 0)
2052 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
2053 if (constructions[constructionsCount - 1] == BLOCK)
2055 constructionsCount--;
2061 * Pops elements until the stack is empty or the top element is
2062 * <code>token</code>.<br>
2063 * Does not remove <code>token</code> from the stack.
2066 * the token to be left as the top of the stack
2068 private int popExclusiveUntil(int token) {
2070 int startCount = constructionsCount;
2071 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2072 if (constructions[i] != NONINDENT_BLOCK)
2074 constructionsCount--;
2080 * Pops elements until the stack is empty or the top element is a
2081 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2082 * Does not remove it from the stack.
2084 private int popExclusiveUntilBlock() {
2085 int startCount = constructionsCount;
2087 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2088 && constructions[i] != NONINDENT_BLOCK; i--) {
2089 constructionsCount--;
2096 * Pops elements until the stack is empty or the top element is a
2097 * <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
2098 * <code>CASE</code>.<br>
2099 * Does not remove it from the stack.
2101 private int popExclusiveUntilBlockOrCase() {
2102 int startCount = constructionsCount;
2104 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2105 && constructions[i] != NONINDENT_BLOCK
2106 && constructions[i] != TokenNamecase; i--) {
2107 constructionsCount--;
2114 * Pops elements until the stack is empty or the top element is
2115 * <code>token</code>.<br>
2116 * Removes <code>token</code> from the stack too.
2119 * the token to remove from the stack
2121 private int popInclusiveUntil(int token) {
2122 int startCount = constructionsCount;
2124 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2125 if (constructions[i] != NONINDENT_BLOCK)
2127 constructionsCount--;
2129 if (constructionsCount > 0) {
2130 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
2132 constructionsCount--;
2138 * Pops elements until the stack is empty or the top element is a
2139 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2140 * Does not remove it from the stack.
2142 private int popInclusiveUntilBlock() {
2143 int startCount = constructionsCount;
2145 for (int i = startCount - 1; i >= 0
2146 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
2148 constructionsCount--;
2150 if (constructionsCount > 0) {
2151 if (constructions[constructionsCount - 1] == BLOCK)
2153 constructionsCount--;
2159 * Pushes a block in the stack. <br>
2160 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
2161 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
2162 * otherwise. Creates a new bigger array if the current one is full.
2164 private int pushBlock() {
2166 if (constructionsCount == constructions.length)
2167 System.arraycopy(constructions, 0,
2168 (constructions = new int[constructionsCount * 2]), 0,
2169 constructionsCount);
2170 if ((constructionsCount == 0)
2171 || (constructions[constructionsCount - 1] == BLOCK)
2172 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
2173 || (constructions[constructionsCount - 1] == TokenNamecase)) {
2175 constructions[constructionsCount++] = BLOCK;
2177 constructions[constructionsCount++] = NONINDENT_BLOCK;
2183 * Pushes <code>token</code>.<br>
2184 * Creates a new bigger array if the current one is full.
2186 private int pushControlStatement(int token) {
2187 if (constructionsCount == constructions.length)
2188 System.arraycopy(constructions, 0,
2189 (constructions = new int[constructionsCount * 2]), 0,
2190 constructionsCount);
2191 constructions[constructionsCount++] = token;
2195 private static boolean separateFirstArgumentOn(int currentToken) {
2196 // return (currentToken == TokenNameCOMMA || currentToken ==
2197 // TokenNameSEMICOLON);
2198 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
2199 && currentToken != TokenNameNOT
2200 && currentToken != TokenNamewhile
2201 && currentToken != TokenNamefor
2202 && currentToken != TokenNameforeach
2203 && currentToken != TokenNameswitch;
2207 * Set the positions to map. The mapped positions should be retrieved using
2208 * the getMappedPositions() method.
2212 * @deprecated Set the positions to map using the format(String, int, int[])
2215 * @see #getMappedPositions()
2217 public void setPositionsToMap(int[] positions) {
2218 positionsToMap = positions;
2221 mappedPositions = new int[positions.length];
2225 * Appends a space character to the current line buffer.
2227 private void space() {
2228 currentLineBuffer.append(' ');
2229 increaseLineDelta(1);
2233 * Splits <code>stringToSplit</code> on the top level token <br>
2234 * If there are several identical token at the same level, the string is cut
2237 * @return an object containing the operator and all the substrings or null
2238 * if the string cannot be split
2240 public SplitLine split(String stringToSplit) {
2241 return split(stringToSplit, 0);
2245 * Splits <code>stringToSplit</code> on the top level token <br>
2246 * If there are several identical token at the same level, the string is cut
2249 * @return an object containing the operator and all the substrings or null
2250 * if the string cannot be split
2252 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2254 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2255 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2257 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2260 // split doesn't work correct for PHP
2263 // int currentToken = 0;
2264 // int splitTokenType = 0;
2265 // int splitTokenDepth = Integer.MAX_VALUE;
2266 // int splitTokenPriority = Integer.MAX_VALUE;
2267 // int[] substringsStartPositions = new int[10];
2268 // // contains the start position of substrings
2269 // int[] substringsEndPositions = new int[10];
2270 // // contains the start position of substrings
2271 // int substringsCount = 1; // index in the substringsStartPosition
2273 // int[] splitOperators = new int[10];
2274 // // contains the start position of substrings
2275 // int splitOperatorsCount = 0; // index in the substringsStartPosition
2277 // int[] openParenthesisPosition = new int[10];
2278 // int openParenthesisPositionCount = 0;
2279 // int position = 0;
2280 // int lastOpenParenthesisPosition = -1;
2281 // // used to remember the position of the 1st open parenthesis
2282 // // needed for a pattern like: A.B(C); we want formatted like A.B(
2284 // // setup the scanner with a new source
2285 // int lastCommentStartPosition = -1;
2286 // // to remember the start position of the last comment
2287 // int firstTokenOnLine = -1;
2288 // // to remember the first token of the line
2289 // int previousToken = -1;
2290 // // to remember the previous token.
2291 // splitScanner.setSource(stringToSplit.toCharArray());
2293 // // start the loop
2295 // // takes the next token
2297 // if (currentToken != Scanner.TokenNameWHITESPACE)
2298 // previousToken = currentToken;
2299 // currentToken = splitScanner.getNextToken();
2300 // if (Scanner.DEBUG) {
2301 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2302 // int currentStartPosition = splitScanner
2303 // .getCurrentTokenStartPosition();
2304 // System.out.print(currentStartPosition + "," + currentEndPosition
2306 // System.out.println(scanner.toStringAction(currentToken));
2308 // } catch (InvalidInputException e) {
2309 // if (!handleInvalidToken(e))
2311 // currentToken = 0;
2312 // // this value is not modify when an exception is raised.
2314 // if (currentToken == TokenNameEOF)
2316 // if (firstTokenOnLine == -1) {
2317 // firstTokenOnLine = currentToken;
2319 // switch (currentToken) {
2320 // case TokenNameRBRACE :
2321 // case TokenNameRPAREN :
2322 // if (openParenthesisPositionCount > 0) {
2323 // if (openParenthesisPositionCount == 1
2324 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2325 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2326 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2327 // || (splitTokenDepth > openParenthesisPositionCount &&
2328 // openParenthesisPositionCount == 1)) {
2329 // splitTokenType = 0;
2330 // splitTokenDepth = openParenthesisPositionCount;
2331 // splitTokenPriority = Integer.MAX_VALUE;
2332 // substringsStartPositions[0] = 0;
2333 // // better token means the whole line until now is the first
2335 // substringsCount = 1; // resets the count of substrings
2336 // substringsEndPositions[0] = openParenthesisPosition[0];
2337 // // substring ends on operator start
2338 // position = openParenthesisPosition[0];
2339 // // the string mustn't be cut before the closing parenthesis but
2340 // // after the opening one.
2341 // splitOperatorsCount = 1; // resets the count of split operators
2342 // splitOperators[0] = 0;
2344 // openParenthesisPositionCount--;
2347 // case TokenNameLBRACE :
2348 // case TokenNameLPAREN :
2349 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2352 // openParenthesisPosition,
2354 // (openParenthesisPosition = new int[openParenthesisPositionCount *
2356 // 0, openParenthesisPositionCount);
2358 // openParenthesisPosition[openParenthesisPositionCount++] =
2359 // splitScanner.currentPosition;
2360 // if (currentToken == TokenNameLPAREN
2361 // && previousToken == TokenNameRPAREN) {
2362 // openParenthesisPosition[openParenthesisPositionCount - 1] =
2363 // splitScanner.startPosition;
2366 // case TokenNameSEMICOLON :
2368 // case TokenNameCOMMA :
2370 // case TokenNameEQUAL :
2372 // if (openParenthesisPositionCount < splitTokenDepth
2373 // || (openParenthesisPositionCount == splitTokenDepth &&
2374 // splitTokenPriority > getTokenPriority(currentToken))) {
2375 // // the current token is better than the one we currently have
2376 // // (in level or in priority if same level)
2377 // // reset the substringsCount
2378 // splitTokenDepth = openParenthesisPositionCount;
2379 // splitTokenType = currentToken;
2380 // splitTokenPriority = getTokenPriority(currentToken);
2381 // substringsStartPositions[0] = 0;
2382 // // better token means the whole line until now is the first
2384 // if (separateFirstArgumentOn(firstTokenOnLine)
2385 // && openParenthesisPositionCount > 0) {
2386 // substringsCount = 2; // resets the count of substrings
2387 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2389 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2391 // substringsEndPositions[1] = splitScanner.startPosition;
2392 // splitOperatorsCount = 2; // resets the count of split operators
2393 // splitOperators[0] = 0;
2394 // splitOperators[1] = currentToken;
2395 // position = splitScanner.currentPosition;
2396 // // next substring will start from operator end
2398 // substringsCount = 1; // resets the count of substrings
2399 // substringsEndPositions[0] = splitScanner.startPosition;
2400 // // substring ends on operator start
2401 // position = splitScanner.currentPosition;
2402 // // next substring will start from operator end
2403 // splitOperatorsCount = 1; // resets the count of split operators
2404 // splitOperators[0] = currentToken;
2407 // if ((openParenthesisPositionCount == splitTokenDepth &&
2408 // splitTokenPriority == getTokenPriority(currentToken))
2409 // && splitTokenType != TokenNameEQUAL
2410 // && currentToken != TokenNameEQUAL) {
2411 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2413 // // take only the 1st = into account.
2414 // // if another token with the same priority is found,
2415 // // push the start position of the substring and
2416 // // push the token into the stack.
2417 // // create a new array object if the current one is full.
2418 // if (substringsCount == substringsStartPositions.length) {
2421 // substringsStartPositions,
2423 // (substringsStartPositions = new int[substringsCount * 2]),
2424 // 0, substringsCount);
2425 // System.arraycopy(substringsEndPositions, 0,
2426 // (substringsEndPositions = new int[substringsCount * 2]),
2427 // 0, substringsCount);
2429 // if (splitOperatorsCount == splitOperators.length) {
2430 // System.arraycopy(splitOperators, 0,
2431 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2432 // splitOperatorsCount);
2434 // substringsStartPositions[substringsCount] = position;
2435 // substringsEndPositions[substringsCount++] =
2436 // splitScanner.startPosition;
2437 // // substring ends on operator start
2438 // position = splitScanner.currentPosition;
2439 // // next substring will start from operator end
2440 // splitOperators[splitOperatorsCount++] = currentToken;
2444 // case TokenNameCOLON :
2446 // // see 1FK7C5R, we only split on a colon, when it is associated
2447 // // with a question-mark.
2448 // // indeed it might appear also behind a case statement, and we do
2449 // // not to break at this point.
2450 // if ((splitOperatorsCount == 0)
2451 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2454 // case TokenNameextends :
2455 // case TokenNameimplements :
2456 // //case TokenNamethrows :
2457 // case TokenNameDOT :
2459 // case TokenNameMULTIPLY :
2461 // case TokenNameDIVIDE :
2463 // case TokenNameREMAINDER :
2465 // case TokenNamePLUS :
2466 // // + (15.17, 15.17.2)
2467 // case TokenNameMINUS :
2469 // case TokenNameLEFT_SHIFT :
2471 // case TokenNameRIGHT_SHIFT :
2473 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2474 // case TokenNameLESS :
2476 // case TokenNameLESS_EQUAL :
2478 // case TokenNameGREATER :
2480 // case TokenNameGREATER_EQUAL :
2482 // // case TokenNameinstanceof : // instanceof
2483 // case TokenNameEQUAL_EQUAL :
2484 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2485 // case TokenNameEQUAL_EQUAL_EQUAL :
2486 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2487 // case TokenNameNOT_EQUAL :
2488 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2489 // case TokenNameNOT_EQUAL_EQUAL :
2490 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2491 // case TokenNameAND :
2492 // // & (15.21, 15.21.1, 15.21.2)
2493 // case TokenNameOR :
2494 // // | (15.21, 15.21.1, 15.21.2)
2495 // case TokenNameXOR :
2496 // // ^ (15.21, 15.21.1, 15.21.2)
2497 // case TokenNameAND_AND :
2499 // case TokenNameOR_OR :
2501 // case TokenNameQUESTION :
2503 // case TokenNameMULTIPLY_EQUAL :
2505 // case TokenNameDIVIDE_EQUAL :
2507 // case TokenNameREMAINDER_EQUAL :
2509 // case TokenNamePLUS_EQUAL :
2511 // case TokenNameMINUS_EQUAL :
2513 // case TokenNameLEFT_SHIFT_EQUAL :
2515 // case TokenNameRIGHT_SHIFT_EQUAL :
2517 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2518 // case TokenNameAND_EQUAL :
2520 // case TokenNameXOR_EQUAL :
2522 // case TokenNameOR_EQUAL :
2524 // if ((openParenthesisPositionCount < splitTokenDepth ||
2525 // (openParenthesisPositionCount == splitTokenDepth &&
2526 // splitTokenPriority
2527 // > getTokenPriority(currentToken)))
2528 // && !((currentToken == TokenNamePLUS || currentToken ==
2529 // TokenNameMINUS) && (previousToken == TokenNameLBRACE
2530 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition
2532 // // the current token is better than the one we currently have
2533 // // (in level or in priority if same level)
2534 // // reset the substringsCount
2535 // splitTokenDepth = openParenthesisPositionCount;
2536 // splitTokenType = currentToken;
2537 // splitTokenPriority = getTokenPriority(currentToken);
2538 // substringsStartPositions[0] = 0;
2539 // // better token means the whole line until now is the first
2541 // if (separateFirstArgumentOn(firstTokenOnLine)
2542 // && openParenthesisPositionCount > 0) {
2543 // substringsCount = 2; // resets the count of substrings
2544 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2546 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2548 // substringsEndPositions[1] = splitScanner.startPosition;
2549 // splitOperatorsCount = 3; // resets the count of split operators
2550 // splitOperators[0] = 0;
2551 // splitOperators[1] = 0;
2552 // splitOperators[2] = currentToken;
2553 // position = splitScanner.currentPosition;
2554 // // next substring will start from operator end
2556 // substringsCount = 1; // resets the count of substrings
2557 // substringsEndPositions[0] = splitScanner.startPosition;
2558 // // substring ends on operator start
2559 // position = splitScanner.currentPosition;
2560 // // next substring will start from operator end
2561 // splitOperatorsCount = 2; // resets the count of split operators
2562 // splitOperators[0] = 0;
2563 // // nothing for first operand since operator will be inserted in
2564 // // front of the second operand
2565 // splitOperators[1] = currentToken;
2568 // if (openParenthesisPositionCount == splitTokenDepth
2569 // && splitTokenPriority == getTokenPriority(currentToken)) {
2570 // // if another token with the same priority is found,
2571 // // push the start position of the substring and
2572 // // push the token into the stack.
2573 // // create a new array object if the current one is full.
2574 // if (substringsCount == substringsStartPositions.length) {
2577 // substringsStartPositions,
2579 // (substringsStartPositions = new int[substringsCount * 2]),
2580 // 0, substringsCount);
2581 // System.arraycopy(substringsEndPositions, 0,
2582 // (substringsEndPositions = new int[substringsCount * 2]),
2583 // 0, substringsCount);
2585 // if (splitOperatorsCount == splitOperators.length) {
2586 // System.arraycopy(splitOperators, 0,
2587 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2588 // splitOperatorsCount);
2590 // substringsStartPositions[substringsCount] = position;
2591 // substringsEndPositions[substringsCount++] =
2592 // splitScanner.startPosition;
2593 // // substring ends on operator start
2594 // position = splitScanner.currentPosition;
2595 // // next substring will start from operator end
2596 // splitOperators[splitOperatorsCount++] = currentToken;
2602 // if (isComment(currentToken)) {
2603 // lastCommentStartPosition = splitScanner.startPosition;
2605 // lastCommentStartPosition = -1;
2608 // } catch (InvalidInputException e) {
2611 // // if the string cannot be split, return null.
2612 // if (splitOperatorsCount == 0)
2614 // // ## SPECIAL CASES BEGIN
2615 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2616 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2617 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2618 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 &&
2619 // lastOpenParenthesisPosition <= options.maxLineLength) ||
2620 // (separateFirstArgumentOn(firstTokenOnLine)
2621 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2622 // && (lastOpenParenthesisPosition < splitScanner.source.length &&
2623 // splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2624 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis
2626 // // not be broken on two lines
2627 // // only one split on a top level .
2628 // // or more than one split on . and substring before open parenthesis
2631 // // or split inside parenthesis and first token is not a for/while/if
2632 // SplitLine sl = split(
2633 // stringToSplit.substring(lastOpenParenthesisPosition),
2634 // lastOpenParenthesisPosition);
2635 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2636 // // trim() is used to remove the extra blanks at the end of the
2637 // // substring. See PR 1FGYPI1
2638 // return new SplitLine(new int[]{0, 0}, new String[]{
2639 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2640 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2641 // offsetInGlobalLine,
2642 // lastOpenParenthesisPosition + offsetInGlobalLine});
2644 // // right substring can be split and is split on comma
2645 // // copy substrings and operators
2646 // // except if the 1st string is empty.
2647 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2648 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2649 // String[] result = new String[subStringsLength];
2650 // int[] startIndexes = new int[subStringsLength];
2651 // int operatorsLength = sl.operators.length + 1 - startIndex;
2652 // int[] operators = new int[operatorsLength];
2653 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2654 // operators[0] = 0;
2655 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2656 // 1, subStringsLength - 1);
2657 // for (int i = subStringsLength - 1; i >= 0; i--) {
2658 // startIndexes[i] += offsetInGlobalLine;
2660 // System.arraycopy(sl.substrings, startIndex, result, 1,
2661 // subStringsLength - 1);
2662 // System.arraycopy(sl.operators, startIndex, operators, 1,
2663 // operatorsLength - 1);
2664 // return new SplitLine(operators, result, startIndexes);
2667 // // if the last token is a comment and the substring before the
2670 // // split before the comment and return the result.
2671 // if (lastCommentStartPosition > -1
2672 // && lastCommentStartPosition < options.maxLineLength
2673 // && splitTokenPriority > 50) {
2674 // int end = lastCommentStartPosition;
2675 // int start = lastCommentStartPosition;
2676 // if (stringToSplit.charAt(end - 1) == ' ') {
2679 // if (start != end && stringToSplit.charAt(start) == ' ') {
2682 // return new SplitLine(new int[]{0, 0}, new String[]{
2683 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2684 // new int[]{0, start});
2686 // if (position != stringToSplit.length()) {
2687 // if (substringsCount == substringsStartPositions.length) {
2688 // System.arraycopy(substringsStartPositions, 0,
2689 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2690 // substringsCount);
2691 // System.arraycopy(substringsEndPositions, 0,
2692 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2693 // substringsCount);
2695 // // avoid empty extra substring, e.g. line terminated with a
2697 // substringsStartPositions[substringsCount] = position;
2698 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2700 // if (splitOperatorsCount == splitOperators.length) {
2701 // System.arraycopy(splitOperators, 0,
2702 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2703 // splitOperatorsCount);
2705 // splitOperators[splitOperatorsCount] = 0;
2706 // // the last element of the stack is the position of the end of
2708 // // +1 because the substring method excludes the last character
2709 // String[] result = new String[substringsCount];
2710 // for (int i = 0; i < substringsCount; i++) {
2711 // int start = substringsStartPositions[i];
2712 // int end = substringsEndPositions[i];
2713 // if (stringToSplit.charAt(start) == ' ') {
2715 // substringsStartPositions[i]++;
2717 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2720 // result[i] = stringToSplit.substring(start, end);
2721 // substringsStartPositions[i] += offsetInGlobalLine;
2723 // if (splitOperatorsCount > substringsCount) {
2724 // System.arraycopy(substringsStartPositions, 0,
2725 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2726 // substringsCount);
2727 // System.arraycopy(substringsEndPositions, 0,
2728 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2729 // substringsCount);
2730 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2731 // substringsStartPositions[i] = position;
2732 // substringsEndPositions[i] = position;
2734 // System.arraycopy(splitOperators, 0,
2735 // (splitOperators = new int[splitOperatorsCount]), 0,
2736 // splitOperatorsCount);
2738 // System.arraycopy(substringsStartPositions, 0,
2739 // (substringsStartPositions = new int[substringsCount]), 0,
2740 // substringsCount);
2741 // System.arraycopy(substringsEndPositions, 0,
2742 // (substringsEndPositions = new int[substringsCount]), 0,
2743 // substringsCount);
2744 // System.arraycopy(splitOperators, 0,
2745 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2747 // SplitLine splitLine = new SplitLine(splitOperators, result,
2748 // substringsStartPositions);
2749 // return splitLine;
2752 private void updateMappedPositions(int startPosition) {
2753 if (positionsToMap == null) {
2756 char[] source = scanner.source;
2757 int sourceLength = source.length;
2758 while (indexToMap < positionsToMap.length
2759 && positionsToMap[indexToMap] <= startPosition) {
2760 int posToMap = positionsToMap[indexToMap];
2761 if (posToMap < 0 || posToMap >= sourceLength) {
2762 // protection against out of bounds position
2763 if (posToMap == sourceLength) {
2764 mappedPositions[indexToMap] = formattedSource.length();
2766 indexToMap = positionsToMap.length; // no more mapping
2769 if (CharOperation.isWhitespace(source[posToMap])) {
2770 mappedPositions[indexToMap] = startPosition + globalDelta
2773 if (posToMap == sourceLength - 1) {
2774 mappedPositions[indexToMap] = startPosition + globalDelta
2777 mappedPositions[indexToMap] = posToMap + globalDelta
2785 private void updateMappedPositionsWhileSplitting(int startPosition,
2787 if (mappedPositions == null || mappedPositions.length == indexInMap)
2789 while (indexInMap < mappedPositions.length
2790 && startPosition <= mappedPositions[indexInMap]
2791 && mappedPositions[indexInMap] < endPosition
2792 && indexInMap < indexToMap) {
2793 mappedPositions[indexInMap] += splitDelta;
2798 private int getLength(String s, int tabDepth) {
2800 for (int i = 0; i < tabDepth; i++) {
2801 length += options.tabSize;
2803 for (int i = 0, max = s.length(); i < max; i++) {
2804 char currentChar = s.charAt(i);
2805 switch (currentChar) {
2807 length += options.tabSize;
2817 * Sets the initial indentation level
2819 * @param indentationLevel
2820 * new indentation level
2824 public void setInitialIndentationLevel(int newIndentationLevel) {
2825 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;