1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.formatter;
13 import java.io.BufferedReader;
14 import java.io.IOException;
15 import java.io.StringReader;
16 import java.util.Hashtable;
17 import java.util.Locale;
20 import net.sourceforge.phpdt.core.ICodeFormatter;
21 import net.sourceforge.phpdt.core.compiler.CharOperation;
22 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
23 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
24 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
25 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;
30 /** <h2>How to format a piece of code ?</h2>
31 * <ul><li>Create an instance of <code>CodeFormatter</code>
32 * <li>Use the method <code>void format(aString)</code>
33 * on this instance to format <code>aString</code>.
34 * It will return the formatted string.</ul>
36 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
38 public FormatterOptions options;
41 * Represents a block in the <code>constructions</code> stack.
43 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
46 * Represents a block following a control statement in the <code>constructions</code> stack.
48 public static final int NONINDENT_BLOCK = -100;
51 * Contains the formatted output.
53 StringBuffer formattedSource;
56 * Contains the current line.<br>
57 * Will be dumped at the next "newline"
59 StringBuffer currentLineBuffer;
62 * Used during the formatting to get each token.
67 * Contains the tokens responsible for the current indentation level
68 * and the blocks not closed yet.
70 private int[] constructions;
73 * Index in the <code>constructions</code> array.
75 private int constructionsCount;
78 * Level of indentation of the current token (number of tab char put in front of it).
80 private int indentationLevel;
83 * Regular level of indentation of all the lines
85 private int initialIndentationLevel;
88 * Used to split a line.
93 * To remember the offset between the beginning of the line and the
94 * beginning of the comment.
96 int currentCommentOffset;
97 int currentLineIndentationLevel;
99 private boolean containsOpenCloseBraces;
100 private int indentationLevelForOpenCloseBraces;
103 * Collections of positions to map
105 private int[] positionsToMap;
108 * Collections of mapped positions
110 private int[] mappedPositions;
112 private int indexToMap;
114 private int indexInMap;
116 private int globalDelta;
118 private int lineDelta;
120 private int splitDelta;
122 private int beginningOfLineIndex;
124 private int multipleLineCommentCounter;
127 * Creates a new instance of Code Formatter using the given settings.
129 * @deprecated backport 1.0 internal functionality
131 public CodeFormatter(ConfigurableOption[] settings) {
132 this(convertConfigurableOptions(settings));
136 * Creates a new instance of Code Formatter using the FormattingOptions object
138 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
140 public CodeFormatter() {
144 * Creates a new instance of Code Formatter using the given settings.
146 public CodeFormatter(Map settings) {
148 // initialize internal state
149 constructionsCount = 0;
150 constructions = new int[10];
151 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
152 currentCommentOffset = -1;
154 // initialize primary and secondary scanners
155 scanner = new Scanner(true /*comment*/
156 , true /*whitespace*/
159 ); // regular scanner for forming lines
160 scanner.recordLineSeparator = true;
162 // to remind of the position of the beginning of the line.
163 splitScanner = new Scanner(true /*comment*/
164 , true /*whitespace*/
168 // secondary scanner to split long lines formed by primary scanning
170 // initialize current line buffer
171 currentLineBuffer = new StringBuffer();
172 this.options = new FormatterOptions(settings);
176 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
179 private static boolean breakLineBeforeOperator(int operator) {
181 case TokenNameCOMMA :
182 case TokenNameSEMICOLON :
183 case TokenNameEQUAL :
191 * @deprecated backport 1.0 internal functionality
193 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
194 Hashtable options = new Hashtable(10);
196 for (int i = 0; i < settings.length; i++) {
199 .equals(CodeFormatter.class.getName())) {
200 String optionName = settings[i].getOptionName();
201 int valueIndex = settings[i].getCurrentValueIndex();
203 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
204 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
206 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
207 options.put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
209 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
210 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
212 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
213 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
215 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
216 options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
218 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
219 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
221 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
222 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
225 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
227 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
228 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
237 * Returns the end of the source code.
239 private final String copyRemainingSource() {
240 char str[] = scanner.source;
241 int startPosition = scanner.startPosition;
242 int length = str.length - startPosition;
243 StringBuffer bufr = new StringBuffer(length);
244 if (startPosition < str.length) {
245 bufr.append(str, startPosition, length);
247 return (bufr.toString());
251 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
253 private void dumpTab(int tabCount) {
254 if (options.indentWithTab) {
255 for (int j = 0; j < tabCount; j++) {
256 formattedSource.append('\t');
257 increaseSplitDelta(1);
260 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
261 formattedSource.append(' ');
262 increaseSplitDelta(1);
268 * Dumps <code>currentLineBuffer</code> into the formatted string.
270 private void flushBuffer() {
271 String currentString = currentLineBuffer.toString();
273 beginningOfLineIndex = formattedSource.length();
274 if (containsOpenCloseBraces) {
275 containsOpenCloseBraces = false;
279 indentationLevelForOpenCloseBraces,
284 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
289 currentLineIndentationLevel,
295 int scannerSourceLength = scanner.source.length;
296 if (scannerSourceLength > 2) {
297 if (scanner.source[scannerSourceLength - 1] == '\n'
298 && scanner.source[scannerSourceLength - 2] == '\r') {
299 formattedSource.append(options.lineSeparatorSequence);
300 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
301 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
302 formattedSource.append(options.lineSeparatorSequence);
303 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
304 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
305 formattedSource.append(options.lineSeparatorSequence);
306 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
309 updateMappedPositions(scanner.startPosition);
313 * Formats the input string.
315 private void format() {
317 int previousToken = 0;
318 int previousCompilableToken = 0;
319 int indentationOffset = 0;
320 int newLinesInWhitespace = 0;
322 // number of new lines in the previous whitespace token
323 // (used to leave blank lines before comments)
324 int pendingNewLines = 0;
325 boolean expectingOpenBrace = false;
326 boolean clearNonBlockIndents = false;
327 // true if all indentations till the 1st { (usefull after } or ;)
328 boolean pendingSpace = true;
329 boolean pendingNewlineAfterParen = false;
330 // true when a cr is to be put after a ) (in conditional statements)
331 boolean inAssignment = false;
332 boolean inArrayAssignment = false;
333 boolean inThrowsClause = false;
334 boolean inClassOrInterfaceHeader = false;
336 // openBracketCount is used to count the number of open brackets not closed yet.
337 int openBracketCount = 0;
338 int unarySignModifier = 0;
340 // openParenthesis[0] is used to count the parenthesis not belonging to a condition
341 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
342 int openParenthesisCount = 1;
343 int[] openParenthesis = new int[10];
345 // tokenBeforeColon is used to know what token goes along with the current :
346 // it can be case or ?
347 int tokenBeforeColonCount = 0;
348 int[] tokenBeforeColon = new int[10];
350 constructionsCount = 0; // initializes the constructions count.
352 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
355 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
356 boolean specialElse = false;
358 // OPTION (IndentationLevel): initial indentation level may be non-zero.
359 currentLineIndentationLevel += constructionsCount;
361 // An InvalidInputException exception might cause the termination of this loop.
364 // Get the next token. Catch invalid input and output it
365 // with minimal formatting, also catch end of input and
368 token = scanner.getNextToken();
370 // Patch for line comment
371 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
372 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
373 int length = scanner.currentPosition;
374 loop : for (int index = length - 1; index >= 0; index--) {
375 switch (scanner.source[index]) {
378 scanner.currentPosition--;
385 } catch (InvalidInputException e) {
386 if (!handleInvalidToken(e)) {
391 if (token == Scanner.TokenNameEOF)
394 /* ## MODIFYING the indentation level before generating new lines
395 and indentation in the output string
398 // Removes all the indentations made by statements not followed by a block
399 // except if the current token is ELSE, CATCH or if we are in a switch/case
400 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
403 if (constructionsCount > 0
404 && constructions[constructionsCount - 1] == TokenNameelse) {
408 indentationLevel += popInclusiveUntil(TokenNameif);
410 // case TokenNamecatch :
411 // indentationLevel += popInclusiveUntil(TokenNamecatch);
413 // case TokenNamefinally :
414 // indentationLevel += popInclusiveUntil(TokenNamecatch);
416 case TokenNamewhile :
417 if (nlicsToken == TokenNamedo) {
418 indentationLevel += pop(TokenNamedo);
422 indentationLevel += popExclusiveUntilBlockOrCase();
423 // clear until a CASE, DEFAULT or BLOCK is encountered.
424 // Thus, the indentationLevel is correctly cleared either
425 // in a switch/case statement or in any other situation.
427 clearNonBlockIndents = false;
429 // returns to the indentation level created by the SWITCH keyword
430 // if the current token is a CASE or a DEFAULT
431 if (token == TokenNamecase || token == TokenNamedefault) {
432 indentationLevel += pop(TokenNamecase);
434 // if (token == Scanner.TokenNamethrows) {
435 // inThrowsClause = true;
438 == Scanner.TokenNameclass // || token == Scanner.TokenNameinterface
440 && previousToken != Scanner.TokenNameDOT) {
441 inClassOrInterfaceHeader = true;
444 /* ## APPEND newlines and indentations to the output string
446 // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
447 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
448 if (pendingNewlineAfterParen
449 && previousCompilableToken == TokenNameelse
450 && token == TokenNameif
451 && options.compactElseIfMode) {
452 pendingNewlineAfterParen = false;
454 indentationLevel += pop(TokenNameelse);
455 // because else if is now one single statement,
456 // the indentation level after it is increased by one and not by 2
457 // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
459 // Add a newline & indent to the formatted source string if
460 // a for/if-else/while statement was scanned and there is no block
462 pendingNewlineAfterParen =
463 pendingNewlineAfterParen
464 || (previousCompilableToken == TokenNameRPAREN
465 && token == TokenNameLBRACE);
466 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
467 pendingNewlineAfterParen = false;
469 // Do to add a newline & indent sequence if the current token is an
470 // open brace or a period or if the current token is a semi-colon and the
471 // previous token is a close paren.
472 // add a new line if a parenthesis belonging to a for() statement
473 // has been closed and the current token is not an opening brace
474 if (token != TokenNameLBRACE
475 && !isComment(token) // to avoid adding new line between else and a comment
476 && token != TokenNameDOT
477 && !(previousCompilableToken == TokenNameRPAREN
478 && token == TokenNameSEMICOLON)) {
480 currentLineIndentationLevel = indentationLevel;
482 pendingSpace = false;
484 if (token == TokenNameLBRACE
485 && options.newLineBeforeOpeningBraceMode) {
487 if (constructionsCount > 0
488 && constructions[constructionsCount - 1] != BLOCK
489 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
490 currentLineIndentationLevel = indentationLevel - 1;
492 currentLineIndentationLevel = indentationLevel;
495 pendingSpace = false;
499 if (token == TokenNameLBRACE
500 && options.newLineBeforeOpeningBraceMode
501 && constructionsCount > 0
502 && constructions[constructionsCount - 1] == TokenNamedo) {
504 currentLineIndentationLevel = indentationLevel - 1;
506 pendingSpace = false;
509 if (token == TokenNameLBRACE && inThrowsClause) {
510 inThrowsClause = false;
511 if (options.newLineBeforeOpeningBraceMode) {
513 currentLineIndentationLevel = indentationLevel;
515 pendingSpace = false;
519 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
520 inClassOrInterfaceHeader = false;
521 if (options.newLineBeforeOpeningBraceMode) {
523 currentLineIndentationLevel = indentationLevel;
525 pendingSpace = false;
528 // Add pending new lines to the formatted source string.
529 // Note: pending new lines are not added if the current token
530 // is a single line comment or whitespace.
531 // if the comment is between parenthesis, there is no blank line preservation
532 // (if it's a one-line comment, a blank line is added after it).
533 if (((pendingNewLines > 0 && (!isComment(token)))
534 || (newLinesInWhitespace > 0
535 && (openParenthesisCount <= 1 && isComment(token)))
536 || (previousCompilableToken == TokenNameLBRACE
537 && token == TokenNameRBRACE))
538 && token != Scanner.TokenNameWHITESPACE) {
540 // Do not add newline & indent between an adjoining close brace and
541 // close paren. Anonymous inner classes may use this form.
542 boolean closeBraceAndCloseParen =
543 previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
545 // OPTION (NewLineInCompoundStatement): do not add newline & indent
546 // between close brace and else, (do) while, catch, and finally if
547 // newlineInCompoundStatement is true.
548 boolean nlicsOption =
549 previousToken == TokenNameRBRACE
550 && !options.newlineInControlStatementMode
551 && (token == TokenNameelse
552 || (token == TokenNamewhile && nlicsToken == TokenNamedo));
553 // || token == TokenNamecatch
554 // || token == TokenNamefinally);
556 // Do not add a newline & indent between a close brace and semi-colon.
557 boolean semiColonAndCloseBrace =
558 previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
560 // Do not add a new line & indent between a multiline comment and a opening brace
561 boolean commentAndOpenBrace =
562 previousToken == Scanner.TokenNameCOMMENT_BLOCK
563 && token == TokenNameLBRACE;
565 // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
566 boolean commaAndCloseBrace =
567 previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
569 // Add a newline and indent, if appropriate.
571 || (!commentAndOpenBrace
572 && !closeBraceAndCloseParen
574 && !semiColonAndCloseBrace
575 && !commaAndCloseBrace)) {
577 // if clearAllBlankLinesMode=false, leaves the blank lines
578 // inserted by the user
579 // if clearAllBlankLinesMode=true, removes all of then
580 // and insert only blank lines required by the formatting.
581 if (!options.clearAllBlankLinesMode) {
582 // (isComment(token))
584 (pendingNewLines < newLinesInWhitespace)
585 ? newLinesInWhitespace
587 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
589 if (previousCompilableToken == TokenNameLBRACE
590 && token == TokenNameRBRACE) {
591 containsOpenCloseBraces = true;
592 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
593 if (isComment(previousToken)) {
594 newLine(pendingNewLines);
596 /* if (!(constructionsCount > 1
597 && constructions[constructionsCount-1] == NONINDENT_BLOCK
598 && (constructions[constructionsCount-2] == TokenNamefor
599 || constructions[constructionsCount-2] == TokenNamewhile))) {*/
600 if (options.newLineInEmptyBlockMode) {
601 if (inArrayAssignment) {
602 newLine(1); // array assigment with an empty block
604 newLine(pendingNewLines);
610 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
611 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK
612 || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
613 && token == TokenNameSEMICOLON)) {
614 newLine(pendingNewLines);
617 if (((previousCompilableToken == TokenNameSEMICOLON)
618 || (previousCompilableToken == TokenNameLBRACE)
619 || (previousCompilableToken == TokenNameRBRACE)
620 || (isComment(previousToken)))
621 && (token == TokenNameRBRACE)) {
622 indentationOffset = -1;
623 indentationLevel += popExclusiveUntilBlock();
625 if (previousToken == Scanner.TokenNameCOMMENT_LINE
628 currentLineIndentationLevel++;
630 currentLineIndentationLevel =
631 indentationLevel + indentationOffset;
633 pendingSpace = false;
634 indentationOffset = 0;
637 newLinesInWhitespace = 0;
640 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
646 // case TokenNamefinally :
647 expectingOpenBrace = true;
648 pendingNewlineAfterParen = true;
649 indentationLevel += pushControlStatement(token);
652 case TokenNamedefault :
653 if (tokenBeforeColonCount == tokenBeforeColon.length) {
657 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
659 tokenBeforeColonCount);
661 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
662 indentationLevel += pushControlStatement(TokenNamecase);
664 case TokenNameQUESTION :
665 if (tokenBeforeColonCount == tokenBeforeColon.length) {
669 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
671 tokenBeforeColonCount);
673 tokenBeforeColon[tokenBeforeColonCount++] = token;
675 case TokenNameswitch :
678 case TokenNamewhile :
679 if (openParenthesisCount == openParenthesis.length) {
683 (openParenthesis = new int[openParenthesisCount * 2]),
685 openParenthesisCount);
687 openParenthesis[openParenthesisCount++] = 0;
688 expectingOpenBrace = true;
690 indentationLevel += pushControlStatement(token);
692 // case TokenNametry :
693 // pendingNewlineAfterParen = true;
694 // case TokenNamecatch :
695 // // several CATCH statements can be contiguous.
696 // // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
697 // // as CATCH and TRY are the same token in the stack).
698 // expectingOpenBrace = true;
699 // indentationLevel += pushControlStatement(TokenNamecatch);
703 expectingOpenBrace = true;
704 indentationLevel += pushControlStatement(token);
709 case TokenNameLPAREN :
710 // if (previousToken == TokenNamesynchronized) {
711 // indentationLevel += pushControlStatement(previousToken);
713 // Put a space between the previous and current token if the
714 // previous token was not a keyword, open paren, logical
715 // compliment (eg: !), semi-colon, open brace, close brace,
717 if (previousCompilableToken != TokenNameLBRACKET
718 && previousToken != TokenNameIdentifier
719 && previousToken != 0
720 && previousToken != TokenNameNOT
721 && previousToken != TokenNameLPAREN
722 && previousToken != TokenNameTWIDDLE
723 && previousToken != TokenNameSEMICOLON
724 && previousToken != TokenNameLBRACE
725 && previousToken != TokenNameRBRACE) {
726 // && previousToken != TokenNamesuper
727 // && previousToken != TokenNamethis) {
730 // If in a for/if/while statement, increase the parenthesis count
731 // for the current openParenthesisCount
732 // else increase the count for stand alone parenthesis.
733 if (openParenthesisCount > 0)
734 openParenthesis[openParenthesisCount - 1]++;
736 openParenthesis[0]++;
738 pendingSpace = false;
741 case TokenNameRPAREN :
743 // Decrease the parenthesis count
744 // if there is no more unclosed parenthesis,
745 // a new line and indent may be append (depending on the next token).
746 if ((openParenthesisCount > 1)
747 && (openParenthesis[openParenthesisCount - 1] > 0)) {
748 openParenthesis[openParenthesisCount - 1]--;
749 if (openParenthesis[openParenthesisCount - 1] <= 0) {
750 pendingNewlineAfterParen = true;
751 inAssignment = false;
752 openParenthesisCount--;
755 openParenthesis[0]--;
757 pendingSpace = false;
759 case TokenNameLBRACE :
760 if ((previousCompilableToken == TokenNameRBRACKET)
761 || (previousCompilableToken == TokenNameEQUAL)) {
762 // if (previousCompilableToken == TokenNameRBRACKET) {
763 inArrayAssignment = true;
764 inAssignment = false;
766 if (inArrayAssignment) {
767 indentationLevel += pushBlock();
769 // Add new line and increase indentation level after open brace.
771 indentationLevel += pushBlock();
774 case TokenNameRBRACE :
775 if (previousCompilableToken == TokenNameRPAREN) {
776 pendingSpace = false;
778 if (inArrayAssignment) {
779 inArrayAssignment = false;
781 indentationLevel += popInclusiveUntilBlock();
784 indentationLevel += popInclusiveUntilBlock();
786 if (previousCompilableToken == TokenNameRPAREN) {
787 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
788 currentLineBuffer.append(options.lineSeparatorSequence);
789 increaseLineDelta(options.lineSeparatorSequence.length);
791 if (constructionsCount > 0) {
792 switch (constructions[constructionsCount - 1]) {
794 //indentationLevel += popExclusiveUntilBlock();
796 case TokenNameswitch :
799 // case TokenNametry :
800 // case TokenNamecatch :
801 // case TokenNamefinally :
802 case TokenNamewhile :
804 // case TokenNamesynchronized :
805 clearNonBlockIndents = true;
812 case TokenNameLBRACKET :
814 pendingSpace = false;
816 case TokenNameRBRACKET :
817 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
818 // if there is no left bracket to close, the right bracket is ignored.
819 pendingSpace = false;
821 case TokenNameCOMMA :
823 pendingSpace = false;
825 case TokenNameSEMICOLON :
827 // Do not generate line terminators in the definition of
828 // the for statement.
829 // if not in this case, jump a line and reduce indentation after the brace
830 // if the block it closes belongs to a conditional statement (if, while, do...).
831 if (openParenthesisCount <= 1) {
833 if (expectingOpenBrace) {
834 clearNonBlockIndents = true;
835 expectingOpenBrace = false;
838 inAssignment = false;
839 pendingSpace = false;
841 case TokenNamePLUS_PLUS :
842 case TokenNameMINUS_MINUS :
844 // Do not put a space between a post-increment/decrement
845 // and the identifier being modified.
846 if (previousToken == TokenNameIdentifier
847 || previousToken == TokenNameRBRACKET) {
848 pendingSpace = false;
851 case TokenNamePLUS : // previously ADDITION
852 case TokenNameMINUS :
854 // Handle the unary operators plus and minus via a flag
855 if (!isLiteralToken(previousToken)
856 && previousToken != TokenNameIdentifier
857 && previousToken != TokenNameRPAREN
858 && previousToken != TokenNameRBRACKET) {
859 unarySignModifier = 1;
862 case TokenNameCOLON :
863 // In a switch/case statement, add a newline & indent
864 // when a colon is encountered.
865 if (tokenBeforeColonCount > 0) {
866 if (tokenBeforeColon[tokenBeforeColonCount - 1]
870 tokenBeforeColonCount--;
873 case TokenNameEQUAL :
876 case Scanner.TokenNameCOMMENT_LINE :
879 currentLineIndentationLevel++;
881 break; // a line is always inserted after a one-line comment
882 case Scanner.TokenNameCOMMENT_PHPDOC :
883 case Scanner.TokenNameCOMMENT_BLOCK :
884 currentCommentOffset = getCurrentCommentOffset();
887 case Scanner.TokenNameWHITESPACE :
889 // Count the number of line terminators in the whitespace so
890 // line spacing can be preserved near comments.
891 char[] source = scanner.source;
892 newLinesInWhitespace = 0;
893 for (int i = scanner.startPosition, max = scanner.currentPosition;
896 if (source[i] == '\r') {
898 if (source[++i] == '\n') {
899 newLinesInWhitespace++;
901 newLinesInWhitespace++;
904 newLinesInWhitespace++;
906 } else if (source[i] == '\n') {
907 newLinesInWhitespace++;
910 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
913 if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
914 // || token == TokenNamesuper
915 // || token == TokenNamethis) {
917 // Do not put a space between a unary operator
918 // (eg: ++, --, +, -) and the identifier being modified.
919 if (previousToken == TokenNamePLUS_PLUS
920 || previousToken == TokenNameMINUS_MINUS
921 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
922 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
923 pendingSpace = false;
925 unarySignModifier = 0;
929 // Do not output whitespace tokens.
930 if (token != Scanner.TokenNameWHITESPACE) {
932 /* Add pending space to the formatted source string.
933 Do not output a space under the following circumstances:
934 1) this is the first pass
935 2) previous token is an open paren
936 3) previous token is a period
937 4) previous token is the logical compliment (eg: !)
938 5) previous token is the bitwise compliment (eg: ~)
939 6) previous token is the open bracket (eg: [)
940 7) in an assignment statement, if the previous token is an
941 open brace or the current token is a close brace
942 8) previous token is a single line comment
944 boolean openAndCloseBrace =
945 previousCompilableToken == TokenNameLBRACE
946 && token == TokenNameRBRACE;
949 && insertSpaceAfter(previousToken)
951 && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
952 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
953 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
954 && !openAndCloseBrace)
957 // Add the next token to the formatted source string.
958 outputCurrentToken(token);
959 if (token == Scanner.TokenNameCOMMENT_LINE
960 && openParenthesisCount > 1) {
962 currentLineBuffer.append(options.lineSeparatorSequence);
963 increaseLineDelta(options.lineSeparatorSequence.length);
967 // Whitespace tokens do not need to be remembered.
968 if (token != Scanner.TokenNameWHITESPACE) {
969 previousToken = token;
970 if (token != Scanner.TokenNameCOMMENT_BLOCK
971 && token != Scanner.TokenNameCOMMENT_LINE
972 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
973 previousCompilableToken = token;
977 output(copyRemainingSource());
979 // dump the last token of the source in the formatted output.
980 } catch (InvalidInputException e) {
981 output(copyRemainingSource());
983 // dump the last token of the source in the formatted output.
988 * Formats the char array <code>sourceString</code>,
989 * and returns a string containing the formatted version.
990 * @return the formatted ouput.
992 public String formatSourceString(String sourceString) {
993 char[] sourceChars = sourceString.toCharArray();
994 formattedSource = new StringBuffer(sourceChars.length);
995 scanner.setSource(sourceChars);
997 return formattedSource.toString();
1001 * Formats the char array <code>sourceString</code>,
1002 * and returns a string containing the formatted version.
1003 * @param string the string to format
1004 * @param indentationLevel the initial indentation level
1005 * @return the formatted ouput.
1007 public String format(String string, int indentationLevel) {
1008 return format(string, indentationLevel, (int[]) null);
1012 * Formats the char array <code>sourceString</code>,
1013 * and returns a string containing the formatted version.
1014 * The positions array is modified to contain the mapped positions.
1015 * @param string the string to format
1016 * @param indentationLevel the initial indentation level
1017 * @param positions the array of positions to map
1018 * @return the formatted ouput.
1020 public String format(String string, int indentationLevel, int[] positions) {
1021 return this.format(string, indentationLevel, positions, null);
1024 public String format(
1026 int indentationLevel,
1028 String lineSeparator) {
1029 if (lineSeparator != null) {
1030 this.options.setLineSeparator(lineSeparator);
1032 if (positions != null) {
1033 this.setPositionsToMap(positions);
1034 this.setInitialIndentationLevel(indentationLevel);
1035 String formattedString = this.formatSourceString(string);
1036 int[] mappedPositions = this.getMappedPositions();
1037 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1038 return formattedString;
1040 this.setInitialIndentationLevel(indentationLevel);
1041 return this.formatSourceString(string);
1045 * Formats the char array <code>sourceString</code>,
1046 * and returns a string containing the formatted version. The initial indentation level is 0.
1047 * @param string the string to format
1048 * @return the formatted ouput.
1050 public String format(String string) {
1051 return this.format(string, 0, (int[]) null);
1055 * Formats a given source string, starting indenting it at a particular
1056 * depth and using the given options
1058 * @deprecated backport 1.0 internal functionality
1060 public static String format(
1061 String sourceString,
1062 int initialIndentationLevel,
1063 ConfigurableOption[] options) {
1064 CodeFormatter formatter = new CodeFormatter(options);
1065 formatter.setInitialIndentationLevel(initialIndentationLevel);
1066 return formatter.formatSourceString(sourceString);
1070 * Returns the number of characters and tab char between the beginning of the line
1071 * and the beginning of the comment.
1073 private int getCurrentCommentOffset() {
1074 int linePtr = scanner.linePtr;
1075 // if there is no beginning of line, return 0.
1079 int beginningOfLine = scanner.lineEnds[linePtr];
1080 int currentStartPosition = scanner.startPosition;
1081 char[] source = scanner.source;
1083 // find the position of the beginning of the line containing the comment
1084 while (beginningOfLine > currentStartPosition) {
1086 beginningOfLine = scanner.lineEnds[--linePtr];
1088 beginningOfLine = 0;
1092 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1093 char currentCharacter = source[i];
1094 switch (currentCharacter) {
1096 offset += options.tabSize;
1112 * Returns an array of descriptions for the configurable options.
1113 * The descriptions may be changed and passed back to a different
1116 * @deprecated backport 1.0 internal functionality
1118 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1119 String componentName = CodeFormatter.class.getName();
1120 FormatterOptions options = new FormatterOptions();
1121 return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
1122 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
1123 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
1124 new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1125 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
1126 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
1127 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
1128 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
1129 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1134 * Returns the array of mapped positions.
1135 * Returns null is no positions have been set.
1137 * @deprecated There is no need to retrieve the mapped positions anymore.
1139 public int[] getMappedPositions() {
1140 return mappedPositions;
1144 * Returns the priority of the token given as argument<br>
1145 * The most prioritary the token is, the smallest the return value is.
1146 * @return the priority of <code>token</code>
1147 * @param token the token of which the priority is requested
1149 private static int getTokenPriority(int token) {
1151 case TokenNameextends :
1152 // case TokenNameimplements :
1153 // case TokenNamethrows :
1155 case TokenNameSEMICOLON : // ;
1157 case TokenNameCOMMA : // ,
1159 case TokenNameEQUAL : // =
1161 case TokenNameAND_AND : // &&
1162 case TokenNameOR_OR : // ||
1164 case TokenNameQUESTION : // ?
1165 case TokenNameCOLON : // :
1166 return 50; // it's better cutting on ?: than on ;
1167 case TokenNameEQUAL_EQUAL : // ==
1168 case TokenNameNOT_EQUAL : // !=
1170 case TokenNameLESS : // <
1171 case TokenNameLESS_EQUAL : // <=
1172 case TokenNameGREATER : // >
1173 case TokenNameGREATER_EQUAL : // >=
1174 // case TokenNameinstanceof : // instanceof
1176 case TokenNamePLUS : // +
1177 case TokenNameMINUS : // -
1179 case TokenNameMULTIPLY : // *
1180 case TokenNameDIVIDE : // /
1181 case TokenNameREMAINDER : // %
1183 case TokenNameLEFT_SHIFT : // <<
1184 case TokenNameRIGHT_SHIFT : // >>
1185 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1187 case TokenNameAND : // &
1188 case TokenNameOR : // |
1189 case TokenNameXOR : // ^
1191 case TokenNameMULTIPLY_EQUAL : // *=
1192 case TokenNameDIVIDE_EQUAL : // /=
1193 case TokenNameREMAINDER_EQUAL : // %=
1194 case TokenNamePLUS_EQUAL : // +=
1195 case TokenNameMINUS_EQUAL : // -=
1196 case TokenNameLEFT_SHIFT_EQUAL : // <<=
1197 case TokenNameRIGHT_SHIFT_EQUAL : // >>=
1198 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1199 case TokenNameAND_EQUAL : // &=
1200 case TokenNameXOR_EQUAL : // ^=
1201 case TokenNameOR_EQUAL : // |=
1203 case TokenNameDOT : // .
1206 return Integer.MAX_VALUE;
1211 * Handles the exception raised when an invalid token is encountered.
1212 * Returns true if the exception has been handled, false otherwise.
1214 private boolean handleInvalidToken(Exception e) {
1215 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1216 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1217 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1223 private final void increaseGlobalDelta(int offset) {
1224 globalDelta += offset;
1227 private final void increaseLineDelta(int offset) {
1228 lineDelta += offset;
1231 private final void increaseSplitDelta(int offset) {
1232 splitDelta += offset;
1236 * Returns true if a space has to be inserted after <code>operator</code>
1239 private boolean insertSpaceAfter(int token) {
1241 case TokenNameLPAREN :
1243 case TokenNameTWIDDLE :
1245 case 0 : // no token
1246 case TokenNameLBRACKET :
1247 case Scanner.TokenNameCOMMENT_LINE :
1255 * Returns true if a space has to be inserted before <code>operator</code>
1256 * false otherwise.<br>
1257 * Cannot be static as it uses the code formatter options
1258 * (to know if the compact assignment mode is on).
1260 private boolean insertSpaceBefore(int token) {
1262 case TokenNameEQUAL :
1263 return (!options.compactAssignmentMode);
1269 private static boolean isComment(int token) {
1271 token == Scanner.TokenNameCOMMENT_BLOCK
1272 || token == Scanner.TokenNameCOMMENT_LINE
1273 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1277 private static boolean isLiteralToken(int token) {
1278 boolean result = token == TokenNameIntegerLiteral
1279 // || token == TokenNameLongLiteral
1280 // || token == TokenNameFloatingPointLiteral
1281 || token == TokenNameDoubleLiteral
1282 // || token == TokenNameCharacterLiteral
1283 || token == TokenNameStringLiteral;
1288 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1289 * it is split and the result is dumped in <code>formattedSource</code>
1290 * @param newLineCount the number of new lines to append
1292 private void newLine(int newLineCount) {
1294 // format current line
1296 beginningOfLineIndex = formattedSource.length();
1297 String currentLine = currentLineBuffer.toString();
1298 if (containsOpenCloseBraces) {
1299 containsOpenCloseBraces = false;
1303 indentationLevelForOpenCloseBraces,
1308 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1313 currentLineIndentationLevel,
1319 // dump line break(s)
1320 for (int i = 0; i < newLineCount; i++) {
1321 formattedSource.append(options.lineSeparatorSequence);
1322 increaseSplitDelta(options.lineSeparatorSequence.length);
1324 // reset formatter for next line
1325 int currentLength = currentLine.length();
1328 currentLength > maxLineSize ? maxLineSize =
1329 currentLength : maxLineSize);
1331 increaseGlobalDelta(splitDelta);
1332 increaseGlobalDelta(lineDelta);
1334 currentLineIndentationLevel = initialIndentationLevel;
1337 private String operatorString(int operator) {
1339 case TokenNameextends :
1340 return "extends"; //$NON-NLS-1$
1342 // case TokenNameimplements :
1343 // return "implements"; //$NON-NLS-1$
1345 // case TokenNamethrows :
1346 // return "throws"; //$NON-NLS-1$
1348 case TokenNameSEMICOLON : // ;
1349 return ";"; //$NON-NLS-1$
1351 case TokenNameCOMMA : // ,
1352 return ","; //$NON-NLS-1$
1354 case TokenNameEQUAL : // =
1355 return "="; //$NON-NLS-1$
1357 case TokenNameAND_AND : // && (15.22)
1358 return "&&"; //$NON-NLS-1$
1360 case TokenNameOR_OR : // || (15.23)
1361 return "||"; //$NON-NLS-1$
1363 case TokenNameQUESTION : // ? (15.24)
1364 return "?"; //$NON-NLS-1$
1366 case TokenNameCOLON : // : (15.24)
1367 return ":"; //$NON-NLS-1$
1369 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1370 return "=="; //$NON-NLS-1$
1372 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1373 return "!="; //$NON-NLS-1$
1375 case TokenNameLESS : // < (15.19.1)
1376 return "<"; //$NON-NLS-1$
1378 case TokenNameLESS_EQUAL : // <= (15.19.1)
1379 return "<="; //$NON-NLS-1$
1381 case TokenNameGREATER : // > (15.19.1)
1382 return ">"; //$NON-NLS-1$
1384 case TokenNameGREATER_EQUAL : // >= (15.19.1)
1385 return ">="; //$NON-NLS-1$
1387 // case TokenNameinstanceof : // instanceof
1388 // return "instanceof"; //$NON-NLS-1$
1390 case TokenNamePLUS : // + (15.17, 15.17.2)
1391 return "+"; //$NON-NLS-1$
1393 case TokenNameMINUS : // - (15.17.2)
1394 return "-"; //$NON-NLS-1$
1396 case TokenNameMULTIPLY : // * (15.16.1)
1397 return "*"; //$NON-NLS-1$
1399 case TokenNameDIVIDE : // / (15.16.2)
1400 return "/"; //$NON-NLS-1$
1402 case TokenNameREMAINDER : // % (15.16.3)
1403 return "%"; //$NON-NLS-1$
1405 case TokenNameLEFT_SHIFT : // << (15.18)
1406 return "<<"; //$NON-NLS-1$
1408 case TokenNameRIGHT_SHIFT : // >> (15.18)
1409 return ">>"; //$NON-NLS-1$
1411 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1412 // return ">>>"; //$NON-NLS-1$
1414 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
1415 return "&"; //$NON-NLS-1$
1417 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
1418 return "|"; //$NON-NLS-1$
1420 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
1421 return "^"; //$NON-NLS-1$
1423 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
1424 return "*="; //$NON-NLS-1$
1426 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
1427 return "/="; //$NON-NLS-1$
1429 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
1430 return "%="; //$NON-NLS-1$
1432 case TokenNamePLUS_EQUAL : // += (15.25.2)
1433 return "+="; //$NON-NLS-1$
1435 case TokenNameMINUS_EQUAL : // -= (15.25.2)
1436 return "-="; //$NON-NLS-1$
1438 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
1439 return "<<="; //$NON-NLS-1$
1441 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
1442 return ">>="; //$NON-NLS-1$
1444 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1445 // return ">>>="; //$NON-NLS-1$
1447 case TokenNameAND_EQUAL : // &= (15.25.2)
1448 return "&="; //$NON-NLS-1$
1450 case TokenNameXOR_EQUAL : // ^= (15.25.2)
1451 return "^="; //$NON-NLS-1$
1453 case TokenNameOR_EQUAL : // |= (15.25.2)
1454 return "|="; //$NON-NLS-1$
1456 case TokenNameDOT : // .
1457 return "."; //$NON-NLS-1$
1460 return ""; //$NON-NLS-1$
1465 * Appends <code>stringToOutput</code> to the formatted output.<br>
1466 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1468 private void output(String stringToOutput) {
1469 char currentCharacter;
1470 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1471 currentCharacter = stringToOutput.charAt(i);
1472 if (currentCharacter != '\t') {
1473 currentLineBuffer.append(currentCharacter);
1479 * Appends <code>token</code> to the formatted output.<br>
1480 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1482 private void outputCurrentToken(int token) {
1483 char[] source = scanner.source;
1484 int startPosition = scanner.startPosition;
1487 case Scanner.TokenNameCOMMENT_PHPDOC :
1488 case Scanner.TokenNameCOMMENT_BLOCK :
1489 case Scanner.TokenNameCOMMENT_LINE :
1490 boolean endOfLine = false;
1491 int currentCommentOffset = getCurrentCommentOffset();
1492 int beginningOfLineSpaces = 0;
1494 currentCommentOffset = getCurrentCommentOffset();
1495 beginningOfLineSpaces = 0;
1496 boolean pendingCarriageReturn = false;
1497 for (int i = startPosition, max = scanner.currentPosition;
1500 char currentCharacter = source[i];
1501 updateMappedPositions(i);
1502 switch (currentCharacter) {
1504 pendingCarriageReturn = true;
1508 if (pendingCarriageReturn) {
1509 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1511 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1513 pendingCarriageReturn = false;
1514 currentLineBuffer.append(options.lineSeparatorSequence);
1515 beginningOfLineSpaces = 0;
1519 if (pendingCarriageReturn) {
1520 pendingCarriageReturn = false;
1521 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1522 currentLineBuffer.append(options.lineSeparatorSequence);
1523 beginningOfLineSpaces = 0;
1527 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1528 beginningOfLineSpaces += options.tabSize;
1529 if (beginningOfLineSpaces > currentCommentOffset) {
1530 currentLineBuffer.append(currentCharacter);
1532 increaseGlobalDelta(-1);
1535 currentLineBuffer.append(currentCharacter);
1539 if (pendingCarriageReturn) {
1540 pendingCarriageReturn = false;
1541 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1542 currentLineBuffer.append(options.lineSeparatorSequence);
1543 beginningOfLineSpaces = 0;
1547 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1548 beginningOfLineSpaces++;
1549 if (beginningOfLineSpaces > currentCommentOffset) {
1550 currentLineBuffer.append(currentCharacter);
1552 increaseGlobalDelta(-1);
1555 currentLineBuffer.append(currentCharacter);
1559 if (pendingCarriageReturn) {
1560 pendingCarriageReturn = false;
1561 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1562 currentLineBuffer.append(options.lineSeparatorSequence);
1563 beginningOfLineSpaces = 0;
1566 beginningOfLineSpaces = 0;
1567 currentLineBuffer.append(currentCharacter);
1572 updateMappedPositions(scanner.currentPosition - 1);
1573 multipleLineCommentCounter++;
1576 for (int i = startPosition, max = scanner.currentPosition;
1579 char currentCharacter = source[i];
1580 updateMappedPositions(i);
1581 currentLineBuffer.append(currentCharacter);
1587 * Outputs <code>currentString</code>:<br>
1588 * <ul><li>If its length is < maxLineLength, output
1589 * <li>Otherwise it is split.</ul>
1590 * @param currentString string to output
1591 * @param preIndented whether the string to output was pre-indented
1592 * @param depth number of indentation to put in front of <code>currentString</code>
1593 * @param operator value of the operator belonging to <code>currentString</code>.
1595 private void outputLine(
1596 String currentString,
1597 boolean preIndented,
1601 int[] startSubstringIndexes,
1602 int offsetInGlobalLine) {
1604 boolean emptyFirstSubString = false;
1605 String operatorString = operatorString(operator);
1606 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1607 boolean placeOperatorAhead = !placeOperatorBehind;
1609 // dump prefix operator?
1610 if (placeOperatorAhead) {
1615 if (operator != 0) {
1616 if (insertSpaceBefore(operator)) {
1617 formattedSource.append(' ');
1618 increaseSplitDelta(1);
1620 formattedSource.append(operatorString);
1621 increaseSplitDelta(operatorString.length());
1623 if (insertSpaceAfter(operator)
1624 // && operator != TokenNameimplements
1625 && operator != TokenNameextends) {
1626 // && operator != TokenNamethrows) {
1627 formattedSource.append(' ');
1628 increaseSplitDelta(1);
1632 SplitLine splitLine = null;
1633 if (options.maxLineLength == 0
1634 || getLength(currentString, depth) < options.maxLineLength
1635 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1637 // depending on the type of operator, outputs new line before of after dumping it
1638 // indent before postfix operator
1639 // indent also when the line cannot be split
1640 if (operator == TokenNameextends) {
1641 // || operator == TokenNameimplements
1642 // || operator == TokenNamethrows) {
1643 formattedSource.append(' ');
1644 increaseSplitDelta(1);
1646 if (placeOperatorBehind) {
1651 int max = currentString.length();
1652 if (multipleLineCommentCounter != 0) {
1654 BufferedReader reader =
1655 new BufferedReader(new StringReader(currentString));
1656 String line = reader.readLine();
1657 while (line != null) {
1658 updateMappedPositionsWhileSplitting(
1659 beginningOfLineIndex,
1660 beginningOfLineIndex
1662 + options.lineSeparatorSequence.length);
1663 formattedSource.append(line);
1664 beginningOfLineIndex = beginningOfLineIndex + line.length();
1665 if ((line = reader.readLine()) != null) {
1666 formattedSource.append(options.lineSeparatorSequence);
1667 beginningOfLineIndex += options.lineSeparatorSequence.length;
1668 dumpTab(currentLineIndentationLevel);
1672 } catch (IOException e) {
1673 e.printStackTrace();
1676 updateMappedPositionsWhileSplitting(
1677 beginningOfLineIndex,
1678 beginningOfLineIndex + max);
1679 for (int i = 0; i < max; i++) {
1680 char currentChar = currentString.charAt(i);
1681 switch (currentChar) {
1686 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
1687 // a substring cannot end with a lineSeparatorSequence,
1688 // except if it has been added by format() after a one-line comment
1689 formattedSource.append(options.lineSeparatorSequence);
1691 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1696 formattedSource.append(currentChar);
1700 // update positions inside the mappedPositions table
1701 if (substringIndex != -1) {
1702 if (multipleLineCommentCounter == 0) {
1704 beginningOfLineIndex + startSubstringIndexes[substringIndex];
1705 updateMappedPositionsWhileSplitting(
1707 startPosition + max);
1710 // compute the splitDelta resulting with the operator and blank removal
1711 if (substringIndex + 1 != startSubstringIndexes.length) {
1713 startSubstringIndexes[substringIndex]
1715 - startSubstringIndexes[substringIndex
1719 // dump postfix operator?
1720 if (placeOperatorBehind) {
1721 if (insertSpaceBefore(operator)) {
1722 formattedSource.append(' ');
1723 if (operator != 0) {
1724 increaseSplitDelta(1);
1727 formattedSource.append(operatorString);
1728 if (operator != 0) {
1729 increaseSplitDelta(operatorString.length());
1734 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1735 // extends has to stand alone on a line when currentString has been split.
1736 if (options.maxLineLength != 0
1737 && splitLine != null
1738 && (operator == TokenNameextends)) {
1739 // || operator == TokenNameimplements
1740 // || operator == TokenNamethrows)) {
1741 formattedSource.append(options.lineSeparatorSequence);
1742 increaseSplitDelta(options.lineSeparatorSequence.length);
1745 if (operator == TokenNameextends) {
1746 // || operator == TokenNameimplements
1747 // || operator == TokenNamethrows) {
1748 formattedSource.append(' ');
1749 increaseSplitDelta(1);
1752 // perform actual splitting
1753 String result[] = splitLine.substrings;
1754 int[] splitOperators = splitLine.operators;
1756 if (result[0].length() == 0) {
1757 // when the substring 0 is null, the substring 1 is correctly indented.
1759 emptyFirstSubString = true;
1761 // the operator going in front of the result[0] string is the operator parameter
1762 for (int i = 0, max = result.length; i < max; i++) {
1763 // the new depth is the current one if this is the first substring,
1764 // the current one + 1 otherwise.
1765 // if the substring is a comment, use the current indentation Level instead of the depth
1766 // (-1 because the ouputline increases depth).
1767 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
1768 String currentResult = result[i];
1770 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1771 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1772 || currentResult.startsWith("//")) //$NON-NLS-1$
1773 ? indentationLevel - 1 : depth;
1776 i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1777 i == 0 ? newDepth : newDepth + 1,
1780 splitLine.startSubstringsIndexes,
1781 currentString.indexOf(currentResult));
1783 formattedSource.append(options.lineSeparatorSequence);
1784 increaseSplitDelta(options.lineSeparatorSequence.length);
1788 if (result.length == splitOperators.length - 1) {
1789 int lastOperator = splitOperators[result.length];
1790 String lastOperatorString = operatorString(lastOperator);
1791 formattedSource.append(options.lineSeparatorSequence);
1792 increaseSplitDelta(options.lineSeparatorSequence.length);
1794 if (breakLineBeforeOperator(lastOperator)) {
1796 if (lastOperator != 0) {
1797 if (insertSpaceBefore(lastOperator)) {
1798 formattedSource.append(' ');
1799 increaseSplitDelta(1);
1801 formattedSource.append(lastOperatorString);
1802 increaseSplitDelta(lastOperatorString.length());
1804 if (insertSpaceAfter(lastOperator)
1805 // && lastOperator != TokenNameimplements
1806 && lastOperator != TokenNameextends) {
1807 // && lastOperator != TokenNamethrows) {
1808 formattedSource.append(' ');
1809 increaseSplitDelta(1);
1814 if (placeOperatorBehind) {
1815 if (insertSpaceBefore(operator)) {
1816 formattedSource.append(' ');
1817 increaseSplitDelta(1);
1819 formattedSource.append(operatorString);
1820 //increaseSplitDelta(operatorString.length());
1825 * Pops the top statement of the stack if it is <code>token</code>
1827 private int pop(int token) {
1829 if ((constructionsCount > 0)
1830 && (constructions[constructionsCount - 1] == token)) {
1832 constructionsCount--;
1838 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1840 private int popBlock() {
1842 if ((constructionsCount > 0)
1843 && ((constructions[constructionsCount - 1] == BLOCK)
1844 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1845 if (constructions[constructionsCount - 1] == BLOCK)
1847 constructionsCount--;
1853 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1854 * Does not remove <code>token</code> from the stack.
1855 * @param token the token to be left as the top of the stack
1857 private int popExclusiveUntil(int token) {
1859 int startCount = constructionsCount;
1860 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1861 if (constructions[i] != NONINDENT_BLOCK)
1863 constructionsCount--;
1869 * Pops elements until the stack is empty or the top element is
1870 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1871 * Does not remove it from the stack.
1873 private int popExclusiveUntilBlock() {
1874 int startCount = constructionsCount;
1876 for (int i = startCount - 1;
1878 && constructions[i] != BLOCK
1879 && constructions[i] != NONINDENT_BLOCK;
1881 constructionsCount--;
1888 * Pops elements until the stack is empty or the top element is
1889 * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1890 * Does not remove it from the stack.
1892 private int popExclusiveUntilBlockOrCase() {
1893 int startCount = constructionsCount;
1895 for (int i = startCount - 1;
1897 && constructions[i] != BLOCK
1898 && constructions[i] != NONINDENT_BLOCK
1899 && constructions[i] != TokenNamecase;
1901 constructionsCount--;
1908 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1909 * Removes <code>token</code> from the stack too.
1910 * @param token the token to remove from the stack
1912 private int popInclusiveUntil(int token) {
1913 int startCount = constructionsCount;
1915 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1916 if (constructions[i] != NONINDENT_BLOCK)
1918 constructionsCount--;
1920 if (constructionsCount > 0) {
1921 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1923 constructionsCount--;
1929 * Pops elements until the stack is empty or the top element is
1930 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1931 * Does not remove it from the stack.
1933 private int popInclusiveUntilBlock() {
1934 int startCount = constructionsCount;
1936 for (int i = startCount - 1;
1938 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK);
1941 constructionsCount--;
1943 if (constructionsCount > 0) {
1944 if (constructions[constructionsCount - 1] == BLOCK)
1946 constructionsCount--;
1952 * Pushes a block in the stack.<br>
1953 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
1954 * pushes <code>NONINDENT_BLOCK</code> otherwise.
1955 * Creates a new bigger array if the current one is full.
1957 private int pushBlock() {
1959 if (constructionsCount == constructions.length)
1963 (constructions = new int[constructionsCount * 2]),
1965 constructionsCount);
1967 if ((constructionsCount == 0)
1968 || (constructions[constructionsCount - 1] == BLOCK)
1969 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1970 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1972 constructions[constructionsCount++] = BLOCK;
1974 constructions[constructionsCount++] = NONINDENT_BLOCK;
1980 * Pushes <code>token</code>.<br>
1981 * Creates a new bigger array if the current one is full.
1983 private int pushControlStatement(int token) {
1984 if (constructionsCount == constructions.length)
1988 (constructions = new int[constructionsCount * 2]),
1990 constructionsCount);
1991 constructions[constructionsCount++] = token;
1995 private static boolean separateFirstArgumentOn(int currentToken) {
1996 //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
1997 return currentToken != TokenNameif
1998 && currentToken != TokenNameLPAREN
1999 && currentToken != TokenNameNOT
2000 && currentToken != TokenNamewhile
2001 && currentToken != TokenNamefor
2002 && currentToken != TokenNameswitch;
2006 * Set the positions to map. The mapped positions should be retrieved using the
2007 * getMappedPositions() method.
2008 * @param positions int[]
2009 * @deprecated Set the positions to map using the format(String, int, int[]) method.
2011 * @see #getMappedPositions()
2013 public void setPositionsToMap(int[] positions) {
2014 positionsToMap = positions;
2017 mappedPositions = new int[positions.length];
2021 * Appends a space character to the current line buffer.
2023 private void space() {
2024 currentLineBuffer.append(' ');
2025 increaseLineDelta(1);
2029 * Splits <code>stringToSplit</code> on the top level token<br>
2030 * If there are several identical token at the same level,
2031 * the string is cut into many pieces.
2032 * @return an object containing the operator and all the substrings
2033 * or null if the string cannot be split
2035 public SplitLine split(String stringToSplit) {
2036 return split(stringToSplit, 0);
2040 * Splits <code>stringToSplit</code> on the top level token<br>
2041 * If there are several identical token at the same level,
2042 * the string is cut into many pieces.
2043 * @return an object containing the operator and all the substrings
2044 * or null if the string cannot be split
2046 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2048 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2049 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2051 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2055 int currentToken = 0;
2056 int splitTokenType = 0;
2057 int splitTokenDepth = Integer.MAX_VALUE;
2058 int splitTokenPriority = Integer.MAX_VALUE;
2060 int[] substringsStartPositions = new int[10];
2061 // contains the start position of substrings
2062 int[] substringsEndPositions = new int[10];
2063 // contains the start position of substrings
2064 int substringsCount = 1; // index in the substringsStartPosition array
2065 int[] splitOperators = new int[10];
2066 // contains the start position of substrings
2067 int splitOperatorsCount = 0; // index in the substringsStartPosition array
2068 int[] openParenthesisPosition = new int[10];
2069 int openParenthesisPositionCount = 0;
2071 int lastOpenParenthesisPosition = -1;
2072 // used to remember the position of the 1st open parenthesis
2073 // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2074 // setup the scanner with a new source
2075 int lastCommentStartPosition = -1;
2076 // to remember the start position of the last comment
2077 int firstTokenOnLine = -1;
2078 // to remember the first token of the line
2079 int previousToken = -1;
2080 // to remember the previous token.
2081 splitScanner.setSource(stringToSplit.toCharArray());
2086 // takes the next token
2088 if (currentToken != Scanner.TokenNameWHITESPACE)
2089 previousToken = currentToken;
2090 currentToken = splitScanner.getNextToken();
2091 } catch (InvalidInputException e) {
2092 if (!handleInvalidToken(e))
2095 // this value is not modify when an exception is raised.
2097 if (currentToken == TokenNameEOF)
2100 if (firstTokenOnLine == -1) {
2101 firstTokenOnLine = currentToken;
2103 switch (currentToken) {
2104 case TokenNameRBRACE :
2105 case TokenNameRPAREN :
2106 if (openParenthesisPositionCount > 0) {
2107 if (openParenthesisPositionCount == 1
2108 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2109 lastOpenParenthesisPosition = openParenthesisPosition[0];
2111 (splitTokenDepth == Integer.MAX_VALUE)
2112 || (splitTokenDepth > openParenthesisPositionCount
2113 && openParenthesisPositionCount == 1)) {
2115 splitTokenDepth = openParenthesisPositionCount;
2116 splitTokenPriority = Integer.MAX_VALUE;
2117 substringsStartPositions[0] = 0;
2118 // better token means the whole line until now is the first substring
2119 substringsCount = 1; // resets the count of substrings
2120 substringsEndPositions[0] = openParenthesisPosition[0];
2121 // substring ends on operator start
2122 position = openParenthesisPosition[0];
2123 // the string mustn't be cut before the closing parenthesis but after the opening one.
2124 splitOperatorsCount = 1; // resets the count of split operators
2125 splitOperators[0] = 0;
2127 openParenthesisPositionCount--;
2130 case TokenNameLBRACE :
2131 case TokenNameLPAREN :
2132 if (openParenthesisPositionCount
2133 == openParenthesisPosition.length) {
2135 openParenthesisPosition,
2137 (openParenthesisPosition =
2138 new int[openParenthesisPositionCount * 2]),
2140 openParenthesisPositionCount);
2142 openParenthesisPosition[openParenthesisPositionCount++] =
2143 splitScanner.currentPosition;
2144 if (currentToken == TokenNameLPAREN
2145 && previousToken == TokenNameRPAREN) {
2146 openParenthesisPosition[openParenthesisPositionCount - 1] =
2147 splitScanner.startPosition;
2150 case TokenNameSEMICOLON : // ;
2151 case TokenNameCOMMA : // ,
2152 case TokenNameEQUAL : // =
2153 if (openParenthesisPositionCount < splitTokenDepth
2154 || (openParenthesisPositionCount == splitTokenDepth
2155 && splitTokenPriority > getTokenPriority(currentToken))) {
2156 // the current token is better than the one we currently have
2157 // (in level or in priority if same level)
2158 // reset the substringsCount
2159 splitTokenDepth = openParenthesisPositionCount;
2160 splitTokenType = currentToken;
2161 splitTokenPriority = getTokenPriority(currentToken);
2162 substringsStartPositions[0] = 0;
2163 // better token means the whole line until now is the first substring
2165 if (separateFirstArgumentOn(firstTokenOnLine)
2166 && openParenthesisPositionCount > 0) {
2167 substringsCount = 2; // resets the count of substrings
2169 substringsEndPositions[0] =
2170 openParenthesisPosition[splitTokenDepth - 1];
2171 substringsStartPositions[1] =
2172 openParenthesisPosition[splitTokenDepth - 1];
2173 substringsEndPositions[1] = splitScanner.startPosition;
2174 splitOperatorsCount = 2; // resets the count of split operators
2175 splitOperators[0] = 0;
2176 splitOperators[1] = currentToken;
2177 position = splitScanner.currentPosition;
2178 // next substring will start from operator end
2180 substringsCount = 1; // resets the count of substrings
2182 substringsEndPositions[0] = splitScanner.startPosition;
2183 // substring ends on operator start
2184 position = splitScanner.currentPosition;
2185 // next substring will start from operator end
2186 splitOperatorsCount = 1; // resets the count of split operators
2187 splitOperators[0] = currentToken;
2190 if ((openParenthesisPositionCount == splitTokenDepth
2191 && splitTokenPriority == getTokenPriority(currentToken))
2192 && splitTokenType != TokenNameEQUAL
2193 && currentToken != TokenNameEQUAL) {
2194 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
2195 // take only the 1st = into account.
2196 // if another token with the same priority is found,
2197 // push the start position of the substring and
2198 // push the token into the stack.
2199 // create a new array object if the current one is full.
2200 if (substringsCount == substringsStartPositions.length) {
2202 substringsStartPositions,
2204 (substringsStartPositions = new int[substringsCount * 2]),
2208 substringsEndPositions,
2210 (substringsEndPositions = new int[substringsCount * 2]),
2214 if (splitOperatorsCount == splitOperators.length) {
2218 (splitOperators = new int[splitOperatorsCount * 2]),
2220 splitOperatorsCount);
2222 substringsStartPositions[substringsCount] = position;
2223 substringsEndPositions[substringsCount++] =
2224 splitScanner.startPosition;
2225 // substring ends on operator start
2226 position = splitScanner.currentPosition;
2227 // next substring will start from operator end
2228 splitOperators[splitOperatorsCount++] = currentToken;
2233 case TokenNameCOLON : // : (15.24)
2234 // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
2235 // indeed it might appear also behind a case statement, and we do not to break at this point.
2236 if ((splitOperatorsCount == 0)
2237 || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2240 case TokenNameextends :
2241 // case TokenNameimplements :
2242 // case TokenNamethrows :
2244 case TokenNameDOT : // .
2245 case TokenNameMULTIPLY : // * (15.16.1)
2246 case TokenNameDIVIDE : // / (15.16.2)
2247 case TokenNameREMAINDER : // % (15.16.3)
2248 case TokenNamePLUS : // + (15.17, 15.17.2)
2249 case TokenNameMINUS : // - (15.17.2)
2250 case TokenNameLEFT_SHIFT : // << (15.18)
2251 case TokenNameRIGHT_SHIFT : // >> (15.18)
2252 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2253 case TokenNameLESS : // < (15.19.1)
2254 case TokenNameLESS_EQUAL : // <= (15.19.1)
2255 case TokenNameGREATER : // > (15.19.1)
2256 case TokenNameGREATER_EQUAL : // >= (15.19.1)
2257 // case TokenNameinstanceof : // instanceof
2258 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2259 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2260 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
2261 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
2262 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
2263 case TokenNameAND_AND : // && (15.22)
2264 case TokenNameOR_OR : // || (15.23)
2265 case TokenNameQUESTION : // ? (15.24)
2266 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
2267 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
2268 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
2269 case TokenNamePLUS_EQUAL : // += (15.25.2)
2270 case TokenNameMINUS_EQUAL : // -= (15.25.2)
2271 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
2272 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
2273 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2274 case TokenNameAND_EQUAL : // &= (15.25.2)
2275 case TokenNameXOR_EQUAL : // ^= (15.25.2)
2276 case TokenNameOR_EQUAL : // |= (15.25.2)
2278 if ((openParenthesisPositionCount < splitTokenDepth
2279 || (openParenthesisPositionCount == splitTokenDepth
2280 && splitTokenPriority > getTokenPriority(currentToken)))
2281 && !((currentToken == TokenNamePLUS
2282 || currentToken == TokenNameMINUS)
2283 && (previousToken == TokenNameLBRACE
2284 || previousToken == TokenNameLBRACKET
2285 || splitScanner.startPosition == 0))) {
2286 // the current token is better than the one we currently have
2287 // (in level or in priority if same level)
2288 // reset the substringsCount
2289 splitTokenDepth = openParenthesisPositionCount;
2290 splitTokenType = currentToken;
2291 splitTokenPriority = getTokenPriority(currentToken);
2292 substringsStartPositions[0] = 0;
2293 // better token means the whole line until now is the first substring
2295 if (separateFirstArgumentOn(firstTokenOnLine)
2296 && openParenthesisPositionCount > 0) {
2297 substringsCount = 2; // resets the count of substrings
2299 substringsEndPositions[0] =
2300 openParenthesisPosition[splitTokenDepth - 1];
2301 substringsStartPositions[1] =
2302 openParenthesisPosition[splitTokenDepth - 1];
2303 substringsEndPositions[1] = splitScanner.startPosition;
2304 splitOperatorsCount = 3; // resets the count of split operators
2305 splitOperators[0] = 0;
2306 splitOperators[1] = 0;
2307 splitOperators[2] = currentToken;
2308 position = splitScanner.currentPosition;
2309 // next substring will start from operator end
2311 substringsCount = 1; // resets the count of substrings
2313 substringsEndPositions[0] = splitScanner.startPosition;
2314 // substring ends on operator start
2315 position = splitScanner.currentPosition;
2316 // next substring will start from operator end
2317 splitOperatorsCount = 2; // resets the count of split operators
2318 splitOperators[0] = 0;
2319 // nothing for first operand since operator will be inserted in front of the second operand
2320 splitOperators[1] = currentToken;
2324 if (openParenthesisPositionCount == splitTokenDepth
2325 && splitTokenPriority == getTokenPriority(currentToken)) {
2326 // if another token with the same priority is found,
2327 // push the start position of the substring and
2328 // push the token into the stack.
2329 // create a new array object if the current one is full.
2330 if (substringsCount == substringsStartPositions.length) {
2332 substringsStartPositions,
2334 (substringsStartPositions = new int[substringsCount * 2]),
2338 substringsEndPositions,
2340 (substringsEndPositions = new int[substringsCount * 2]),
2344 if (splitOperatorsCount == splitOperators.length) {
2348 (splitOperators = new int[splitOperatorsCount * 2]),
2350 splitOperatorsCount);
2352 substringsStartPositions[substringsCount] = position;
2353 substringsEndPositions[substringsCount++] =
2354 splitScanner.startPosition;
2355 // substring ends on operator start
2356 position = splitScanner.currentPosition;
2357 // next substring will start from operator end
2358 splitOperators[splitOperatorsCount++] = currentToken;
2364 if (isComment(currentToken)) {
2365 lastCommentStartPosition = splitScanner.startPosition;
2367 lastCommentStartPosition = -1;
2370 } catch (InvalidInputException e) {
2373 // if the string cannot be split, return null.
2374 if (splitOperatorsCount == 0)
2377 // ## SPECIAL CASES BEGIN
2378 if (((splitOperatorsCount == 2
2379 && splitOperators[1] == TokenNameDOT
2380 && splitTokenDepth == 0
2381 && lastOpenParenthesisPosition > -1)
2382 || (splitOperatorsCount > 2
2383 && splitOperators[1] == TokenNameDOT
2384 && splitTokenDepth == 0
2385 && lastOpenParenthesisPosition > -1
2386 && lastOpenParenthesisPosition <= options.maxLineLength)
2387 || (separateFirstArgumentOn(firstTokenOnLine)
2388 && splitTokenDepth > 0
2389 && lastOpenParenthesisPosition > -1))
2390 && (lastOpenParenthesisPosition < splitScanner.source.length
2391 && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2392 // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
2393 // only one split on a top level .
2394 // or more than one split on . and substring before open parenthesis fits one line.
2395 // or split inside parenthesis and first token is not a for/while/if
2398 stringToSplit.substring(lastOpenParenthesisPosition),
2399 lastOpenParenthesisPosition);
2400 if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2401 // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
2402 return new SplitLine(
2405 stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2406 stringToSplit.substring(lastOpenParenthesisPosition)},
2409 lastOpenParenthesisPosition + offsetInGlobalLine });
2411 // right substring can be split and is split on comma
2412 // copy substrings and operators
2413 // except if the 1st string is empty.
2414 int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2415 int subStringsLength = sl.substrings.length + 1 - startIndex;
2416 String[] result = new String[subStringsLength];
2417 int[] startIndexes = new int[subStringsLength];
2418 int operatorsLength = sl.operators.length + 1 - startIndex;
2419 int[] operators = new int[operatorsLength];
2421 result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2425 sl.startSubstringsIndexes,
2429 subStringsLength - 1);
2430 for (int i = subStringsLength - 1; i >= 0; i--) {
2431 startIndexes[i] += offsetInGlobalLine;
2438 subStringsLength - 1);
2444 operatorsLength - 1);
2446 return new SplitLine(operators, result, startIndexes);
2449 // if the last token is a comment and the substring before the comment fits on a line,
2450 // split before the comment and return the result.
2451 if (lastCommentStartPosition > -1
2452 && lastCommentStartPosition < options.maxLineLength
2453 && splitTokenPriority > 50) {
2454 int end = lastCommentStartPosition;
2455 int start = lastCommentStartPosition;
2456 if (stringToSplit.charAt(end - 1) == ' ') {
2459 if (start != end && stringToSplit.charAt(start) == ' ') {
2462 return new SplitLine(
2465 stringToSplit.substring(0, end),
2466 stringToSplit.substring(start)},
2467 new int[] { 0, start });
2469 if (position != stringToSplit.length()) {
2470 if (substringsCount == substringsStartPositions.length) {
2472 substringsStartPositions,
2474 (substringsStartPositions = new int[substringsCount * 2]),
2478 substringsEndPositions,
2480 (substringsEndPositions = new int[substringsCount * 2]),
2484 // avoid empty extra substring, e.g. line terminated with a semi-colon
2485 substringsStartPositions[substringsCount] = position;
2486 substringsEndPositions[substringsCount++] = stringToSplit.length();
2488 if (splitOperatorsCount == splitOperators.length) {
2492 (splitOperators = new int[splitOperatorsCount * 2]),
2494 splitOperatorsCount);
2496 splitOperators[splitOperatorsCount] = 0;
2498 // the last element of the stack is the position of the end of StringToSPlit
2499 // +1 because the substring method excludes the last character
2500 String[] result = new String[substringsCount];
2501 for (int i = 0; i < substringsCount; i++) {
2502 int start = substringsStartPositions[i];
2503 int end = substringsEndPositions[i];
2504 if (stringToSplit.charAt(start) == ' ') {
2506 substringsStartPositions[i]++;
2508 if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2511 result[i] = stringToSplit.substring(start, end);
2512 substringsStartPositions[i] += offsetInGlobalLine;
2514 if (splitOperatorsCount > substringsCount) {
2516 substringsStartPositions,
2518 (substringsStartPositions = new int[splitOperatorsCount]),
2522 substringsEndPositions,
2524 (substringsEndPositions = new int[splitOperatorsCount]),
2527 for (int i = substringsCount; i < splitOperatorsCount; i++) {
2528 substringsStartPositions[i] = position;
2529 substringsEndPositions[i] = position;
2534 (splitOperators = new int[splitOperatorsCount]),
2536 splitOperatorsCount);
2539 substringsStartPositions,
2541 (substringsStartPositions = new int[substringsCount]),
2545 substringsEndPositions,
2547 (substringsEndPositions = new int[substringsCount]),
2553 (splitOperators = new int[substringsCount]),
2557 SplitLine splitLine =
2558 new SplitLine(splitOperators, result, substringsStartPositions);
2562 private void updateMappedPositions(int startPosition) {
2563 if (positionsToMap == null) {
2566 char[] source = scanner.source;
2567 int sourceLength = source.length;
2568 while (indexToMap < positionsToMap.length
2569 && positionsToMap[indexToMap] <= startPosition) {
2570 int posToMap = positionsToMap[indexToMap];
2572 || posToMap >= sourceLength) {
2573 // protection against out of bounds position
2574 if (posToMap == sourceLength) {
2575 mappedPositions[indexToMap] = formattedSource.length();
2577 indexToMap = positionsToMap.length; // no more mapping
2580 if (CharOperation.isWhitespace(source[posToMap])) {
2581 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2583 if (posToMap == sourceLength - 1) {
2584 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2586 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2593 private void updateMappedPositionsWhileSplitting(
2596 if (mappedPositions == null || mappedPositions.length == indexInMap)
2599 while (indexInMap < mappedPositions.length
2600 && startPosition <= mappedPositions[indexInMap]
2601 && mappedPositions[indexInMap] < endPosition
2602 && indexInMap < indexToMap) {
2603 mappedPositions[indexInMap] += splitDelta;
2608 private int getLength(String s, int tabDepth) {
2610 for (int i = 0; i < tabDepth; i++) {
2611 length += options.tabSize;
2613 for (int i = 0, max = s.length(); i < max; i++) {
2614 char currentChar = s.charAt(i);
2615 switch (currentChar) {
2617 length += options.tabSize;
2627 * Sets the initial indentation level
2628 * @param indentationLevel new indentation level
2632 public void setInitialIndentationLevel(int newIndentationLevel) {
2633 this.initialIndentationLevel =
2634 currentLineIndentationLevel = indentationLevel = newIndentationLevel;