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];
804 case TokenNameRPAREN:
805 // check for closing array declaration
806 if (arrayDeclarationCount > 0) {
807 if (arrayDeclarationParenthesis[arrayDeclarationCount] == openParenthesis[openParenthesisCount]) {
808 if (previousCompilableToken != TokenNameLPAREN) {
812 currentLineIndentationLevel = indentationLevel;
814 arrayDeclarationCount--;
817 // Decrease the parenthesis count
818 // if there is no more unclosed parenthesis,
819 // a new line and indent may be append (depending on the
822 if ((openParenthesisCount > 1)
823 && (openParenthesis[openParenthesisCount - 1] > 0)) {
824 openParenthesis[openParenthesisCount - 1]--;
825 if (openParenthesis[openParenthesisCount - 1] <= 0) {
826 pendingNewlineAfterParen = true;
827 inAssignment = false;
828 openParenthesisCount--;
831 openParenthesis[0]--;
833 pendingSpace = false;
835 case TokenNameLBRACE:
836 if (previousCompilableToken == TokenNameDOLLAR) {
839 if ((previousCompilableToken == TokenNameRBRACKET)
840 || (previousCompilableToken == TokenNameEQUAL)) {
841 // if (previousCompilableToken == TokenNameRBRACKET)
843 inArrayAssignment = true;
844 inAssignment = false;
846 if (inArrayAssignment) {
847 indentationLevel += pushBlock();
849 // Add new line and increase indentation level after
852 indentationLevel += pushBlock();
853 inAssignment = false;
857 case TokenNameRBRACE:
858 if (dollarBraceCount > 0) {
862 if (previousCompilableToken == TokenNameRPAREN) {
863 pendingSpace = false;
865 if (inArrayAssignment) {
866 inArrayAssignment = false;
868 indentationLevel += popInclusiveUntilBlock();
871 indentationLevel += popInclusiveUntilBlock();
872 if (previousCompilableToken == TokenNameRPAREN) {
873 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on
877 .append(options.lineSeparatorSequence);
878 increaseLineDelta(options.lineSeparatorSequence.length);
880 if (constructionsCount > 0) {
881 switch (constructions[constructionsCount - 1]) {
883 case TokenNameforeach:
884 // indentationLevel += popExclusiveUntilBlock();
886 case TokenNameswitch:
891 case TokenNamefinally:
894 // case TokenNamesynchronized :
895 clearNonBlockIndents = true;
902 case TokenNameLBRACKET:
904 pendingSpace = false;
906 case TokenNameRBRACKET:
907 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
908 // if there is no left bracket to close, the right bracket
911 pendingSpace = false;
914 pendingSpace = false;
915 if (arrayDeclarationCount > 0) {
921 pendingSpace = false;
923 case TokenNameSEMICOLON:
924 // Do not generate line terminators in the definition of
925 // the for statement.
926 // if not in this case, jump a line and reduce indentation
929 // if the block it closes belongs to a conditional statement
932 if (openParenthesisCount <= 1) {
934 if (expectingOpenBrace) {
935 clearNonBlockIndents = true;
936 expectingOpenBrace = false;
939 inAssignment = false;
940 pendingSpace = false;
942 case TokenNamePLUS_PLUS:
943 case TokenNameMINUS_MINUS:
944 // Do not put a space between a post-increment/decrement
945 // and the identifier being modified.
946 if (previousToken == TokenNameIdentifier
947 || previousToken == TokenNameRBRACKET
948 || previousToken == TokenNameVariable) {
949 pendingSpace = false;
953 // previously ADDITION
955 // Handle the unary operators plus and minus via a flag
956 if (!isLiteralToken(previousToken)
957 && previousToken != TokenNameIdentifier
958 && previousToken != TokenNameRPAREN
959 && previousToken != TokenNameRBRACKET) {
960 unarySignModifier = 1;
964 // In a switch/case statement, add a newline & indent
965 // when a colon is encountered.
966 if (tokenBeforeColonCount > 0) {
967 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
970 tokenBeforeColonCount--;
976 case Scanner.TokenNameCOMMENT_LINE:
979 currentLineIndentationLevel++;
981 break; // a line is always inserted after a one-line
983 case Scanner.TokenNameCOMMENT_PHPDOC:
984 case Scanner.TokenNameCOMMENT_BLOCK:
985 currentCommentOffset = getCurrentCommentOffset();
988 case Scanner.TokenNameWHITESPACE:
989 if (!phpTagAndWhitespace) {
990 // Count the number of line terminators in the
992 // line spacing can be preserved near comments.
993 char[] source = scanner.source;
994 newLinesInWhitespace = 0;
995 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
996 if (source[i] == '\r') {
998 if (source[++i] == '\n') {
999 newLinesInWhitespace++;
1001 newLinesInWhitespace++;
1004 newLinesInWhitespace++;
1006 } else if (source[i] == '\n') {
1007 newLinesInWhitespace++;
1010 increaseLineDelta(scanner.startPosition
1011 - scanner.currentPosition);
1014 // case TokenNameHTML :
1015 // // Add the next token to the formatted source string.
1016 // // outputCurrentToken(token);
1017 // int startPosition = scanner.startPosition;
1019 // for (int i = startPosition, max =
1020 // scanner.currentPosition; i <
1022 // char currentCharacter = scanner.source[i];
1023 // updateMappedPositions(i);
1024 // currentLineBuffer.append(currentCharacter);
1028 if ((token == TokenNameIdentifier) || isLiteralToken(token)
1029 || token == TokenNamesuper) {
1030 // || token == TokenNamethis) {
1031 // Do not put a space between a unary operator
1032 // (eg: ++, --, +, -) and the identifier being modified.
1033 if (previousToken == TokenNamePLUS_PLUS
1034 || previousToken == TokenNameMINUS_MINUS
1035 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
1036 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
1037 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
1038 pendingSpace = false;
1040 unarySignModifier = 0;
1044 // Do not output whitespace tokens.
1045 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1047 * Add pending space to the formatted source string. Do not
1048 * output a space under the following circumstances: 1) this
1049 * is the first pass 2) previous token is an open paren 3)
1050 * previous token is a period 4) previous token is the
1051 * logical compliment (eg: !) 5) previous token is the
1052 * bitwise compliment (eg: ~) 6) previous token is the open
1053 * bracket (eg: [) 7) in an assignment statement, if the
1054 * previous token is an open brace or the current token is a
1055 * close brace 8) previous token is a single line comment 9)
1056 * current token is a '->'
1058 if (token == TokenNameMINUS_GREATER
1059 && options.compactDereferencingMode)
1060 pendingSpace = false;
1062 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
1063 && token == TokenNameRBRACE;
1065 && insertSpaceAfter(previousToken)
1066 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
1067 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
1068 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
1069 && !openAndCloseBrace)
1072 // Add the next token to the formatted source string.
1073 outputCurrentToken(token);
1074 if (token == Scanner.TokenNameCOMMENT_LINE
1075 && openParenthesisCount > 1) {
1076 pendingNewLines = 0;
1077 currentLineBuffer.append(options.lineSeparatorSequence);
1078 increaseLineDelta(options.lineSeparatorSequence.length);
1080 pendingSpace = true;
1082 // Whitespace tokens do not need to be remembered.
1083 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
1084 previousToken = token;
1085 if (token != Scanner.TokenNameCOMMENT_BLOCK
1086 && token != Scanner.TokenNameCOMMENT_LINE
1087 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
1088 previousCompilableToken = token;
1092 output(copyRemainingSource());
1094 // dump the last token of the source in the formatted output.
1095 } catch (InvalidInputException e) {
1096 output(copyRemainingSource());
1098 // dump the last token of the source in the formatted output.
1103 * Formats the char array <code>sourceString</code>, and returns a string
1104 * containing the formatted version.
1106 * @return the formatted ouput.
1108 public String formatSourceString(String sourceString) {
1109 char[] sourceChars = sourceString.toCharArray();
1110 formattedSource = new StringBuffer(sourceChars.length);
1111 scanner.setSource(sourceChars);
1113 return formattedSource.toString();
1117 * Formats the char array <code>sourceString</code>, and returns a string
1118 * containing the formatted version.
1121 * the string to format
1122 * @param indentationLevel
1123 * the initial indentation level
1124 * @return the formatted ouput.
1126 public String format(String string, int indentationLevel) {
1127 return format(string, indentationLevel, (int[]) null);
1131 * Formats the char array <code>sourceString</code>, and returns a string
1132 * containing the formatted version. The positions array is modified to
1133 * contain the mapped positions.
1136 * the string to format
1137 * @param indentationLevel
1138 * the initial indentation level
1140 * the array of positions to map
1141 * @return the formatted ouput.
1143 public String format(String string, int indentationLevel, int[] positions) {
1144 return this.format(string, indentationLevel, positions, null);
1147 public String format(String string, int indentationLevel, int[] positions,
1148 String lineSeparator) {
1149 if (lineSeparator != null) {
1150 this.options.setLineSeparator(lineSeparator);
1152 if (positions != null) {
1153 this.setPositionsToMap(positions);
1154 this.setInitialIndentationLevel(indentationLevel);
1155 String formattedString = this.formatSourceString(string);
1156 int[] mappedPositions = this.getMappedPositions();
1158 .arraycopy(mappedPositions, 0, positions, 0,
1160 return formattedString;
1162 this.setInitialIndentationLevel(indentationLevel);
1163 return this.formatSourceString(string);
1168 * Formats the char array <code>sourceString</code>, and returns a string
1169 * containing the formatted version. The initial indentation level is 0.
1172 * the string to format
1173 * @return the formatted ouput.
1175 public String format(String string) {
1176 return this.format(string, 0, (int[]) null);
1180 * Formats a given source string, starting indenting it at a particular
1181 * depth and using the given options
1183 * @deprecated backport 1.0 internal functionality
1185 public static String format(String sourceString,
1186 int initialIndentationLevel, ConfigurableOption[] options) {
1187 CodeFormatter formatter = new CodeFormatter(options);
1188 formatter.setInitialIndentationLevel(initialIndentationLevel);
1189 return formatter.formatSourceString(sourceString);
1193 * Returns the number of characters and tab char between the beginning of
1194 * the line and the beginning of the comment.
1196 private int getCurrentCommentOffset() {
1197 int linePtr = scanner.linePtr;
1198 // if there is no beginning of line, return 0.
1202 int beginningOfLine = scanner.lineEnds[linePtr];
1203 int currentStartPosition = scanner.startPosition;
1204 char[] source = scanner.source;
1205 // find the position of the beginning of the line containing the comment
1206 while (beginningOfLine > currentStartPosition) {
1208 beginningOfLine = scanner.lineEnds[--linePtr];
1210 beginningOfLine = 0;
1214 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1215 char currentCharacter = source[i];
1216 switch (currentCharacter) {
1218 offset += options.tabSize;
1234 * Returns an array of descriptions for the configurable options. The
1235 * descriptions may be changed and passed back to a different compiler.
1237 * @deprecated backport 1.0 internal functionality
1239 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1240 String componentName = CodeFormatter.class.getName();
1241 FormatterOptions options = new FormatterOptions();
1242 return new ConfigurableOption[] {
1243 new ConfigurableOption(componentName, "newline.openingBrace",
1244 locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1246 new ConfigurableOption(componentName,
1247 "newline.controlStatement", locale,
1248 options.newlineInControlStatementMode ? 0 : 1),
1250 new ConfigurableOption(componentName, "newline.clearAll",
1251 locale, options.clearAllBlankLinesMode ? 0 : 1),
1253 // new ConfigurableOption(componentName, "newline.elseIf",
1255 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1256 new ConfigurableOption(componentName, "newline.emptyBlock",
1257 locale, options.newLineInEmptyBlockMode ? 0 : 1),
1259 new ConfigurableOption(componentName, "line.split", locale,
1260 options.maxLineLength),
1262 new ConfigurableOption(componentName,
1263 "style.compactAssignment", locale,
1264 options.compactAssignmentMode ? 0 : 1),
1266 new ConfigurableOption(componentName, "tabulation.char",
1267 locale, options.indentWithTab ? 0 : 1),
1269 new ConfigurableOption(componentName,
1270 "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1275 * Returns the array of mapped positions. Returns null is no positions have
1279 * @deprecated There is no need to retrieve the mapped positions anymore.
1281 public int[] getMappedPositions() {
1282 if (null != mappedPositions) {
1283 for (int i = 0; i < mappedPositions.length; i++) {
1284 if (mappedPositions[i] >= formattedSource.length()) {
1285 mappedPositions[i] = formattedSource.length() - 1;
1289 return mappedPositions;
1293 * Returns the priority of the token given as argument <br>
1294 * The most prioritary the token is, the smallest the return value is.
1296 * @return the priority of <code>token</code>
1298 * the token of which the priority is requested
1300 private static int getTokenPriority(int token) {
1302 case TokenNameextends:
1303 // case TokenNameimplements :
1304 // case TokenNamethrows :
1306 case TokenNameSEMICOLON:
1309 case TokenNameCOMMA:
1312 case TokenNameEQUAL:
1315 case TokenNameAND_AND:
1317 case TokenNameOR_OR:
1320 case TokenNameQUESTION:
1322 case TokenNameCOLON:
1324 return 50; // it's better cutting on ?: than on ;
1325 case TokenNameEQUAL_EQUAL:
1327 case TokenNameEQUAL_EQUAL_EQUAL:
1329 case TokenNameNOT_EQUAL:
1331 case TokenNameNOT_EQUAL_EQUAL:
1336 case TokenNameLESS_EQUAL:
1338 case TokenNameGREATER:
1340 case TokenNameGREATER_EQUAL:
1342 // case TokenNameinstanceof : // instanceof
1346 case TokenNameMINUS:
1349 case TokenNameMULTIPLY:
1351 case TokenNameDIVIDE:
1353 case TokenNameREMAINDER:
1356 case TokenNameLEFT_SHIFT:
1358 case TokenNameRIGHT_SHIFT:
1360 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1369 case TokenNameMULTIPLY_EQUAL:
1371 case TokenNameDIVIDE_EQUAL:
1373 case TokenNameREMAINDER_EQUAL:
1375 case TokenNamePLUS_EQUAL:
1377 case TokenNameMINUS_EQUAL:
1379 case TokenNameLEFT_SHIFT_EQUAL:
1381 case TokenNameRIGHT_SHIFT_EQUAL:
1383 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1384 case TokenNameAND_EQUAL:
1386 case TokenNameXOR_EQUAL:
1388 case TokenNameOR_EQUAL:
1390 case TokenNameDOT_EQUAL:
1397 return Integer.MAX_VALUE;
1402 * Handles the exception raised when an invalid token is encountered.
1403 * Returns true if the exception has been handled, false otherwise.
1405 private boolean handleInvalidToken(Exception e) {
1406 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1407 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1408 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1414 private final void increaseGlobalDelta(int offset) {
1415 globalDelta += offset;
1418 private final void increaseLineDelta(int offset) {
1419 lineDelta += offset;
1422 private final void increaseSplitDelta(int offset) {
1423 splitDelta += offset;
1427 * Returns true if a space has to be inserted after <code>operator</code>
1430 private boolean insertSpaceAfter(int token) {
1432 case TokenNameLPAREN:
1434 case TokenNameTWIDDLE:
1437 case TokenNameWHITESPACE:
1438 case TokenNameLBRACKET:
1439 case TokenNameDOLLAR:
1440 case Scanner.TokenNameCOMMENT_LINE:
1448 * Returns true if a space has to be inserted before <code>operator</code>
1449 * false otherwise. <br>
1450 * Cannot be static as it uses the code formatter options (to know if the
1451 * compact assignment mode is on).
1453 private boolean insertSpaceBefore(int token) {
1455 case TokenNameEQUAL:
1456 return (!options.compactAssignmentMode);
1462 private static boolean isComment(int token) {
1463 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1464 || token == Scanner.TokenNameCOMMENT_LINE
1465 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1469 private static boolean isLiteralToken(int token) {
1470 boolean result = token == TokenNameIntegerLiteral
1471 // || token == TokenNameLongLiteral
1472 // || token == TokenNameFloatingPointLiteral
1473 || token == TokenNameDoubleLiteral
1474 // || token == TokenNameCharacterLiteral
1475 || token == TokenNameStringDoubleQuote;
1480 * If the length of <code>oneLineBuffer</code> exceeds
1481 * <code>maxLineLength</code>, it is split and the result is dumped in
1482 * <code>formattedSource</code>
1484 * @param newLineCount
1485 * the number of new lines to append
1487 private void newLine(int newLineCount) {
1488 // format current line
1490 beginningOfLineIndex = formattedSource.length();
1491 String currentLine = currentLineBuffer.toString();
1492 if (containsOpenCloseBraces) {
1493 containsOpenCloseBraces = false;
1494 outputLine(currentLine, false, indentationLevelForOpenCloseBraces,
1496 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1498 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1,
1501 // dump line break(s)
1502 for (int i = 0; i < newLineCount; i++) {
1503 formattedSource.append(options.lineSeparatorSequence);
1504 increaseSplitDelta(options.lineSeparatorSequence.length);
1506 // reset formatter for next line
1507 int currentLength = currentLine.length();
1508 currentLineBuffer = new StringBuffer(
1509 currentLength > maxLineSize ? maxLineSize = currentLength
1511 increaseGlobalDelta(splitDelta);
1512 increaseGlobalDelta(lineDelta);
1514 currentLineIndentationLevel = initialIndentationLevel;
1517 private String operatorString(int operator) {
1519 case TokenNameextends:
1520 return "extends"; //$NON-NLS-1$
1521 // case TokenNameimplements :
1522 // return "implements"; //$NON-NLS-1$
1524 // case TokenNamethrows :
1525 // return "throws"; //$NON-NLS-1$
1526 case TokenNameSEMICOLON:
1528 return ";"; //$NON-NLS-1$
1529 case TokenNameCOMMA:
1531 return ","; //$NON-NLS-1$
1532 case TokenNameEQUAL:
1534 return "="; //$NON-NLS-1$
1535 case TokenNameAND_AND:
1537 return "&&"; //$NON-NLS-1$
1538 case TokenNameOR_OR:
1540 return "||"; //$NON-NLS-1$
1541 case TokenNameQUESTION:
1543 return "?"; //$NON-NLS-1$
1544 case TokenNameCOLON:
1546 return ":"; //$NON-NLS-1$
1547 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1549 return "::"; //$NON-NLS-1$
1550 case TokenNameEQUAL_EQUAL:
1551 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1552 return "=="; //$NON-NLS-1$
1553 case TokenNameEQUAL_EQUAL_EQUAL:
1554 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1555 return "==="; //$NON-NLS-1$
1556 case TokenNameEQUAL_GREATER:
1558 return "=>"; //$NON-NLS-1$
1559 case TokenNameNOT_EQUAL:
1560 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1561 return "!="; //$NON-NLS-1$
1562 case TokenNameNOT_EQUAL_EQUAL:
1563 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1564 return "!=="; //$NON-NLS-1$
1567 return "<"; //$NON-NLS-1$
1568 case TokenNameLESS_EQUAL:
1570 return "<="; //$NON-NLS-1$
1571 case TokenNameGREATER:
1573 return ">"; //$NON-NLS-1$
1574 case TokenNameGREATER_EQUAL:
1576 return ">="; //$NON-NLS-1$
1577 // case TokenNameinstanceof : // instanceof
1578 // return "instanceof"; //$NON-NLS-1$
1580 // + (15.17, 15.17.2)
1581 return "+"; //$NON-NLS-1$
1582 case TokenNameMINUS:
1584 return "-"; //$NON-NLS-1$
1585 case TokenNameMULTIPLY:
1587 return "*"; //$NON-NLS-1$
1588 case TokenNameDIVIDE:
1590 return "/"; //$NON-NLS-1$
1591 case TokenNameREMAINDER:
1593 return "%"; //$NON-NLS-1$
1594 case TokenNameLEFT_SHIFT:
1596 return "<<"; //$NON-NLS-1$
1597 case TokenNameRIGHT_SHIFT:
1599 return ">>"; //$NON-NLS-1$
1600 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1601 // return ">>>"; //$NON-NLS-1$
1603 // & (15.21, 15.21.1, 15.21.2)
1604 return "&"; //$NON-NLS-1$
1606 // | (15.21, 15.21.1, 15.21.2)
1607 return "|"; //$NON-NLS-1$
1609 // ^ (15.21, 15.21.1, 15.21.2)
1610 return "^"; //$NON-NLS-1$
1611 case TokenNameMULTIPLY_EQUAL:
1613 return "*="; //$NON-NLS-1$
1614 case TokenNameDIVIDE_EQUAL:
1616 return "/="; //$NON-NLS-1$
1617 case TokenNameREMAINDER_EQUAL:
1619 return "%="; //$NON-NLS-1$
1620 case TokenNamePLUS_EQUAL:
1622 return "+="; //$NON-NLS-1$
1623 case TokenNameMINUS_EQUAL:
1625 return "-="; //$NON-NLS-1$
1626 case TokenNameMINUS_GREATER:
1628 return "->"; //$NON-NLS-1$
1629 case TokenNameLEFT_SHIFT_EQUAL:
1631 return "<<="; //$NON-NLS-1$
1632 case TokenNameRIGHT_SHIFT_EQUAL:
1634 return ">>="; //$NON-NLS-1$
1635 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1636 // return ">>>="; //$NON-NLS-1$
1637 case TokenNameAND_EQUAL:
1639 return "&="; //$NON-NLS-1$
1640 case TokenNameXOR_EQUAL:
1642 return "^="; //$NON-NLS-1$
1643 case TokenNameOR_EQUAL:
1645 return "|="; //$NON-NLS-1$
1646 case TokenNameDOT_EQUAL:
1648 return ".="; //$NON-NLS-1$
1651 return "."; //$NON-NLS-1$
1653 return ""; //$NON-NLS-1$
1658 * Appends <code>stringToOutput</code> to the formatted output. <br>
1659 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1661 private void output(String stringToOutput) {
1662 char currentCharacter;
1663 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1664 currentCharacter = stringToOutput.charAt(i);
1665 if (currentCharacter != '\t') {
1666 currentLineBuffer.append(currentCharacter);
1671 private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
1672 newLine(newLineCount);
1673 formattedSource.append(scanner.source, scanner.startPosition,
1674 scanner.currentPosition - scanner.startPosition);
1678 * Appends <code>token</code> to the formatted output. <br>
1679 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1682 private void outputCurrentToken(int token) {
1683 char[] source = scanner.source;
1684 int startPosition = scanner.startPosition;
1686 case Scanner.TokenNameCOMMENT_PHPDOC:
1687 case Scanner.TokenNameCOMMENT_BLOCK:
1688 case Scanner.TokenNameCOMMENT_LINE:
1689 boolean endOfLine = false;
1690 int currentCommentOffset = getCurrentCommentOffset();
1691 int beginningOfLineSpaces = 0;
1693 currentCommentOffset = getCurrentCommentOffset();
1694 beginningOfLineSpaces = 0;
1695 boolean pendingCarriageReturn = false;
1696 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1697 char currentCharacter = source[i];
1698 updateMappedPositions(i);
1699 switch (currentCharacter) {
1701 pendingCarriageReturn = true;
1705 if (pendingCarriageReturn) {
1706 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1708 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1710 pendingCarriageReturn = false;
1711 currentLineBuffer.append(options.lineSeparatorSequence);
1712 beginningOfLineSpaces = 0;
1716 if (pendingCarriageReturn) {
1717 pendingCarriageReturn = false;
1718 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1719 currentLineBuffer.append(options.lineSeparatorSequence);
1720 beginningOfLineSpaces = 0;
1724 // we remove a maximum of currentCommentOffset
1726 // are converted to space numbers).
1727 beginningOfLineSpaces += options.tabSize;
1728 if (beginningOfLineSpaces > currentCommentOffset) {
1729 currentLineBuffer.append(currentCharacter);
1731 increaseGlobalDelta(-1);
1734 currentLineBuffer.append(currentCharacter);
1738 if (pendingCarriageReturn) {
1739 pendingCarriageReturn = false;
1740 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1741 currentLineBuffer.append(options.lineSeparatorSequence);
1742 beginningOfLineSpaces = 0;
1746 // we remove a maximum of currentCommentOffset
1748 // are converted to space numbers).
1749 beginningOfLineSpaces++;
1750 if (beginningOfLineSpaces > currentCommentOffset) {
1751 currentLineBuffer.append(currentCharacter);
1753 increaseGlobalDelta(-1);
1756 currentLineBuffer.append(currentCharacter);
1760 if (pendingCarriageReturn) {
1761 pendingCarriageReturn = false;
1762 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1763 currentLineBuffer.append(options.lineSeparatorSequence);
1764 beginningOfLineSpaces = 0;
1767 beginningOfLineSpaces = 0;
1768 currentLineBuffer.append(currentCharacter);
1773 updateMappedPositions(scanner.currentPosition - 1);
1774 multipleLineCommentCounter++;
1777 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1778 char currentCharacter = source[i];
1779 updateMappedPositions(i);
1780 currentLineBuffer.append(currentCharacter);
1786 * Outputs <code>currentString</code>:<br>
1788 * <li>If its length is < maxLineLength, output
1789 * <li>Otherwise it is split.
1792 * @param currentString
1794 * @param preIndented
1795 * whether the string to output was pre-indented
1797 * number of indentation to put in front of
1798 * <code>currentString</code>
1800 * value of the operator belonging to <code>currentString</code>.
1802 private void outputLine(String currentString, boolean preIndented,
1803 int depth, int operator, int substringIndex,
1804 int[] startSubstringIndexes, int offsetInGlobalLine) {
1805 boolean emptyFirstSubString = false;
1806 String operatorString = operatorString(operator);
1807 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1808 boolean placeOperatorAhead = !placeOperatorBehind;
1809 // dump prefix operator?
1810 if (placeOperatorAhead) {
1815 if (operator != 0) {
1816 if (insertSpaceBefore(operator)) {
1817 formattedSource.append(' ');
1818 increaseSplitDelta(1);
1820 formattedSource.append(operatorString);
1821 increaseSplitDelta(operatorString.length());
1822 if (insertSpaceAfter(operator)
1823 && operator != TokenNameimplements
1824 && operator != TokenNameextends) {
1825 // && operator != TokenNamethrows) {
1826 formattedSource.append(' ');
1827 increaseSplitDelta(1);
1831 SplitLine splitLine = null;
1832 if (options.maxLineLength == 0
1833 || getLength(currentString, depth) < options.maxLineLength
1834 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1835 // depending on the type of operator, outputs new line before of
1838 // indent before postfix operator
1839 // indent also when the line cannot be split
1840 if (operator == TokenNameextends || operator == TokenNameimplements) {
1841 // || operator == TokenNamethrows) {
1842 formattedSource.append(' ');
1843 increaseSplitDelta(1);
1845 if (placeOperatorBehind) {
1850 int max = currentString.length();
1851 if (multipleLineCommentCounter != 0) {
1853 BufferedReader reader = new BufferedReader(
1854 new StringReader(currentString));
1855 String line = reader.readLine();
1856 while (line != null) {
1857 updateMappedPositionsWhileSplitting(
1858 beginningOfLineIndex, beginningOfLineIndex
1860 + options.lineSeparatorSequence.length);
1861 formattedSource.append(line);
1862 beginningOfLineIndex = beginningOfLineIndex
1864 if ((line = reader.readLine()) != null) {
1866 .append(options.lineSeparatorSequence);
1867 beginningOfLineIndex += options.lineSeparatorSequence.length;
1868 dumpTab(currentLineIndentationLevel);
1872 } catch (IOException e) {
1873 e.printStackTrace();
1876 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1877 beginningOfLineIndex + max);
1878 for (int i = 0; i < max; i++) {
1879 char currentChar = currentString.charAt(i);
1880 switch (currentChar) {
1885 // fix for 1FFYL5C: LFCOM:ALL - Incorrect
1887 // split with a comment inside a condition
1888 // a substring cannot end with a
1889 // lineSeparatorSequence,
1890 // except if it has been added by format() after a
1894 .append(options.lineSeparatorSequence);
1895 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
1901 formattedSource.append(currentChar);
1905 // update positions inside the mappedPositions table
1906 if (substringIndex != -1) {
1907 if (multipleLineCommentCounter == 0) {
1908 int startPosition = beginningOfLineIndex
1909 + startSubstringIndexes[substringIndex];
1910 updateMappedPositionsWhileSplitting(startPosition,
1911 startPosition + max);
1913 // compute the splitDelta resulting with the operator and blank
1915 if (substringIndex + 1 != startSubstringIndexes.length) {
1916 increaseSplitDelta(startSubstringIndexes[substringIndex]
1917 + max - startSubstringIndexes[substringIndex + 1]);
1920 // dump postfix operator?
1921 if (placeOperatorBehind) {
1922 if (insertSpaceBefore(operator)) {
1923 formattedSource.append(' ');
1924 if (operator != 0) {
1925 increaseSplitDelta(1);
1928 formattedSource.append(operatorString);
1929 if (operator != 0) {
1930 increaseSplitDelta(operatorString.length());
1935 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1936 // extends has to stand alone on a line when currentString has been
1938 if (options.maxLineLength != 0 && splitLine != null
1939 && (operator == TokenNameextends)) {
1940 // || operator == TokenNameimplements
1941 // || operator == TokenNamethrows)) {
1942 formattedSource.append(options.lineSeparatorSequence);
1943 increaseSplitDelta(options.lineSeparatorSequence.length);
1946 if (operator == TokenNameextends) {
1947 // || operator == TokenNameimplements
1948 // || operator == TokenNamethrows) {
1949 formattedSource.append(' ');
1950 increaseSplitDelta(1);
1953 // perform actual splitting
1954 String result[] = splitLine.substrings;
1955 int[] splitOperators = splitLine.operators;
1956 if (result[0].length() == 0) {
1957 // when the substring 0 is null, the substring 1 is correctly
1960 emptyFirstSubString = true;
1962 // the operator going in front of the result[0] string is the operator
1964 for (int i = 0, max = result.length; i < max; i++) {
1965 // the new depth is the current one if this is the first substring,
1966 // the current one + 1 otherwise.
1967 // if the substring is a comment, use the current indentation Level
1968 // instead of the depth
1969 // (-1 because the ouputline increases depth).
1970 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of
1973 String currentResult = result[i];
1974 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1975 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1976 || currentResult.startsWith("//")) //$NON-NLS-1$
1977 ? indentationLevel - 1
1979 outputLine(currentResult, i == 0
1980 || (i == 1 && emptyFirstSubString) ? preIndented
1981 : false, i == 0 ? newDepth : newDepth + 1,
1982 splitOperators[i], i, splitLine.startSubstringsIndexes,
1983 currentString.indexOf(currentResult));
1985 formattedSource.append(options.lineSeparatorSequence);
1986 increaseSplitDelta(options.lineSeparatorSequence.length);
1990 if (result.length == splitOperators.length - 1) {
1991 int lastOperator = splitOperators[result.length];
1992 String lastOperatorString = operatorString(lastOperator);
1993 formattedSource.append(options.lineSeparatorSequence);
1994 increaseSplitDelta(options.lineSeparatorSequence.length);
1995 if (breakLineBeforeOperator(lastOperator)) {
1997 if (lastOperator != 0) {
1998 if (insertSpaceBefore(lastOperator)) {
1999 formattedSource.append(' ');
2000 increaseSplitDelta(1);
2002 formattedSource.append(lastOperatorString);
2003 increaseSplitDelta(lastOperatorString.length());
2004 if (insertSpaceAfter(lastOperator)
2005 && lastOperator != TokenNameimplements
2006 && lastOperator != TokenNameextends) {
2007 // && lastOperator != TokenNamethrows) {
2008 formattedSource.append(' ');
2009 increaseSplitDelta(1);
2014 if (placeOperatorBehind) {
2015 if (insertSpaceBefore(operator)) {
2016 formattedSource.append(' ');
2017 increaseSplitDelta(1);
2019 formattedSource.append(operatorString);
2020 // increaseSplitDelta(operatorString.length());
2025 * Pops the top statement of the stack if it is <code>token</code>
2027 private int pop(int token) {
2029 if ((constructionsCount > 0)
2030 && (constructions[constructionsCount - 1] == token)) {
2032 constructionsCount--;
2038 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
2039 * <code>NONINDENT_BLOCK</code>.
2041 private int popBlock() {
2043 if ((constructionsCount > 0)
2044 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
2045 if (constructions[constructionsCount - 1] == BLOCK)
2047 constructionsCount--;
2053 * Pops elements until the stack is empty or the top element is
2054 * <code>token</code>.<br>
2055 * Does not remove <code>token</code> from the stack.
2058 * the token to be left as the top of the stack
2060 private int popExclusiveUntil(int token) {
2062 int startCount = constructionsCount;
2063 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2064 if (constructions[i] != NONINDENT_BLOCK)
2066 constructionsCount--;
2072 * Pops elements until the stack is empty or the top element is a
2073 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2074 * Does not remove it from the stack.
2076 private int popExclusiveUntilBlock() {
2077 int startCount = constructionsCount;
2079 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2080 && constructions[i] != NONINDENT_BLOCK; i--) {
2081 constructionsCount--;
2088 * Pops elements until the stack is empty or the top element is a
2089 * <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
2090 * <code>CASE</code>.<br>
2091 * Does not remove it from the stack.
2093 private int popExclusiveUntilBlockOrCase() {
2094 int startCount = constructionsCount;
2096 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
2097 && constructions[i] != NONINDENT_BLOCK
2098 && constructions[i] != TokenNamecase; i--) {
2099 constructionsCount--;
2106 * Pops elements until the stack is empty or the top element is
2107 * <code>token</code>.<br>
2108 * Removes <code>token</code> from the stack too.
2111 * the token to remove from the stack
2113 private int popInclusiveUntil(int token) {
2114 int startCount = constructionsCount;
2116 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
2117 if (constructions[i] != NONINDENT_BLOCK)
2119 constructionsCount--;
2121 if (constructionsCount > 0) {
2122 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
2124 constructionsCount--;
2130 * Pops elements until the stack is empty or the top element is a
2131 * <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
2132 * Does not remove it from the stack.
2134 private int popInclusiveUntilBlock() {
2135 int startCount = constructionsCount;
2137 for (int i = startCount - 1; i >= 0
2138 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
2140 constructionsCount--;
2142 if (constructionsCount > 0) {
2143 if (constructions[constructionsCount - 1] == BLOCK)
2145 constructionsCount--;
2151 * Pushes a block in the stack. <br>
2152 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
2153 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
2154 * otherwise. Creates a new bigger array if the current one is full.
2156 private int pushBlock() {
2158 if (constructionsCount == constructions.length)
2159 System.arraycopy(constructions, 0,
2160 (constructions = new int[constructionsCount * 2]), 0,
2161 constructionsCount);
2162 if ((constructionsCount == 0)
2163 || (constructions[constructionsCount - 1] == BLOCK)
2164 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
2165 || (constructions[constructionsCount - 1] == TokenNamecase)) {
2167 constructions[constructionsCount++] = BLOCK;
2169 constructions[constructionsCount++] = NONINDENT_BLOCK;
2175 * Pushes <code>token</code>.<br>
2176 * Creates a new bigger array if the current one is full.
2178 private int pushControlStatement(int token) {
2179 if (constructionsCount == constructions.length)
2180 System.arraycopy(constructions, 0,
2181 (constructions = new int[constructionsCount * 2]), 0,
2182 constructionsCount);
2183 constructions[constructionsCount++] = token;
2187 private static boolean separateFirstArgumentOn(int currentToken) {
2188 // return (currentToken == TokenNameCOMMA || currentToken ==
2189 // TokenNameSEMICOLON);
2190 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
2191 && currentToken != TokenNameNOT
2192 && currentToken != TokenNamewhile
2193 && currentToken != TokenNamefor
2194 && currentToken != TokenNameforeach
2195 && currentToken != TokenNameswitch;
2199 * Set the positions to map. The mapped positions should be retrieved using
2200 * the getMappedPositions() method.
2204 * @deprecated Set the positions to map using the format(String, int, int[])
2207 * @see #getMappedPositions()
2209 public void setPositionsToMap(int[] positions) {
2210 positionsToMap = positions;
2213 mappedPositions = new int[positions.length];
2217 * Appends a space character to the current line buffer.
2219 private void space() {
2220 currentLineBuffer.append(' ');
2221 increaseLineDelta(1);
2225 * Splits <code>stringToSplit</code> on the top level token <br>
2226 * If there are several identical token at the same level, the string is cut
2229 * @return an object containing the operator and all the substrings or null
2230 * if the string cannot be split
2232 public SplitLine split(String stringToSplit) {
2233 return split(stringToSplit, 0);
2237 * Splits <code>stringToSplit</code> on the top level token <br>
2238 * If there are several identical token at the same level, the string is cut
2241 * @return an object containing the operator and all the substrings or null
2242 * if the string cannot be split
2244 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2246 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2247 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2249 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2252 // split doesn't work correct for PHP
2255 // int currentToken = 0;
2256 // int splitTokenType = 0;
2257 // int splitTokenDepth = Integer.MAX_VALUE;
2258 // int splitTokenPriority = Integer.MAX_VALUE;
2259 // int[] substringsStartPositions = new int[10];
2260 // // contains the start position of substrings
2261 // int[] substringsEndPositions = new int[10];
2262 // // contains the start position of substrings
2263 // int substringsCount = 1; // index in the substringsStartPosition
2265 // int[] splitOperators = new int[10];
2266 // // contains the start position of substrings
2267 // int splitOperatorsCount = 0; // index in the substringsStartPosition
2269 // int[] openParenthesisPosition = new int[10];
2270 // int openParenthesisPositionCount = 0;
2271 // int position = 0;
2272 // int lastOpenParenthesisPosition = -1;
2273 // // used to remember the position of the 1st open parenthesis
2274 // // needed for a pattern like: A.B(C); we want formatted like A.B(
2276 // // setup the scanner with a new source
2277 // int lastCommentStartPosition = -1;
2278 // // to remember the start position of the last comment
2279 // int firstTokenOnLine = -1;
2280 // // to remember the first token of the line
2281 // int previousToken = -1;
2282 // // to remember the previous token.
2283 // splitScanner.setSource(stringToSplit.toCharArray());
2285 // // start the loop
2287 // // takes the next token
2289 // if (currentToken != Scanner.TokenNameWHITESPACE)
2290 // previousToken = currentToken;
2291 // currentToken = splitScanner.getNextToken();
2292 // if (Scanner.DEBUG) {
2293 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2294 // int currentStartPosition = splitScanner
2295 // .getCurrentTokenStartPosition();
2296 // System.out.print(currentStartPosition + "," + currentEndPosition
2298 // System.out.println(scanner.toStringAction(currentToken));
2300 // } catch (InvalidInputException e) {
2301 // if (!handleInvalidToken(e))
2303 // currentToken = 0;
2304 // // this value is not modify when an exception is raised.
2306 // if (currentToken == TokenNameEOF)
2308 // if (firstTokenOnLine == -1) {
2309 // firstTokenOnLine = currentToken;
2311 // switch (currentToken) {
2312 // case TokenNameRBRACE :
2313 // case TokenNameRPAREN :
2314 // if (openParenthesisPositionCount > 0) {
2315 // if (openParenthesisPositionCount == 1
2316 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2317 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2318 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2319 // || (splitTokenDepth > openParenthesisPositionCount &&
2320 // openParenthesisPositionCount == 1)) {
2321 // splitTokenType = 0;
2322 // splitTokenDepth = openParenthesisPositionCount;
2323 // splitTokenPriority = Integer.MAX_VALUE;
2324 // substringsStartPositions[0] = 0;
2325 // // better token means the whole line until now is the first
2327 // substringsCount = 1; // resets the count of substrings
2328 // substringsEndPositions[0] = openParenthesisPosition[0];
2329 // // substring ends on operator start
2330 // position = openParenthesisPosition[0];
2331 // // the string mustn't be cut before the closing parenthesis but
2332 // // after the opening one.
2333 // splitOperatorsCount = 1; // resets the count of split operators
2334 // splitOperators[0] = 0;
2336 // openParenthesisPositionCount--;
2339 // case TokenNameLBRACE :
2340 // case TokenNameLPAREN :
2341 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2344 // openParenthesisPosition,
2346 // (openParenthesisPosition = new int[openParenthesisPositionCount *
2348 // 0, openParenthesisPositionCount);
2350 // openParenthesisPosition[openParenthesisPositionCount++] =
2351 // splitScanner.currentPosition;
2352 // if (currentToken == TokenNameLPAREN
2353 // && previousToken == TokenNameRPAREN) {
2354 // openParenthesisPosition[openParenthesisPositionCount - 1] =
2355 // splitScanner.startPosition;
2358 // case TokenNameSEMICOLON :
2360 // case TokenNameCOMMA :
2362 // case TokenNameEQUAL :
2364 // if (openParenthesisPositionCount < splitTokenDepth
2365 // || (openParenthesisPositionCount == splitTokenDepth &&
2366 // splitTokenPriority > getTokenPriority(currentToken))) {
2367 // // the current token is better than the one we currently have
2368 // // (in level or in priority if same level)
2369 // // reset the substringsCount
2370 // splitTokenDepth = openParenthesisPositionCount;
2371 // splitTokenType = currentToken;
2372 // splitTokenPriority = getTokenPriority(currentToken);
2373 // substringsStartPositions[0] = 0;
2374 // // better token means the whole line until now is the first
2376 // if (separateFirstArgumentOn(firstTokenOnLine)
2377 // && openParenthesisPositionCount > 0) {
2378 // substringsCount = 2; // resets the count of substrings
2379 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2381 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2383 // substringsEndPositions[1] = splitScanner.startPosition;
2384 // splitOperatorsCount = 2; // resets the count of split operators
2385 // splitOperators[0] = 0;
2386 // splitOperators[1] = currentToken;
2387 // position = splitScanner.currentPosition;
2388 // // next substring will start from operator end
2390 // substringsCount = 1; // resets the count of substrings
2391 // substringsEndPositions[0] = splitScanner.startPosition;
2392 // // substring ends on operator start
2393 // position = splitScanner.currentPosition;
2394 // // next substring will start from operator end
2395 // splitOperatorsCount = 1; // resets the count of split operators
2396 // splitOperators[0] = currentToken;
2399 // if ((openParenthesisPositionCount == splitTokenDepth &&
2400 // splitTokenPriority == getTokenPriority(currentToken))
2401 // && splitTokenType != TokenNameEQUAL
2402 // && currentToken != TokenNameEQUAL) {
2403 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2405 // // take only the 1st = into account.
2406 // // if another token with the same priority is found,
2407 // // push the start position of the substring and
2408 // // push the token into the stack.
2409 // // create a new array object if the current one is full.
2410 // if (substringsCount == substringsStartPositions.length) {
2413 // substringsStartPositions,
2415 // (substringsStartPositions = new int[substringsCount * 2]),
2416 // 0, substringsCount);
2417 // System.arraycopy(substringsEndPositions, 0,
2418 // (substringsEndPositions = new int[substringsCount * 2]),
2419 // 0, substringsCount);
2421 // if (splitOperatorsCount == splitOperators.length) {
2422 // System.arraycopy(splitOperators, 0,
2423 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2424 // splitOperatorsCount);
2426 // substringsStartPositions[substringsCount] = position;
2427 // substringsEndPositions[substringsCount++] =
2428 // splitScanner.startPosition;
2429 // // substring ends on operator start
2430 // position = splitScanner.currentPosition;
2431 // // next substring will start from operator end
2432 // splitOperators[splitOperatorsCount++] = currentToken;
2436 // case TokenNameCOLON :
2438 // // see 1FK7C5R, we only split on a colon, when it is associated
2439 // // with a question-mark.
2440 // // indeed it might appear also behind a case statement, and we do
2441 // // not to break at this point.
2442 // if ((splitOperatorsCount == 0)
2443 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2446 // case TokenNameextends :
2447 // case TokenNameimplements :
2448 // //case TokenNamethrows :
2449 // case TokenNameDOT :
2451 // case TokenNameMULTIPLY :
2453 // case TokenNameDIVIDE :
2455 // case TokenNameREMAINDER :
2457 // case TokenNamePLUS :
2458 // // + (15.17, 15.17.2)
2459 // case TokenNameMINUS :
2461 // case TokenNameLEFT_SHIFT :
2463 // case TokenNameRIGHT_SHIFT :
2465 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2466 // case TokenNameLESS :
2468 // case TokenNameLESS_EQUAL :
2470 // case TokenNameGREATER :
2472 // case TokenNameGREATER_EQUAL :
2474 // // case TokenNameinstanceof : // instanceof
2475 // case TokenNameEQUAL_EQUAL :
2476 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2477 // case TokenNameEQUAL_EQUAL_EQUAL :
2478 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2479 // case TokenNameNOT_EQUAL :
2480 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2481 // case TokenNameNOT_EQUAL_EQUAL :
2482 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2483 // case TokenNameAND :
2484 // // & (15.21, 15.21.1, 15.21.2)
2485 // case TokenNameOR :
2486 // // | (15.21, 15.21.1, 15.21.2)
2487 // case TokenNameXOR :
2488 // // ^ (15.21, 15.21.1, 15.21.2)
2489 // case TokenNameAND_AND :
2491 // case TokenNameOR_OR :
2493 // case TokenNameQUESTION :
2495 // case TokenNameMULTIPLY_EQUAL :
2497 // case TokenNameDIVIDE_EQUAL :
2499 // case TokenNameREMAINDER_EQUAL :
2501 // case TokenNamePLUS_EQUAL :
2503 // case TokenNameMINUS_EQUAL :
2505 // case TokenNameLEFT_SHIFT_EQUAL :
2507 // case TokenNameRIGHT_SHIFT_EQUAL :
2509 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2510 // case TokenNameAND_EQUAL :
2512 // case TokenNameXOR_EQUAL :
2514 // case TokenNameOR_EQUAL :
2516 // if ((openParenthesisPositionCount < splitTokenDepth ||
2517 // (openParenthesisPositionCount == splitTokenDepth &&
2518 // splitTokenPriority
2519 // > getTokenPriority(currentToken)))
2520 // && !((currentToken == TokenNamePLUS || currentToken ==
2521 // TokenNameMINUS) && (previousToken == TokenNameLBRACE
2522 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition
2524 // // the current token is better than the one we currently have
2525 // // (in level or in priority if same level)
2526 // // reset the substringsCount
2527 // splitTokenDepth = openParenthesisPositionCount;
2528 // splitTokenType = currentToken;
2529 // splitTokenPriority = getTokenPriority(currentToken);
2530 // substringsStartPositions[0] = 0;
2531 // // better token means the whole line until now is the first
2533 // if (separateFirstArgumentOn(firstTokenOnLine)
2534 // && openParenthesisPositionCount > 0) {
2535 // substringsCount = 2; // resets the count of substrings
2536 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth -
2538 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth
2540 // substringsEndPositions[1] = splitScanner.startPosition;
2541 // splitOperatorsCount = 3; // resets the count of split operators
2542 // splitOperators[0] = 0;
2543 // splitOperators[1] = 0;
2544 // splitOperators[2] = currentToken;
2545 // position = splitScanner.currentPosition;
2546 // // next substring will start from operator end
2548 // substringsCount = 1; // resets the count of substrings
2549 // substringsEndPositions[0] = splitScanner.startPosition;
2550 // // substring ends on operator start
2551 // position = splitScanner.currentPosition;
2552 // // next substring will start from operator end
2553 // splitOperatorsCount = 2; // resets the count of split operators
2554 // splitOperators[0] = 0;
2555 // // nothing for first operand since operator will be inserted in
2556 // // front of the second operand
2557 // splitOperators[1] = currentToken;
2560 // if (openParenthesisPositionCount == splitTokenDepth
2561 // && splitTokenPriority == getTokenPriority(currentToken)) {
2562 // // if another token with the same priority is found,
2563 // // push the start position of the substring and
2564 // // push the token into the stack.
2565 // // create a new array object if the current one is full.
2566 // if (substringsCount == substringsStartPositions.length) {
2569 // substringsStartPositions,
2571 // (substringsStartPositions = new int[substringsCount * 2]),
2572 // 0, substringsCount);
2573 // System.arraycopy(substringsEndPositions, 0,
2574 // (substringsEndPositions = new int[substringsCount * 2]),
2575 // 0, substringsCount);
2577 // if (splitOperatorsCount == splitOperators.length) {
2578 // System.arraycopy(splitOperators, 0,
2579 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2580 // splitOperatorsCount);
2582 // substringsStartPositions[substringsCount] = position;
2583 // substringsEndPositions[substringsCount++] =
2584 // splitScanner.startPosition;
2585 // // substring ends on operator start
2586 // position = splitScanner.currentPosition;
2587 // // next substring will start from operator end
2588 // splitOperators[splitOperatorsCount++] = currentToken;
2594 // if (isComment(currentToken)) {
2595 // lastCommentStartPosition = splitScanner.startPosition;
2597 // lastCommentStartPosition = -1;
2600 // } catch (InvalidInputException e) {
2603 // // if the string cannot be split, return null.
2604 // if (splitOperatorsCount == 0)
2606 // // ## SPECIAL CASES BEGIN
2607 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2608 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2609 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2610 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 &&
2611 // lastOpenParenthesisPosition <= options.maxLineLength) ||
2612 // (separateFirstArgumentOn(firstTokenOnLine)
2613 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2614 // && (lastOpenParenthesisPosition < splitScanner.source.length &&
2615 // splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2616 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis
2618 // // not be broken on two lines
2619 // // only one split on a top level .
2620 // // or more than one split on . and substring before open parenthesis
2623 // // or split inside parenthesis and first token is not a for/while/if
2624 // SplitLine sl = split(
2625 // stringToSplit.substring(lastOpenParenthesisPosition),
2626 // lastOpenParenthesisPosition);
2627 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2628 // // trim() is used to remove the extra blanks at the end of the
2629 // // substring. See PR 1FGYPI1
2630 // return new SplitLine(new int[]{0, 0}, new String[]{
2631 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2632 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2633 // offsetInGlobalLine,
2634 // lastOpenParenthesisPosition + offsetInGlobalLine});
2636 // // right substring can be split and is split on comma
2637 // // copy substrings and operators
2638 // // except if the 1st string is empty.
2639 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2640 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2641 // String[] result = new String[subStringsLength];
2642 // int[] startIndexes = new int[subStringsLength];
2643 // int operatorsLength = sl.operators.length + 1 - startIndex;
2644 // int[] operators = new int[operatorsLength];
2645 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2646 // operators[0] = 0;
2647 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2648 // 1, subStringsLength - 1);
2649 // for (int i = subStringsLength - 1; i >= 0; i--) {
2650 // startIndexes[i] += offsetInGlobalLine;
2652 // System.arraycopy(sl.substrings, startIndex, result, 1,
2653 // subStringsLength - 1);
2654 // System.arraycopy(sl.operators, startIndex, operators, 1,
2655 // operatorsLength - 1);
2656 // return new SplitLine(operators, result, startIndexes);
2659 // // if the last token is a comment and the substring before the
2662 // // split before the comment and return the result.
2663 // if (lastCommentStartPosition > -1
2664 // && lastCommentStartPosition < options.maxLineLength
2665 // && splitTokenPriority > 50) {
2666 // int end = lastCommentStartPosition;
2667 // int start = lastCommentStartPosition;
2668 // if (stringToSplit.charAt(end - 1) == ' ') {
2671 // if (start != end && stringToSplit.charAt(start) == ' ') {
2674 // return new SplitLine(new int[]{0, 0}, new String[]{
2675 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2676 // new int[]{0, start});
2678 // if (position != stringToSplit.length()) {
2679 // if (substringsCount == substringsStartPositions.length) {
2680 // System.arraycopy(substringsStartPositions, 0,
2681 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2682 // substringsCount);
2683 // System.arraycopy(substringsEndPositions, 0,
2684 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2685 // substringsCount);
2687 // // avoid empty extra substring, e.g. line terminated with a
2689 // substringsStartPositions[substringsCount] = position;
2690 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2692 // if (splitOperatorsCount == splitOperators.length) {
2693 // System.arraycopy(splitOperators, 0,
2694 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2695 // splitOperatorsCount);
2697 // splitOperators[splitOperatorsCount] = 0;
2698 // // the last element of the stack is the position of the end of
2700 // // +1 because the substring method excludes the last character
2701 // String[] result = new String[substringsCount];
2702 // for (int i = 0; i < substringsCount; i++) {
2703 // int start = substringsStartPositions[i];
2704 // int end = substringsEndPositions[i];
2705 // if (stringToSplit.charAt(start) == ' ') {
2707 // substringsStartPositions[i]++;
2709 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2712 // result[i] = stringToSplit.substring(start, end);
2713 // substringsStartPositions[i] += offsetInGlobalLine;
2715 // if (splitOperatorsCount > substringsCount) {
2716 // System.arraycopy(substringsStartPositions, 0,
2717 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2718 // substringsCount);
2719 // System.arraycopy(substringsEndPositions, 0,
2720 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2721 // substringsCount);
2722 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2723 // substringsStartPositions[i] = position;
2724 // substringsEndPositions[i] = position;
2726 // System.arraycopy(splitOperators, 0,
2727 // (splitOperators = new int[splitOperatorsCount]), 0,
2728 // splitOperatorsCount);
2730 // System.arraycopy(substringsStartPositions, 0,
2731 // (substringsStartPositions = new int[substringsCount]), 0,
2732 // substringsCount);
2733 // System.arraycopy(substringsEndPositions, 0,
2734 // (substringsEndPositions = new int[substringsCount]), 0,
2735 // substringsCount);
2736 // System.arraycopy(splitOperators, 0,
2737 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2739 // SplitLine splitLine = new SplitLine(splitOperators, result,
2740 // substringsStartPositions);
2741 // return splitLine;
2744 private void updateMappedPositions(int startPosition) {
2745 if (positionsToMap == null) {
2748 char[] source = scanner.source;
2749 int sourceLength = source.length;
2750 while (indexToMap < positionsToMap.length
2751 && positionsToMap[indexToMap] <= startPosition) {
2752 int posToMap = positionsToMap[indexToMap];
2753 if (posToMap < 0 || posToMap >= sourceLength) {
2754 // protection against out of bounds position
2755 if (posToMap == sourceLength) {
2756 mappedPositions[indexToMap] = formattedSource.length();
2758 indexToMap = positionsToMap.length; // no more mapping
2761 if (CharOperation.isWhitespace(source[posToMap])) {
2762 mappedPositions[indexToMap] = startPosition + globalDelta
2765 if (posToMap == sourceLength - 1) {
2766 mappedPositions[indexToMap] = startPosition + globalDelta
2769 mappedPositions[indexToMap] = posToMap + globalDelta
2777 private void updateMappedPositionsWhileSplitting(int startPosition,
2779 if (mappedPositions == null || mappedPositions.length == indexInMap)
2781 while (indexInMap < mappedPositions.length
2782 && startPosition <= mappedPositions[indexInMap]
2783 && mappedPositions[indexInMap] < endPosition
2784 && indexInMap < indexToMap) {
2785 mappedPositions[indexInMap] += splitDelta;
2790 private int getLength(String s, int tabDepth) {
2792 for (int i = 0; i < tabDepth; i++) {
2793 length += options.tabSize;
2795 for (int i = 0, max = s.length(); i < max; i++) {
2796 char currentChar = s.charAt(i);
2797 switch (currentChar) {
2799 length += options.tabSize;
2809 * Sets the initial indentation level
2811 * @param indentationLevel
2812 * new indentation level
2816 public void setInitialIndentationLevel(int newIndentationLevel) {
2817 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;