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 org.phpeclipse.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 org.phpeclipse.phpdt.internal.formatter.impl.FormatterOptions;
28 import org.phpeclipse.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*/, true /*whitespace*/, false /*nls*/, false /*assert*/); // regular scanner for forming lines
156 scanner.recordLineSeparator = true;
158 // to remind of the position of the beginning of the line.
159 splitScanner = new Scanner(true /*comment*/, true /*whitespace*/, false /*nls*/, false /*assert*/);
160 // secondary scanner to split long lines formed by primary scanning
162 // initialize current line buffer
163 currentLineBuffer = new StringBuffer();
164 this.options = new FormatterOptions(settings);
168 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
171 private static boolean breakLineBeforeOperator(int operator) {
173 case TokenNameCOMMA :
174 case TokenNameSEMICOLON :
175 case TokenNameEQUAL :
183 * @deprecated backport 1.0 internal functionality
185 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
186 Hashtable options = new Hashtable(10);
188 for (int i = 0; i < settings.length; i++) {
189 if(settings[i].getComponentName().equals(CodeFormatter.class.getName())){
190 String optionName = settings[i].getOptionName();
191 int valueIndex = settings[i].getCurrentValueIndex();
193 if(optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
194 options.put("org.phpeclipse.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
196 } else if(optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
197 options.put("org.phpeclipse.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
199 } else if(optionName.equals("newline.clearAll")) { //$NON-NLS-1$
200 options.put("org.phpeclipse.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
202 } else if(optionName.equals("newline.elseIf")) { //$NON-NLS-1$
203 options.put("org.phpeclipse.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
205 } else if(optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
206 options.put("org.phpeclipse.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
208 } else if(optionName.equals("lineSplit")) { //$NON-NLS-1$
209 options.put("org.phpeclipse.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
211 } else if(optionName.equals("style.assignment")) { //$NON-NLS-1$
212 options.put("org.phpeclipse.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
214 } else if(optionName.equals("tabulation.char")) { //$NON-NLS-1$
215 options.put("org.phpeclipse.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
217 } else if(optionName.equals("tabulation.size")) { //$NON-NLS-1$
218 options.put("org.phpeclipse.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
227 * Returns the end of the source code.
229 private final String copyRemainingSource() {
230 char str[] = scanner.source;
231 int startPosition = scanner.startPosition;
232 int length = str.length - startPosition;
233 StringBuffer bufr = new StringBuffer(length);
234 if (startPosition < str.length) {
235 bufr.append(str, startPosition, length);
237 return (bufr.toString());
241 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
243 private void dumpTab(int tabCount) {
244 if (options.indentWithTab) {
245 for (int j = 0; j < tabCount; j++) {
246 formattedSource.append('\t');
247 increaseSplitDelta(1);
250 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
251 formattedSource.append(' ');
252 increaseSplitDelta(1);
258 * Dumps <code>currentLineBuffer</code> into the formatted string.
260 private void flushBuffer() {
261 String currentString = currentLineBuffer.toString();
263 beginningOfLineIndex = formattedSource.length();
264 if (containsOpenCloseBraces) {
265 containsOpenCloseBraces = false;
269 indentationLevelForOpenCloseBraces,
274 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
276 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
278 int scannerSourceLength = scanner.source.length;
279 if (scannerSourceLength > 2) {
280 if (scanner.source[scannerSourceLength - 1] == '\n' &&
281 scanner.source[scannerSourceLength - 2] == '\r') {
282 formattedSource.append(options.lineSeparatorSequence);
283 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
284 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
285 formattedSource.append(options.lineSeparatorSequence);
286 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
287 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
288 formattedSource.append(options.lineSeparatorSequence);
289 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
292 updateMappedPositions(scanner.startPosition);
296 * Formats the input string.
298 private void format() {
300 int previousToken = 0;
301 int previousCompilableToken = 0;
302 int indentationOffset = 0;
303 int newLinesInWhitespace = 0;
305 // number of new lines in the previous whitespace token
306 // (used to leave blank lines before comments)
307 int pendingNewLines = 0;
308 boolean expectingOpenBrace = false;
309 boolean clearNonBlockIndents = false;
310 // true if all indentations till the 1st { (usefull after } or ;)
311 boolean pendingSpace = true;
312 boolean pendingNewlineAfterParen = false;
313 // true when a cr is to be put after a ) (in conditional statements)
314 boolean inAssignment = false;
315 boolean inArrayAssignment = false;
316 boolean inThrowsClause = false;
317 boolean inClassOrInterfaceHeader = false;
319 // openBracketCount is used to count the number of open brackets not closed yet.
320 int openBracketCount = 0;
321 int unarySignModifier = 0;
323 // openParenthesis[0] is used to count the parenthesis not belonging to a condition
324 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
325 int openParenthesisCount = 1;
326 int[] openParenthesis = new int[10];
328 // tokenBeforeColon is used to know what token goes along with the current :
329 // it can be case or ?
330 int tokenBeforeColonCount = 0;
331 int[] tokenBeforeColon = new int[10];
333 constructionsCount = 0; // initializes the constructions count.
335 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
338 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
339 boolean specialElse = false;
341 // OPTION (IndentationLevel): initial indentation level may be non-zero.
342 currentLineIndentationLevel += constructionsCount;
344 // An InvalidInputException exception might cause the termination of this loop.
347 // Get the next token. Catch invalid input and output it
348 // with minimal formatting, also catch end of input and
351 token = scanner.getNextToken();
353 // Patch for line comment
354 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
355 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
356 int length = scanner.currentPosition;
357 loop: for (int index = length - 1; index >= 0; index--) {
358 switch(scanner.source[index]) {
361 scanner.currentPosition--;
368 } catch (InvalidInputException e) {
369 if (!handleInvalidToken(e)) {
374 if (token == Scanner.TokenNameEOF)
377 /* ## MODIFYING the indentation level before generating new lines
378 and indentation in the output string
381 // Removes all the indentations made by statements not followed by a block
382 // except if the current token is ELSE, CATCH or if we are in a switch/case
383 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
386 if (constructionsCount > 0
387 && constructions[constructionsCount - 1] == TokenNameelse) {
391 indentationLevel += popInclusiveUntil(TokenNameif);
393 // case TokenNamecatch :
394 // indentationLevel += popInclusiveUntil(TokenNamecatch);
396 // case TokenNamefinally :
397 // indentationLevel += popInclusiveUntil(TokenNamecatch);
399 case TokenNamewhile :
400 if (nlicsToken == TokenNamedo) {
401 indentationLevel += pop(TokenNamedo);
405 indentationLevel += popExclusiveUntilBlockOrCase();
406 // clear until a CASE, DEFAULT or BLOCK is encountered.
407 // Thus, the indentationLevel is correctly cleared either
408 // in a switch/case statement or in any other situation.
410 clearNonBlockIndents = false;
412 // returns to the indentation level created by the SWITCH keyword
413 // if the current token is a CASE or a DEFAULT
414 if (token == TokenNamecase || token == TokenNamedefault) {
415 indentationLevel += pop(TokenNamecase);
417 // if (token == Scanner.TokenNamethrows) {
418 // inThrowsClause = true;
420 if ((token == Scanner.TokenNameclass
421 // || token == Scanner.TokenNameinterface
422 ) && previousToken != Scanner.TokenNameDOT) {
423 inClassOrInterfaceHeader = true;
426 /* ## APPEND newlines and indentations to the output string
428 // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
429 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
430 if (pendingNewlineAfterParen
431 && previousCompilableToken == TokenNameelse
432 && token == TokenNameif
433 && options.compactElseIfMode) {
434 pendingNewlineAfterParen = false;
436 indentationLevel += pop(TokenNameelse);
437 // because else if is now one single statement,
438 // the indentation level after it is increased by one and not by 2
439 // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
441 // Add a newline & indent to the formatted source string if
442 // a for/if-else/while statement was scanned and there is no block
444 pendingNewlineAfterParen =
445 pendingNewlineAfterParen
446 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
447 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
448 pendingNewlineAfterParen = false;
450 // Do to add a newline & indent sequence if the current token is an
451 // open brace or a period or if the current token is a semi-colon and the
452 // previous token is a close paren.
453 // add a new line if a parenthesis belonging to a for() statement
454 // has been closed and the current token is not an opening brace
455 if (token != TokenNameLBRACE
456 && !isComment(token) // to avoid adding new line between else and a comment
457 && token != TokenNameDOT
458 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
460 currentLineIndentationLevel = indentationLevel;
462 pendingSpace = false;
464 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
466 if (constructionsCount > 0
467 && constructions[constructionsCount - 1] != BLOCK
468 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
469 currentLineIndentationLevel = indentationLevel - 1;
471 currentLineIndentationLevel = indentationLevel;
474 pendingSpace = false;
478 if (token == TokenNameLBRACE
479 && options.newLineBeforeOpeningBraceMode
480 && constructionsCount > 0
481 && constructions[constructionsCount - 1] == TokenNamedo) {
483 currentLineIndentationLevel = indentationLevel - 1;
485 pendingSpace = false;
488 if (token == TokenNameLBRACE && inThrowsClause) {
489 inThrowsClause = false;
490 if (options.newLineBeforeOpeningBraceMode) {
492 currentLineIndentationLevel = indentationLevel;
494 pendingSpace = false;
498 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
499 inClassOrInterfaceHeader = false;
500 if (options.newLineBeforeOpeningBraceMode) {
502 currentLineIndentationLevel = indentationLevel;
504 pendingSpace = false;
507 // Add pending new lines to the formatted source string.
508 // Note: pending new lines are not added if the current token
509 // is a single line comment or whitespace.
510 // if the comment is between parenthesis, there is no blank line preservation
511 // (if it's a one-line comment, a blank line is added after it).
512 if (((pendingNewLines > 0 && (!isComment(token)))
513 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
514 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
515 && token != Scanner.TokenNameWHITESPACE) {
517 // Do not add newline & indent between an adjoining close brace and
518 // close paren. Anonymous inner classes may use this form.
519 boolean closeBraceAndCloseParen =
520 previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
522 // OPTION (NewLineInCompoundStatement): do not add newline & indent
523 // between close brace and else, (do) while, catch, and finally if
524 // newlineInCompoundStatement is true.
525 boolean nlicsOption =
526 previousToken == TokenNameRBRACE
527 && !options.newlineInControlStatementMode
528 && (token == TokenNameelse
529 || (token == TokenNamewhile && nlicsToken == TokenNamedo));
530 // || token == TokenNamecatch
531 // || token == TokenNamefinally);
533 // Do not add a newline & indent between a close brace and semi-colon.
534 boolean semiColonAndCloseBrace =
535 previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
537 // Do not add a new line & indent between a multiline comment and a opening brace
538 boolean commentAndOpenBrace =
539 previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
541 // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
542 boolean commaAndCloseBrace =
543 previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
545 // Add a newline and indent, if appropriate.
547 || (!commentAndOpenBrace
548 && !closeBraceAndCloseParen
550 && !semiColonAndCloseBrace
551 && !commaAndCloseBrace)) {
553 // if clearAllBlankLinesMode=false, leaves the blank lines
554 // inserted by the user
555 // if clearAllBlankLinesMode=true, removes all of then
556 // and insert only blank lines required by the formatting.
557 if (!options.clearAllBlankLinesMode) {
558 // (isComment(token))
560 (pendingNewLines < newLinesInWhitespace)
561 ? newLinesInWhitespace
563 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
565 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
566 containsOpenCloseBraces = true;
567 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
568 if (isComment(previousToken)) {
569 newLine(pendingNewLines);
571 /* if (!(constructionsCount > 1
572 && constructions[constructionsCount-1] == NONINDENT_BLOCK
573 && (constructions[constructionsCount-2] == TokenNamefor
574 || constructions[constructionsCount-2] == TokenNamewhile))) {*/
575 if (options.newLineInEmptyBlockMode) {
576 if (inArrayAssignment) {
577 newLine(1); // array assigment with an empty block
579 newLine(pendingNewLines);
585 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
586 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK
587 || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
588 && token == TokenNameSEMICOLON)) {
589 newLine(pendingNewLines);
592 if (((previousCompilableToken == TokenNameSEMICOLON)
593 || (previousCompilableToken == TokenNameLBRACE)
594 || (previousCompilableToken == TokenNameRBRACE)
595 || (isComment(previousToken)))
596 && (token == TokenNameRBRACE)) {
597 indentationOffset = -1;
598 indentationLevel += popExclusiveUntilBlock();
600 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
602 currentLineIndentationLevel++;
604 currentLineIndentationLevel = indentationLevel + indentationOffset;
606 pendingSpace = false;
607 indentationOffset = 0;
610 newLinesInWhitespace = 0;
613 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
619 // case TokenNamefinally :
620 expectingOpenBrace = true;
621 pendingNewlineAfterParen = true;
622 indentationLevel += pushControlStatement(token);
625 case TokenNamedefault :
626 if (tokenBeforeColonCount == tokenBeforeColon.length) {
630 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
632 tokenBeforeColonCount);
634 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
635 indentationLevel += pushControlStatement(TokenNamecase);
637 case TokenNameQUESTION :
638 if (tokenBeforeColonCount == tokenBeforeColon.length) {
642 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
644 tokenBeforeColonCount);
646 tokenBeforeColon[tokenBeforeColonCount++] = token;
648 case TokenNameswitch :
651 case TokenNamewhile :
652 if (openParenthesisCount == openParenthesis.length) {
656 (openParenthesis = new int[openParenthesisCount * 2]),
658 openParenthesisCount);
660 openParenthesis[openParenthesisCount++] = 0;
661 expectingOpenBrace = true;
663 indentationLevel += pushControlStatement(token);
665 // case TokenNametry :
666 // pendingNewlineAfterParen = true;
667 // case TokenNamecatch :
668 // // several CATCH statements can be contiguous.
669 // // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
670 // // as CATCH and TRY are the same token in the stack).
671 // expectingOpenBrace = true;
672 // indentationLevel += pushControlStatement(TokenNamecatch);
676 expectingOpenBrace = true;
677 indentationLevel += pushControlStatement(token);
682 case TokenNameLPAREN :
683 // if (previousToken == TokenNamesynchronized) {
684 // indentationLevel += pushControlStatement(previousToken);
686 // Put a space between the previous and current token if the
687 // previous token was not a keyword, open paren, logical
688 // compliment (eg: !), semi-colon, open brace, close brace,
690 if (previousCompilableToken != TokenNameLBRACKET
691 && previousToken != TokenNameIdentifier
692 && previousToken != 0
693 && previousToken != TokenNameNOT
694 && previousToken != TokenNameLPAREN
695 && previousToken != TokenNameTWIDDLE
696 && previousToken != TokenNameSEMICOLON
697 && previousToken != TokenNameLBRACE
698 && previousToken != TokenNameRBRACE) {
699 // && previousToken != TokenNamesuper
700 // && previousToken != TokenNamethis) {
703 // If in a for/if/while statement, increase the parenthesis count
704 // for the current openParenthesisCount
705 // else increase the count for stand alone parenthesis.
706 if (openParenthesisCount > 0)
707 openParenthesis[openParenthesisCount - 1]++;
709 openParenthesis[0]++;
711 pendingSpace = false;
714 case TokenNameRPAREN :
716 // Decrease the parenthesis count
717 // if there is no more unclosed parenthesis,
718 // a new line and indent may be append (depending on the next token).
719 if ((openParenthesisCount > 1)
720 && (openParenthesis[openParenthesisCount - 1] > 0)) {
721 openParenthesis[openParenthesisCount - 1]--;
722 if (openParenthesis[openParenthesisCount - 1] <= 0) {
723 pendingNewlineAfterParen = true;
724 inAssignment = false;
725 openParenthesisCount--;
728 openParenthesis[0]--;
730 pendingSpace = false;
732 case TokenNameLBRACE :
733 if ((previousCompilableToken == TokenNameRBRACKET)
734 || (previousCompilableToken == TokenNameEQUAL)) {
735 // if (previousCompilableToken == TokenNameRBRACKET) {
736 inArrayAssignment = true;
737 inAssignment = false;
739 if (inArrayAssignment) {
740 indentationLevel += pushBlock();
742 // Add new line and increase indentation level after open brace.
744 indentationLevel += pushBlock();
747 case TokenNameRBRACE :
748 if (previousCompilableToken == TokenNameRPAREN) {
749 pendingSpace = false;
751 if (inArrayAssignment) {
752 inArrayAssignment = false;
754 indentationLevel += popInclusiveUntilBlock();
757 indentationLevel += popInclusiveUntilBlock();
759 if (previousCompilableToken == TokenNameRPAREN) {
760 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
761 currentLineBuffer.append(options.lineSeparatorSequence);
762 increaseLineDelta(options.lineSeparatorSequence.length);
764 if (constructionsCount > 0) {
765 switch (constructions[constructionsCount - 1]) {
767 //indentationLevel += popExclusiveUntilBlock();
769 case TokenNameswitch :
772 // case TokenNametry :
773 // case TokenNamecatch :
774 // case TokenNamefinally :
775 case TokenNamewhile :
777 // case TokenNamesynchronized :
778 clearNonBlockIndents = true;
785 case TokenNameLBRACKET :
787 pendingSpace = false;
789 case TokenNameRBRACKET :
790 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
791 // if there is no left bracket to close, the right bracket is ignored.
792 pendingSpace = false;
794 case TokenNameCOMMA :
796 pendingSpace = false;
798 case TokenNameSEMICOLON :
800 // Do not generate line terminators in the definition of
801 // the for statement.
802 // if not in this case, jump a line and reduce indentation after the brace
803 // if the block it closes belongs to a conditional statement (if, while, do...).
804 if (openParenthesisCount <= 1) {
806 if (expectingOpenBrace) {
807 clearNonBlockIndents = true;
808 expectingOpenBrace = false;
811 inAssignment = false;
812 pendingSpace = false;
814 case TokenNamePLUS_PLUS :
815 case TokenNameMINUS_MINUS :
817 // Do not put a space between a post-increment/decrement
818 // and the identifier being modified.
819 if (previousToken == TokenNameIdentifier
820 || previousToken == TokenNameRBRACKET) {
821 pendingSpace = false;
824 case TokenNamePLUS : // previously ADDITION
825 case TokenNameMINUS :
827 // Handle the unary operators plus and minus via a flag
828 if (!isLiteralToken(previousToken)
829 && previousToken != TokenNameIdentifier
830 && previousToken != TokenNameRPAREN
831 && previousToken != TokenNameRBRACKET) {
832 unarySignModifier = 1;
835 case TokenNameCOLON :
836 // In a switch/case statement, add a newline & indent
837 // when a colon is encountered.
838 if (tokenBeforeColonCount > 0) {
839 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
842 tokenBeforeColonCount--;
845 case TokenNameEQUAL :
848 case Scanner.TokenNameCOMMENT_LINE :
851 currentLineIndentationLevel++;
853 break; // a line is always inserted after a one-line comment
854 case Scanner.TokenNameCOMMENT_PHPDOC :
855 case Scanner.TokenNameCOMMENT_BLOCK :
856 currentCommentOffset = getCurrentCommentOffset();
859 case Scanner.TokenNameWHITESPACE :
861 // Count the number of line terminators in the whitespace so
862 // line spacing can be preserved near comments.
863 char[] source = scanner.source;
864 newLinesInWhitespace = 0;
865 for (int i = scanner.startPosition, max = scanner.currentPosition;
868 if (source[i] == '\r') {
870 if (source[++i] == '\n') {
871 newLinesInWhitespace++;
873 newLinesInWhitespace++;
876 newLinesInWhitespace++;
878 } else if (source[i] == '\n') {
879 newLinesInWhitespace++;
882 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
885 if ((token == TokenNameIdentifier)
886 || isLiteralToken(token)) {
887 // || token == TokenNamesuper
888 // || token == TokenNamethis) {
890 // Do not put a space between a unary operator
891 // (eg: ++, --, +, -) and the identifier being modified.
892 if (previousToken == TokenNamePLUS_PLUS
893 || previousToken == TokenNameMINUS_MINUS
894 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
895 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
896 pendingSpace = false;
898 unarySignModifier = 0;
902 // Do not output whitespace tokens.
903 if (token != Scanner.TokenNameWHITESPACE) {
905 /* Add pending space to the formatted source string.
906 Do not output a space under the following circumstances:
907 1) this is the first pass
908 2) previous token is an open paren
909 3) previous token is a period
910 4) previous token is the logical compliment (eg: !)
911 5) previous token is the bitwise compliment (eg: ~)
912 6) previous token is the open bracket (eg: [)
913 7) in an assignment statement, if the previous token is an
914 open brace or the current token is a close brace
915 8) previous token is a single line comment
917 boolean openAndCloseBrace =
918 previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
921 && insertSpaceAfter(previousToken)
923 && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
924 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
925 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
926 && !openAndCloseBrace)
929 // Add the next token to the formatted source string.
930 outputCurrentToken(token);
931 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
933 currentLineBuffer.append(options.lineSeparatorSequence);
934 increaseLineDelta(options.lineSeparatorSequence.length);
938 // Whitespace tokens do not need to be remembered.
939 if (token != Scanner.TokenNameWHITESPACE) {
940 previousToken = token;
941 if (token != Scanner.TokenNameCOMMENT_BLOCK
942 && token != Scanner.TokenNameCOMMENT_LINE
943 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
944 previousCompilableToken = token;
948 output(copyRemainingSource());
949 flushBuffer(); // dump the last token of the source in the formatted output.
950 } catch (InvalidInputException e) {
951 output(copyRemainingSource());
952 flushBuffer(); // dump the last token of the source in the formatted output.
957 * Formats the char array <code>sourceString</code>,
958 * and returns a string containing the formatted version.
959 * @return the formatted ouput.
961 public String formatSourceString(String sourceString) {
962 char[] sourceChars = sourceString.toCharArray();
963 formattedSource = new StringBuffer(sourceChars.length);
964 scanner.setSource(sourceChars);
966 return formattedSource.toString();
970 * Formats the char array <code>sourceString</code>,
971 * and returns a string containing the formatted version.
972 * @param string the string to format
973 * @param indentationLevel the initial indentation level
974 * @return the formatted ouput.
976 public String format(String string, int indentationLevel) {
977 return format(string, indentationLevel, (int[])null);
981 * Formats the char array <code>sourceString</code>,
982 * and returns a string containing the formatted version.
983 * The positions array is modified to contain the mapped positions.
984 * @param string the string to format
985 * @param indentationLevel the initial indentation level
986 * @param positions the array of positions to map
987 * @return the formatted ouput.
989 public String format(String string, int indentationLevel, int[] positions) {
990 return this.format(string, indentationLevel, positions, null);
993 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
994 if (lineSeparator != null){
995 this.options.setLineSeparator(lineSeparator);
997 if (positions != null) {
998 this.setPositionsToMap(positions);
999 this.setInitialIndentationLevel(indentationLevel);
1000 String formattedString = this.formatSourceString(string);
1001 int[] mappedPositions = this.getMappedPositions();
1002 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1003 return formattedString;
1005 this.setInitialIndentationLevel(indentationLevel);
1006 return this.formatSourceString(string);
1010 * Formats the char array <code>sourceString</code>,
1011 * and returns a string containing the formatted version. The initial indentation level is 0.
1012 * @param string the string to format
1013 * @return the formatted ouput.
1015 public String format(String string) {
1016 return this.format(string, 0, (int[])null);
1020 * Formats a given source string, starting indenting it at a particular
1021 * depth and using the given options
1023 * @deprecated backport 1.0 internal functionality
1025 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1026 CodeFormatter formatter = new CodeFormatter(options);
1027 formatter.setInitialIndentationLevel(initialIndentationLevel);
1028 return formatter.formatSourceString(sourceString);
1032 * Returns the number of characters and tab char between the beginning of the line
1033 * and the beginning of the comment.
1035 private int getCurrentCommentOffset() {
1036 int linePtr = scanner.linePtr;
1037 // if there is no beginning of line, return 0.
1041 int beginningOfLine = scanner.lineEnds[linePtr];
1042 int currentStartPosition = scanner.startPosition;
1043 char[] source = scanner.source;
1045 // find the position of the beginning of the line containing the comment
1046 while (beginningOfLine > currentStartPosition) {
1048 beginningOfLine = scanner.lineEnds[--linePtr];
1050 beginningOfLine = 0;
1054 for (int i = currentStartPosition - 1; i >= beginningOfLine ; i--) {
1055 char currentCharacter = source[i];
1056 switch (currentCharacter) {
1058 offset += options.tabSize;
1074 * Returns an array of descriptions for the configurable options.
1075 * The descriptions may be changed and passed back to a different
1078 * @deprecated backport 1.0 internal functionality
1080 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1081 String componentName = CodeFormatter.class.getName();
1082 FormatterOptions options = new FormatterOptions();
1083 return new ConfigurableOption[] {
1084 new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
1085 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
1086 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
1087 new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1088 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
1089 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),//$NON-NLS-1$
1090 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
1091 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
1092 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1097 * Returns the array of mapped positions.
1098 * Returns null is no positions have been set.
1100 * @deprecated There is no need to retrieve the mapped positions anymore.
1102 public int[] getMappedPositions() {
1103 return mappedPositions;
1107 * Returns the priority of the token given as argument<br>
1108 * The most prioritary the token is, the smallest the return value is.
1109 * @return the priority of <code>token</code>
1110 * @param token the token of which the priority is requested
1112 private static int getTokenPriority(int token) {
1114 case TokenNameextends :
1115 // case TokenNameimplements :
1116 // case TokenNamethrows :
1118 case TokenNameSEMICOLON : // ;
1120 case TokenNameCOMMA : // ,
1122 case TokenNameEQUAL : // =
1124 case TokenNameAND_AND : // &&
1125 case TokenNameOR_OR : // ||
1127 case TokenNameQUESTION : // ?
1128 case TokenNameCOLON : // :
1129 return 50; // it's better cutting on ?: than on ;
1130 case TokenNameEQUAL_EQUAL : // ==
1131 case TokenNameNOT_EQUAL : // !=
1133 case TokenNameLESS : // <
1134 case TokenNameLESS_EQUAL : // <=
1135 case TokenNameGREATER : // >
1136 case TokenNameGREATER_EQUAL : // >=
1137 // case TokenNameinstanceof : // instanceof
1139 case TokenNamePLUS : // +
1140 case TokenNameMINUS : // -
1142 case TokenNameMULTIPLY : // *
1143 case TokenNameDIVIDE : // /
1144 case TokenNameREMAINDER : // %
1146 case TokenNameLEFT_SHIFT : // <<
1147 case TokenNameRIGHT_SHIFT : // >>
1148 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1150 case TokenNameAND : // &
1151 case TokenNameOR : // |
1152 case TokenNameXOR : // ^
1154 case TokenNameMULTIPLY_EQUAL : // *=
1155 case TokenNameDIVIDE_EQUAL : // /=
1156 case TokenNameREMAINDER_EQUAL : // %=
1157 case TokenNamePLUS_EQUAL : // +=
1158 case TokenNameMINUS_EQUAL : // -=
1159 case TokenNameLEFT_SHIFT_EQUAL : // <<=
1160 case TokenNameRIGHT_SHIFT_EQUAL : // >>=
1161 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1162 case TokenNameAND_EQUAL : // &=
1163 case TokenNameXOR_EQUAL : // ^=
1164 case TokenNameOR_EQUAL : // |=
1166 case TokenNameDOT : // .
1169 return Integer.MAX_VALUE;
1174 * Handles the exception raised when an invalid token is encountered.
1175 * Returns true if the exception has been handled, false otherwise.
1177 private boolean handleInvalidToken(Exception e) {
1178 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1179 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1180 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1186 private final void increaseGlobalDelta(int offset) {
1187 globalDelta += offset;
1190 private final void increaseLineDelta(int offset) {
1191 lineDelta += offset;
1194 private final void increaseSplitDelta(int offset) {
1195 splitDelta += offset;
1199 * Returns true if a space has to be inserted after <code>operator</code>
1202 private boolean insertSpaceAfter(int token) {
1204 case TokenNameLPAREN :
1206 case TokenNameTWIDDLE :
1208 case 0 : // no token
1209 case TokenNameLBRACKET :
1210 case Scanner.TokenNameCOMMENT_LINE :
1218 * Returns true if a space has to be inserted before <code>operator</code>
1219 * false otherwise.<br>
1220 * Cannot be static as it uses the code formatter options
1221 * (to know if the compact assignment mode is on).
1223 private boolean insertSpaceBefore(int token) {
1225 case TokenNameEQUAL :
1226 return (!options.compactAssignmentMode);
1232 private static boolean isComment(int token) {
1234 token == Scanner.TokenNameCOMMENT_BLOCK
1235 || token == Scanner.TokenNameCOMMENT_LINE
1236 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1240 private static boolean isLiteralToken(int token) {
1242 token == TokenNameIntegerLiteral
1243 // || token == TokenNameLongLiteral
1244 // || token == TokenNameFloatingPointLiteral
1245 || token == TokenNameDoubleLiteral
1246 // || token == TokenNameCharacterLiteral
1247 || token == TokenNameStringLiteral;
1252 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1253 * it is split and the result is dumped in <code>formattedSource</code>
1254 * @param newLineCount the number of new lines to append
1256 private void newLine(int newLineCount) {
1258 // format current line
1260 beginningOfLineIndex = formattedSource.length();
1261 String currentLine = currentLineBuffer.toString();
1262 if (containsOpenCloseBraces) {
1263 containsOpenCloseBraces = false;
1267 indentationLevelForOpenCloseBraces,
1272 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1274 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1276 // dump line break(s)
1277 for (int i = 0; i < newLineCount; i++) {
1278 formattedSource.append(options.lineSeparatorSequence);
1279 increaseSplitDelta(options.lineSeparatorSequence.length);
1281 // reset formatter for next line
1282 int currentLength = currentLine.length();
1285 currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1287 increaseGlobalDelta(splitDelta);
1288 increaseGlobalDelta(lineDelta);
1290 currentLineIndentationLevel = initialIndentationLevel;
1293 private String operatorString(int operator) {
1295 case TokenNameextends :
1296 return "extends"; //$NON-NLS-1$
1298 // case TokenNameimplements :
1299 // return "implements"; //$NON-NLS-1$
1301 // case TokenNamethrows :
1302 // return "throws"; //$NON-NLS-1$
1304 case TokenNameSEMICOLON : // ;
1305 return ";"; //$NON-NLS-1$
1307 case TokenNameCOMMA : // ,
1308 return ","; //$NON-NLS-1$
1310 case TokenNameEQUAL : // =
1311 return "="; //$NON-NLS-1$
1313 case TokenNameAND_AND : // && (15.22)
1314 return "&&"; //$NON-NLS-1$
1316 case TokenNameOR_OR : // || (15.23)
1317 return "||"; //$NON-NLS-1$
1319 case TokenNameQUESTION : // ? (15.24)
1320 return "?"; //$NON-NLS-1$
1322 case TokenNameCOLON : // : (15.24)
1323 return ":"; //$NON-NLS-1$
1325 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1326 return "=="; //$NON-NLS-1$
1328 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1329 return "!="; //$NON-NLS-1$
1331 case TokenNameLESS : // < (15.19.1)
1332 return "<"; //$NON-NLS-1$
1334 case TokenNameLESS_EQUAL : // <= (15.19.1)
1335 return "<="; //$NON-NLS-1$
1337 case TokenNameGREATER : // > (15.19.1)
1338 return ">"; //$NON-NLS-1$
1340 case TokenNameGREATER_EQUAL : // >= (15.19.1)
1341 return ">="; //$NON-NLS-1$
1343 // case TokenNameinstanceof : // instanceof
1344 // return "instanceof"; //$NON-NLS-1$
1346 case TokenNamePLUS : // + (15.17, 15.17.2)
1347 return "+"; //$NON-NLS-1$
1349 case TokenNameMINUS : // - (15.17.2)
1350 return "-"; //$NON-NLS-1$
1352 case TokenNameMULTIPLY : // * (15.16.1)
1353 return "*"; //$NON-NLS-1$
1355 case TokenNameDIVIDE : // / (15.16.2)
1356 return "/"; //$NON-NLS-1$
1358 case TokenNameREMAINDER : // % (15.16.3)
1359 return "%"; //$NON-NLS-1$
1361 case TokenNameLEFT_SHIFT : // << (15.18)
1362 return "<<"; //$NON-NLS-1$
1364 case TokenNameRIGHT_SHIFT : // >> (15.18)
1365 return ">>"; //$NON-NLS-1$
1367 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1368 // return ">>>"; //$NON-NLS-1$
1370 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
1371 return "&"; //$NON-NLS-1$
1373 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
1374 return "|"; //$NON-NLS-1$
1376 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
1377 return "^"; //$NON-NLS-1$
1379 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
1380 return "*="; //$NON-NLS-1$
1382 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
1383 return "/="; //$NON-NLS-1$
1385 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
1386 return "%="; //$NON-NLS-1$
1388 case TokenNamePLUS_EQUAL : // += (15.25.2)
1389 return "+="; //$NON-NLS-1$
1391 case TokenNameMINUS_EQUAL : // -= (15.25.2)
1392 return "-="; //$NON-NLS-1$
1394 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
1395 return "<<="; //$NON-NLS-1$
1397 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
1398 return ">>="; //$NON-NLS-1$
1400 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1401 // return ">>>="; //$NON-NLS-1$
1403 case TokenNameAND_EQUAL : // &= (15.25.2)
1404 return "&="; //$NON-NLS-1$
1406 case TokenNameXOR_EQUAL : // ^= (15.25.2)
1407 return "^="; //$NON-NLS-1$
1409 case TokenNameOR_EQUAL : // |= (15.25.2)
1410 return "|="; //$NON-NLS-1$
1412 case TokenNameDOT : // .
1413 return "."; //$NON-NLS-1$
1416 return ""; //$NON-NLS-1$
1421 * Appends <code>stringToOutput</code> to the formatted output.<br>
1422 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1424 private void output(String stringToOutput) {
1425 char currentCharacter;
1426 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1427 currentCharacter = stringToOutput.charAt(i);
1428 if (currentCharacter != '\t') {
1429 currentLineBuffer.append(currentCharacter);
1435 * Appends <code>token</code> to the formatted output.<br>
1436 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1438 private void outputCurrentToken(int token) {
1439 char[] source = scanner.source;
1440 int startPosition = scanner.startPosition;
1443 case Scanner.TokenNameCOMMENT_PHPDOC :
1444 case Scanner.TokenNameCOMMENT_BLOCK :
1445 case Scanner.TokenNameCOMMENT_LINE :
1446 boolean endOfLine = false;
1447 int currentCommentOffset = getCurrentCommentOffset();
1448 int beginningOfLineSpaces = 0;
1450 currentCommentOffset = getCurrentCommentOffset();
1451 beginningOfLineSpaces = 0;
1452 boolean pendingCarriageReturn = false;
1453 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1454 char currentCharacter = source[i];
1455 updateMappedPositions(i);
1456 switch (currentCharacter) {
1458 pendingCarriageReturn = true;
1462 if (pendingCarriageReturn) {
1463 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1465 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1467 pendingCarriageReturn = false;
1468 currentLineBuffer.append(options.lineSeparatorSequence);
1469 beginningOfLineSpaces = 0;
1473 if (pendingCarriageReturn) {
1474 pendingCarriageReturn = false;
1475 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1476 currentLineBuffer.append(options.lineSeparatorSequence);
1477 beginningOfLineSpaces = 0;
1481 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1482 beginningOfLineSpaces += options.tabSize;
1483 if (beginningOfLineSpaces > currentCommentOffset) {
1484 currentLineBuffer.append(currentCharacter);
1486 increaseGlobalDelta(-1);
1489 currentLineBuffer.append(currentCharacter);
1493 if (pendingCarriageReturn) {
1494 pendingCarriageReturn = false;
1495 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1496 currentLineBuffer.append(options.lineSeparatorSequence);
1497 beginningOfLineSpaces = 0;
1501 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1502 beginningOfLineSpaces++;
1503 if (beginningOfLineSpaces > currentCommentOffset) {
1504 currentLineBuffer.append(currentCharacter);
1506 increaseGlobalDelta(-1);
1509 currentLineBuffer.append(currentCharacter);
1513 if (pendingCarriageReturn) {
1514 pendingCarriageReturn = false;
1515 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1516 currentLineBuffer.append(options.lineSeparatorSequence);
1517 beginningOfLineSpaces = 0;
1520 beginningOfLineSpaces = 0;
1521 currentLineBuffer.append(currentCharacter);
1526 updateMappedPositions(scanner.currentPosition - 1);
1527 multipleLineCommentCounter++;
1530 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1531 char currentCharacter = source[i];
1532 updateMappedPositions(i);
1533 currentLineBuffer.append(currentCharacter);
1539 * Outputs <code>currentString</code>:<br>
1540 * <ul><li>If its length is < maxLineLength, output
1541 * <li>Otherwise it is split.</ul>
1542 * @param currentString string to output
1543 * @param preIndented whether the string to output was pre-indented
1544 * @param depth number of indentation to put in front of <code>currentString</code>
1545 * @param operator value of the operator belonging to <code>currentString</code>.
1547 private void outputLine(
1548 String currentString,
1549 boolean preIndented,
1553 int[] startSubstringIndexes,
1554 int offsetInGlobalLine) {
1556 boolean emptyFirstSubString = false;
1557 String operatorString = operatorString(operator);
1558 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1559 boolean placeOperatorAhead = !placeOperatorBehind;
1561 // dump prefix operator?
1562 if (placeOperatorAhead) {
1567 if (operator != 0) {
1568 if (insertSpaceBefore(operator)) {
1569 formattedSource.append(' ');
1570 increaseSplitDelta(1);
1572 formattedSource.append(operatorString);
1573 increaseSplitDelta(operatorString.length());
1575 if (insertSpaceAfter(operator)
1576 // && operator != TokenNameimplements
1577 && operator != TokenNameextends) {
1578 // && operator != TokenNamethrows) {
1579 formattedSource.append(' ');
1580 increaseSplitDelta(1);
1584 SplitLine splitLine = null;
1585 if (options.maxLineLength == 0
1586 || getLength(currentString, depth) < options.maxLineLength
1587 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1589 // depending on the type of operator, outputs new line before of after dumping it
1590 // indent before postfix operator
1591 // indent also when the line cannot be split
1592 if (operator == TokenNameextends) {
1593 // || operator == TokenNameimplements
1594 // || operator == TokenNamethrows) {
1595 formattedSource.append(' ');
1596 increaseSplitDelta(1);
1598 if (placeOperatorBehind) {
1603 int max = currentString.length();
1604 if (multipleLineCommentCounter != 0) {
1606 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1607 String line = reader.readLine();
1608 while (line != null) {
1609 updateMappedPositionsWhileSplitting(
1610 beginningOfLineIndex,
1611 beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length);
1612 formattedSource.append(line);
1613 beginningOfLineIndex = beginningOfLineIndex + line.length();
1614 if ((line = reader.readLine()) != null) {
1615 formattedSource.append(options.lineSeparatorSequence);
1616 beginningOfLineIndex += options.lineSeparatorSequence.length;
1617 dumpTab(currentLineIndentationLevel);
1621 } catch(IOException e) {
1622 e.printStackTrace();
1625 updateMappedPositionsWhileSplitting(
1626 beginningOfLineIndex,
1627 beginningOfLineIndex + max);
1628 for (int i = 0; i < max; i++) {
1629 char currentChar = currentString.charAt(i);
1630 switch (currentChar) {
1635 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
1636 // a substring cannot end with a lineSeparatorSequence,
1637 // except if it has been added by format() after a one-line comment
1638 formattedSource.append(options.lineSeparatorSequence);
1640 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1645 formattedSource.append(currentChar);
1649 // update positions inside the mappedPositions table
1650 if (substringIndex != -1) {
1651 if (multipleLineCommentCounter == 0) {
1653 beginningOfLineIndex + startSubstringIndexes[substringIndex];
1654 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1657 // compute the splitDelta resulting with the operator and blank removal
1658 if (substringIndex + 1 != startSubstringIndexes.length) {
1660 startSubstringIndexes[substringIndex]
1662 - startSubstringIndexes[substringIndex + 1]);
1665 // dump postfix operator?
1666 if (placeOperatorBehind) {
1667 if (insertSpaceBefore(operator)) {
1668 formattedSource.append(' ');
1669 if (operator != 0) {
1670 increaseSplitDelta(1);
1673 formattedSource.append(operatorString);
1674 if (operator != 0) {
1675 increaseSplitDelta(operatorString.length());
1680 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1681 // extends has to stand alone on a line when currentString has been split.
1682 if (options.maxLineLength != 0
1683 && splitLine != null
1684 && (operator == TokenNameextends)) {
1685 // || operator == TokenNameimplements
1686 // || operator == TokenNamethrows)) {
1687 formattedSource.append(options.lineSeparatorSequence);
1688 increaseSplitDelta(options.lineSeparatorSequence.length);
1691 if (operator == TokenNameextends) {
1692 // || operator == TokenNameimplements
1693 // || operator == TokenNamethrows) {
1694 formattedSource.append(' ');
1695 increaseSplitDelta(1);
1698 // perform actual splitting
1699 String result[] = splitLine.substrings;
1700 int[] splitOperators = splitLine.operators;
1702 if (result[0].length() == 0) {
1703 // when the substring 0 is null, the substring 1 is correctly indented.
1705 emptyFirstSubString = true;
1707 // the operator going in front of the result[0] string is the operator parameter
1708 for (int i = 0, max = result.length; i < max; i++) {
1709 // the new depth is the current one if this is the first substring,
1710 // the current one + 1 otherwise.
1711 // if the substring is a comment, use the current indentation Level instead of the depth
1712 // (-1 because the ouputline increases depth).
1713 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
1714 String currentResult = result[i];
1716 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1718 (currentResult.startsWith("/*") //$NON-NLS-1$
1719 || currentResult.startsWith("//")) //$NON-NLS-1$
1720 ? indentationLevel - 1 : depth;
1723 i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1724 i == 0 ? newDepth : newDepth + 1,
1727 splitLine.startSubstringsIndexes,
1728 currentString.indexOf(currentResult));
1730 formattedSource.append(options.lineSeparatorSequence);
1731 increaseSplitDelta(options.lineSeparatorSequence.length);
1735 if (result.length == splitOperators.length - 1) {
1736 int lastOperator = splitOperators[result.length];
1737 String lastOperatorString = operatorString(lastOperator);
1738 formattedSource.append(options.lineSeparatorSequence);
1739 increaseSplitDelta(options.lineSeparatorSequence.length);
1741 if (breakLineBeforeOperator(lastOperator)) {
1743 if (lastOperator != 0) {
1744 if (insertSpaceBefore(lastOperator)) {
1745 formattedSource.append(' ');
1746 increaseSplitDelta(1);
1748 formattedSource.append(lastOperatorString);
1749 increaseSplitDelta(lastOperatorString.length());
1751 if (insertSpaceAfter(lastOperator)
1752 // && lastOperator != TokenNameimplements
1753 && lastOperator != TokenNameextends ) {
1754 // && lastOperator != TokenNamethrows) {
1755 formattedSource.append(' ');
1756 increaseSplitDelta(1);
1761 if (placeOperatorBehind) {
1762 if (insertSpaceBefore(operator)) {
1763 formattedSource.append(' ');
1764 increaseSplitDelta(1);
1766 formattedSource.append(operatorString);
1767 //increaseSplitDelta(operatorString.length());
1772 * Pops the top statement of the stack if it is <code>token</code>
1774 private int pop(int token) {
1776 if ((constructionsCount > 0)
1777 && (constructions[constructionsCount - 1] == token)) {
1779 constructionsCount--;
1785 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1787 private int popBlock() {
1789 if ((constructionsCount > 0)
1790 && ((constructions[constructionsCount - 1] == BLOCK)
1791 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1792 if (constructions[constructionsCount - 1] == BLOCK)
1794 constructionsCount--;
1800 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1801 * Does not remove <code>token</code> from the stack.
1802 * @param token the token to be left as the top of the stack
1804 private int popExclusiveUntil(int token) {
1806 int startCount = constructionsCount;
1807 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1808 if (constructions[i] != NONINDENT_BLOCK)
1810 constructionsCount--;
1816 * Pops elements until the stack is empty or the top element is
1817 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1818 * Does not remove it from the stack.
1820 private int popExclusiveUntilBlock() {
1821 int startCount = constructionsCount;
1823 for (int i = startCount - 1;
1824 i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK;
1826 constructionsCount--;
1833 * Pops elements until the stack is empty or the top element is
1834 * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1835 * Does not remove it from the stack.
1837 private int popExclusiveUntilBlockOrCase() {
1838 int startCount = constructionsCount;
1840 for (int i = startCount - 1;
1842 && constructions[i] != BLOCK
1843 && constructions[i] != NONINDENT_BLOCK
1844 && constructions[i] != TokenNamecase;
1846 constructionsCount--;
1853 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1854 * Removes <code>token</code> from the stack too.
1855 * @param token the token to remove from the stack
1857 private int popInclusiveUntil(int token) {
1858 int startCount = constructionsCount;
1860 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1861 if (constructions[i] != NONINDENT_BLOCK)
1863 constructionsCount--;
1865 if (constructionsCount > 0) {
1866 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1868 constructionsCount--;
1874 * Pops elements until the stack is empty or the top element is
1875 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1876 * Does not remove it from the stack.
1878 private int popInclusiveUntilBlock() {
1879 int startCount = constructionsCount;
1881 for (int i = startCount - 1;
1882 i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK);
1885 constructionsCount--;
1887 if (constructionsCount > 0) {
1888 if (constructions[constructionsCount - 1] == BLOCK)
1890 constructionsCount--;
1896 * Pushes a block in the stack.<br>
1897 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
1898 * pushes <code>NONINDENT_BLOCK</code> otherwise.
1899 * Creates a new bigger array if the current one is full.
1901 private int pushBlock() {
1903 if (constructionsCount == constructions.length)
1907 (constructions = new int[constructionsCount * 2]),
1909 constructionsCount);
1911 if ((constructionsCount == 0)
1912 || (constructions[constructionsCount - 1] == BLOCK)
1913 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1914 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1916 constructions[constructionsCount++] = BLOCK;
1918 constructions[constructionsCount++] = NONINDENT_BLOCK;
1924 * Pushes <code>token</code>.<br>
1925 * Creates a new bigger array if the current one is full.
1927 private int pushControlStatement(int token) {
1928 if (constructionsCount == constructions.length)
1932 (constructions = new int[constructionsCount * 2]),
1934 constructionsCount);
1935 constructions[constructionsCount++] = token;
1939 private static boolean separateFirstArgumentOn(int currentToken) {
1940 //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
1941 return currentToken != TokenNameif
1942 && currentToken != TokenNameLPAREN
1943 && currentToken != TokenNameNOT
1944 && currentToken != TokenNamewhile
1945 && currentToken != TokenNamefor
1946 && currentToken != TokenNameswitch;
1950 * Set the positions to map. The mapped positions should be retrieved using the
1951 * getMappedPositions() method.
1952 * @param positions int[]
1953 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1955 * @see #getMappedPositions()
1957 public void setPositionsToMap(int[] positions) {
1958 positionsToMap = positions;
1961 mappedPositions = new int[positions.length];
1965 * Appends a space character to the current line buffer.
1967 private void space() {
1968 currentLineBuffer.append(' ');
1969 increaseLineDelta(1);
1973 * Splits <code>stringToSplit</code> on the top level token<br>
1974 * If there are several identical token at the same level,
1975 * the string is cut into many pieces.
1976 * @return an object containing the operator and all the substrings
1977 * or null if the string cannot be split
1979 public SplitLine split(String stringToSplit) {
1980 return split(stringToSplit, 0);
1984 * Splits <code>stringToSplit</code> on the top level token<br>
1985 * If there are several identical token at the same level,
1986 * the string is cut into many pieces.
1987 * @return an object containing the operator and all the substrings
1988 * or null if the string cannot be split
1990 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1992 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
1993 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1995 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1999 int currentToken = 0;
2000 int splitTokenType = 0;
2001 int splitTokenDepth = Integer.MAX_VALUE;
2002 int splitTokenPriority = Integer.MAX_VALUE;
2004 int[] substringsStartPositions = new int[10];
2005 // contains the start position of substrings
2006 int[] substringsEndPositions = new int[10];
2007 // contains the start position of substrings
2008 int substringsCount = 1; // index in the substringsStartPosition array
2009 int[] splitOperators = new int[10];
2010 // contains the start position of substrings
2011 int splitOperatorsCount = 0; // index in the substringsStartPosition array
2012 int[] openParenthesisPosition = new int[10];
2013 int openParenthesisPositionCount = 0;
2015 int lastOpenParenthesisPosition = -1;
2016 // used to remember the position of the 1st open parenthesis
2017 // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2018 // setup the scanner with a new source
2019 int lastCommentStartPosition = -1;
2020 // to remember the start position of the last comment
2021 int firstTokenOnLine = -1;
2022 // to remember the first token of the line
2023 int previousToken = -1;
2024 // to remember the previous token.
2025 splitScanner.setSource(stringToSplit.toCharArray());
2030 // takes the next token
2032 if (currentToken != Scanner.TokenNameWHITESPACE)
2033 previousToken = currentToken;
2034 currentToken = splitScanner.getNextToken();
2035 } catch (InvalidInputException e) {
2036 if (!handleInvalidToken(e))
2038 currentToken = 0; // this value is not modify when an exception is raised.
2040 if (currentToken == TokenNameEOF)
2043 if (firstTokenOnLine == -1) {
2044 firstTokenOnLine = currentToken;
2046 switch (currentToken) {
2047 case TokenNameRBRACE :
2048 case TokenNameRPAREN :
2049 if (openParenthesisPositionCount > 0) {
2050 if (openParenthesisPositionCount == 1
2051 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2052 lastOpenParenthesisPosition = openParenthesisPosition[0];
2054 (splitTokenDepth == Integer.MAX_VALUE)
2055 || (splitTokenDepth > openParenthesisPositionCount
2056 && openParenthesisPositionCount == 1)) {
2058 splitTokenDepth = openParenthesisPositionCount;
2059 splitTokenPriority = Integer.MAX_VALUE;
2060 substringsStartPositions[0] = 0;
2061 // better token means the whole line until now is the first substring
2062 substringsCount = 1; // resets the count of substrings
2063 substringsEndPositions[0] = openParenthesisPosition[0];
2064 // substring ends on operator start
2065 position = openParenthesisPosition[0];
2066 // the string mustn't be cut before the closing parenthesis but after the opening one.
2067 splitOperatorsCount = 1; // resets the count of split operators
2068 splitOperators[0] = 0;
2070 openParenthesisPositionCount--;
2073 case TokenNameLBRACE :
2074 case TokenNameLPAREN :
2075 if (openParenthesisPositionCount == openParenthesisPosition.length) {
2077 openParenthesisPosition,
2079 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2081 openParenthesisPositionCount);
2083 openParenthesisPosition[openParenthesisPositionCount++] =
2084 splitScanner.currentPosition;
2085 if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) {
2086 openParenthesisPosition[openParenthesisPositionCount - 1] =
2087 splitScanner.startPosition;
2090 case TokenNameSEMICOLON : // ;
2091 case TokenNameCOMMA : // ,
2092 case TokenNameEQUAL : // =
2093 if (openParenthesisPositionCount < splitTokenDepth
2094 || (openParenthesisPositionCount == splitTokenDepth
2095 && splitTokenPriority > getTokenPriority(currentToken))) {
2096 // the current token is better than the one we currently have
2097 // (in level or in priority if same level)
2098 // reset the substringsCount
2099 splitTokenDepth = openParenthesisPositionCount;
2100 splitTokenType = currentToken;
2101 splitTokenPriority = getTokenPriority(currentToken);
2102 substringsStartPositions[0] = 0;
2103 // better token means the whole line until now is the first substring
2105 if (separateFirstArgumentOn(firstTokenOnLine)
2106 && openParenthesisPositionCount > 0) {
2107 substringsCount = 2; // resets the count of substrings
2109 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2110 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2111 substringsEndPositions[1] = splitScanner.startPosition;
2112 splitOperatorsCount = 2; // resets the count of split operators
2113 splitOperators[0] = 0;
2114 splitOperators[1] = currentToken;
2115 position = splitScanner.currentPosition;
2116 // next substring will start from operator end
2118 substringsCount = 1; // resets the count of substrings
2120 substringsEndPositions[0] = splitScanner.startPosition;
2121 // substring ends on operator start
2122 position = splitScanner.currentPosition;
2123 // next substring will start from operator end
2124 splitOperatorsCount = 1; // resets the count of split operators
2125 splitOperators[0] = currentToken;
2128 if ((openParenthesisPositionCount == splitTokenDepth
2129 && splitTokenPriority == getTokenPriority(currentToken))
2130 && splitTokenType != TokenNameEQUAL
2131 && currentToken != TokenNameEQUAL) {
2132 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
2133 // take only the 1st = into account.
2134 // if another token with the same priority is found,
2135 // push the start position of the substring and
2136 // push the token into the stack.
2137 // create a new array object if the current one is full.
2138 if (substringsCount == substringsStartPositions.length) {
2140 substringsStartPositions,
2142 (substringsStartPositions = new int[substringsCount * 2]),
2146 substringsEndPositions,
2148 (substringsEndPositions = new int[substringsCount * 2]),
2152 if (splitOperatorsCount == splitOperators.length) {
2156 (splitOperators = new int[splitOperatorsCount * 2]),
2158 splitOperatorsCount);
2160 substringsStartPositions[substringsCount] = position;
2161 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2162 // substring ends on operator start
2163 position = splitScanner.currentPosition;
2164 // next substring will start from operator end
2165 splitOperators[splitOperatorsCount++] = currentToken;
2170 case TokenNameCOLON : // : (15.24)
2171 // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
2172 // indeed it might appear also behind a case statement, and we do not to break at this point.
2173 if ((splitOperatorsCount == 0)
2174 || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2177 case TokenNameextends :
2178 // case TokenNameimplements :
2179 // case TokenNamethrows :
2181 case TokenNameDOT : // .
2182 case TokenNameMULTIPLY : // * (15.16.1)
2183 case TokenNameDIVIDE : // / (15.16.2)
2184 case TokenNameREMAINDER : // % (15.16.3)
2185 case TokenNamePLUS : // + (15.17, 15.17.2)
2186 case TokenNameMINUS : // - (15.17.2)
2187 case TokenNameLEFT_SHIFT : // << (15.18)
2188 case TokenNameRIGHT_SHIFT : // >> (15.18)
2189 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2190 case TokenNameLESS : // < (15.19.1)
2191 case TokenNameLESS_EQUAL : // <= (15.19.1)
2192 case TokenNameGREATER : // > (15.19.1)
2193 case TokenNameGREATER_EQUAL : // >= (15.19.1)
2194 // case TokenNameinstanceof : // instanceof
2195 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2196 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2197 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
2198 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
2199 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
2200 case TokenNameAND_AND : // && (15.22)
2201 case TokenNameOR_OR : // || (15.23)
2202 case TokenNameQUESTION : // ? (15.24)
2203 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
2204 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
2205 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
2206 case TokenNamePLUS_EQUAL : // += (15.25.2)
2207 case TokenNameMINUS_EQUAL : // -= (15.25.2)
2208 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
2209 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
2210 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2211 case TokenNameAND_EQUAL : // &= (15.25.2)
2212 case TokenNameXOR_EQUAL : // ^= (15.25.2)
2213 case TokenNameOR_EQUAL : // |= (15.25.2)
2215 if ((openParenthesisPositionCount < splitTokenDepth
2216 || (openParenthesisPositionCount == splitTokenDepth
2217 && splitTokenPriority > getTokenPriority(currentToken)))
2218 && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS)
2219 && (previousToken == TokenNameLBRACE
2220 || previousToken == TokenNameLBRACKET
2221 || splitScanner.startPosition == 0))) {
2222 // the current token is better than the one we currently have
2223 // (in level or in priority if same level)
2224 // reset the substringsCount
2225 splitTokenDepth = openParenthesisPositionCount;
2226 splitTokenType = currentToken;
2227 splitTokenPriority = getTokenPriority(currentToken);
2228 substringsStartPositions[0] = 0;
2229 // better token means the whole line until now is the first substring
2231 if (separateFirstArgumentOn(firstTokenOnLine)
2232 && openParenthesisPositionCount > 0) {
2233 substringsCount = 2; // resets the count of substrings
2235 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2236 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2237 substringsEndPositions[1] = splitScanner.startPosition;
2238 splitOperatorsCount = 3; // resets the count of split operators
2239 splitOperators[0] = 0;
2240 splitOperators[1] = 0;
2241 splitOperators[2] = currentToken;
2242 position = splitScanner.currentPosition;
2243 // next substring will start from operator end
2245 substringsCount = 1; // resets the count of substrings
2247 substringsEndPositions[0] = splitScanner.startPosition;
2248 // substring ends on operator start
2249 position = splitScanner.currentPosition;
2250 // next substring will start from operator end
2251 splitOperatorsCount = 2; // resets the count of split operators
2252 splitOperators[0] = 0;
2253 // nothing for first operand since operator will be inserted in front of the second operand
2254 splitOperators[1] = currentToken;
2258 if (openParenthesisPositionCount == splitTokenDepth
2259 && splitTokenPriority == getTokenPriority(currentToken)) {
2260 // if another token with the same priority is found,
2261 // push the start position of the substring and
2262 // push the token into the stack.
2263 // create a new array object if the current one is full.
2264 if (substringsCount == substringsStartPositions.length) {
2266 substringsStartPositions,
2268 (substringsStartPositions = new int[substringsCount * 2]),
2272 substringsEndPositions,
2274 (substringsEndPositions = new int[substringsCount * 2]),
2278 if (splitOperatorsCount == splitOperators.length) {
2282 (splitOperators = new int[splitOperatorsCount * 2]),
2284 splitOperatorsCount);
2286 substringsStartPositions[substringsCount] = position;
2287 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2288 // substring ends on operator start
2289 position = splitScanner.currentPosition;
2290 // next substring will start from operator end
2291 splitOperators[splitOperatorsCount++] = currentToken;
2297 if (isComment(currentToken)) {
2298 lastCommentStartPosition = splitScanner.startPosition;
2300 lastCommentStartPosition = -1;
2303 } catch (InvalidInputException e) {
2306 // if the string cannot be split, return null.
2307 if (splitOperatorsCount == 0)
2310 // ## SPECIAL CASES BEGIN
2311 if (((splitOperatorsCount == 2
2312 && splitOperators[1] == TokenNameDOT
2313 && splitTokenDepth == 0
2314 && lastOpenParenthesisPosition > -1)
2315 || (splitOperatorsCount > 2
2316 && splitOperators[1] == TokenNameDOT
2317 && splitTokenDepth == 0
2318 && lastOpenParenthesisPosition > -1
2319 && lastOpenParenthesisPosition <= options.maxLineLength)
2320 || (separateFirstArgumentOn(firstTokenOnLine)
2321 && splitTokenDepth > 0
2322 && lastOpenParenthesisPosition > -1))
2323 && (lastOpenParenthesisPosition < splitScanner.source.length
2324 && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2325 // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
2326 // only one split on a top level .
2327 // or more than one split on . and substring before open parenthesis fits one line.
2328 // or split inside parenthesis and first token is not a for/while/if
2331 stringToSplit.substring(lastOpenParenthesisPosition),
2332 lastOpenParenthesisPosition);
2333 if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2334 // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
2335 return new SplitLine(
2338 stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2339 stringToSplit.substring(lastOpenParenthesisPosition)},
2342 lastOpenParenthesisPosition + offsetInGlobalLine });
2344 // right substring can be split and is split on comma
2345 // copy substrings and operators
2346 // except if the 1st string is empty.
2347 int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2348 int subStringsLength = sl.substrings.length + 1 - startIndex;
2349 String[] result = new String[subStringsLength];
2350 int[] startIndexes = new int[subStringsLength];
2351 int operatorsLength = sl.operators.length + 1 - startIndex;
2352 int[] operators = new int[operatorsLength];
2354 result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2358 sl.startSubstringsIndexes,
2362 subStringsLength - 1);
2363 for (int i = subStringsLength - 1; i >= 0; i--) {
2364 startIndexes[i] += offsetInGlobalLine;
2366 System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1);
2367 System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1);
2369 return new SplitLine(operators, result, startIndexes);
2372 // if the last token is a comment and the substring before the comment fits on a line,
2373 // split before the comment and return the result.
2374 if (lastCommentStartPosition > -1
2375 && lastCommentStartPosition < options.maxLineLength
2376 && splitTokenPriority > 50) {
2377 int end = lastCommentStartPosition;
2378 int start = lastCommentStartPosition;
2379 if (stringToSplit.charAt(end - 1) == ' ') {
2382 if (start != end && stringToSplit.charAt(start) == ' ') {
2385 return new SplitLine(
2387 new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)},
2388 new int[] { 0, start });
2390 if (position != stringToSplit.length()) {
2391 if (substringsCount == substringsStartPositions.length) {
2393 substringsStartPositions,
2395 (substringsStartPositions = new int[substringsCount * 2]),
2399 substringsEndPositions,
2401 (substringsEndPositions = new int[substringsCount * 2]),
2405 // avoid empty extra substring, e.g. line terminated with a semi-colon
2406 substringsStartPositions[substringsCount] = position;
2407 substringsEndPositions[substringsCount++] = stringToSplit.length();
2409 if (splitOperatorsCount == splitOperators.length) {
2413 (splitOperators = new int[splitOperatorsCount * 2]),
2415 splitOperatorsCount);
2417 splitOperators[splitOperatorsCount] = 0;
2419 // the last element of the stack is the position of the end of StringToSPlit
2420 // +1 because the substring method excludes the last character
2421 String[] result = new String[substringsCount];
2422 for (int i = 0; i < substringsCount; i++) {
2423 int start = substringsStartPositions[i];
2424 int end = substringsEndPositions[i];
2425 if (stringToSplit.charAt(start) == ' ') {
2427 substringsStartPositions[i]++;
2429 if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2432 result[i] = stringToSplit.substring(start, end);
2433 substringsStartPositions[i] += offsetInGlobalLine;
2435 if (splitOperatorsCount > substringsCount) {
2437 substringsStartPositions,
2439 (substringsStartPositions = new int[splitOperatorsCount]),
2443 substringsEndPositions,
2445 (substringsEndPositions = new int[splitOperatorsCount]),
2448 for (int i = substringsCount; i < splitOperatorsCount; i++) {
2449 substringsStartPositions[i] = position;
2450 substringsEndPositions[i] = position;
2455 (splitOperators = new int[splitOperatorsCount]),
2457 splitOperatorsCount);
2460 substringsStartPositions,
2462 (substringsStartPositions = new int[substringsCount]),
2466 substringsEndPositions,
2468 (substringsEndPositions = new int[substringsCount]),
2474 (splitOperators = new int[substringsCount]),
2478 SplitLine splitLine =
2479 new SplitLine(splitOperators, result, substringsStartPositions);
2483 private void updateMappedPositions(int startPosition) {
2484 if (positionsToMap == null) {
2487 char[] source = scanner.source;
2488 int sourceLength = source.length;
2489 while (indexToMap < positionsToMap.length
2490 && positionsToMap[indexToMap] <= startPosition) {
2491 int posToMap = positionsToMap[indexToMap];
2493 || posToMap >= sourceLength) { // protection against out of bounds position
2494 if (posToMap == sourceLength) {
2495 mappedPositions[indexToMap] = formattedSource.length();
2497 indexToMap = positionsToMap.length; // no more mapping
2500 if (CharOperation.isWhitespace(source[posToMap])) {
2501 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2503 if (posToMap == sourceLength - 1) {
2504 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2506 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2513 private void updateMappedPositionsWhileSplitting(
2516 if (mappedPositions == null || mappedPositions.length == indexInMap)
2519 while (indexInMap < mappedPositions.length
2520 && startPosition <= mappedPositions[indexInMap]
2521 && mappedPositions[indexInMap] < endPosition
2522 && indexInMap < indexToMap) {
2523 mappedPositions[indexInMap] += splitDelta;
2528 private int getLength(String s, int tabDepth) {
2530 for (int i = 0; i < tabDepth; i++) {
2531 length += options.tabSize;
2533 for (int i = 0, max = s.length(); i < max; i++) {
2534 char currentChar = s.charAt(i);
2535 switch (currentChar) {
2537 length += options.tabSize;
2547 * Sets the initial indentation level
2548 * @param indentationLevel new indentation level
2552 public void setInitialIndentationLevel(int newIndentationLevel) {
2553 this.initialIndentationLevel =
2554 currentLineIndentationLevel = indentationLevel = newIndentationLevel;