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++) {
197 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
198 String optionName = settings[i].getOptionName();
199 int valueIndex = settings[i].getCurrentValueIndex();
201 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
202 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
204 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
205 options.put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
207 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
208 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
210 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
211 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
213 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
214 options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
217 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
219 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
220 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
222 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
223 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
225 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
226 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
235 * Returns the end of the source code.
237 private final String copyRemainingSource() {
238 char str[] = scanner.source;
239 int startPosition = scanner.startPosition;
240 int length = str.length - startPosition;
241 StringBuffer bufr = new StringBuffer(length);
242 if (startPosition < str.length) {
243 bufr.append(str, startPosition, length);
245 return (bufr.toString());
249 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
251 private void dumpTab(int tabCount) {
252 if (options.indentWithTab) {
253 for (int j = 0; j < tabCount; j++) {
254 formattedSource.append('\t');
255 increaseSplitDelta(1);
258 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
259 formattedSource.append(' ');
260 increaseSplitDelta(1);
266 * Dumps <code>currentLineBuffer</code> into the formatted string.
268 private void flushBuffer() {
269 String currentString = currentLineBuffer.toString();
271 beginningOfLineIndex = formattedSource.length();
272 if (containsOpenCloseBraces) {
273 containsOpenCloseBraces = false;
274 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
275 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
277 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
279 int scannerSourceLength = scanner.source.length;
280 if (scannerSourceLength > 2) {
281 if (scanner.source[scannerSourceLength - 1] == '\n' && 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;
318 int dollarBraceCount = 0;
320 // openBracketCount is used to count the number of open brackets not closed yet.
321 int openBracketCount = 0;
322 int unarySignModifier = 0;
324 // openParenthesis[0] is used to count the parenthesis not belonging to a condition
325 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
326 int openParenthesisCount = 1;
327 int[] openParenthesis = new int[10];
329 // tokenBeforeColon is used to know what token goes along with the current :
330 // it can be case or ?
331 int tokenBeforeColonCount = 0;
332 int[] tokenBeforeColon = new int[10];
334 constructionsCount = 0; // initializes the constructions count.
336 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
339 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
340 boolean specialElse = false;
342 // OPTION (IndentationLevel): initial indentation level may be non-zero.
343 currentLineIndentationLevel += constructionsCount;
345 // An InvalidInputException exception might cause the termination of this loop.
348 // Get the next token. Catch invalid input and output it
349 // with minimal formatting, also catch end of input and
352 token = scanner.getNextToken();
354 int currentEndPosition = scanner.getCurrentTokenEndPosition();
355 int currentStartPosition = scanner.getCurrentTokenStartPosition();
357 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
358 System.out.println(scanner.toStringAction(token));
361 // Patch for line comment
362 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
363 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
364 int length = scanner.currentPosition;
365 loop : for (int index = length - 1; index >= 0; index--) {
366 switch (scanner.source[index]) {
369 scanner.currentPosition--;
376 } catch (InvalidInputException e) {
377 if (!handleInvalidToken(e)) {
382 if (token == Scanner.TokenNameEOF)
385 /* ## MODIFYING the indentation level before generating new lines
386 and indentation in the output string
389 // Removes all the indentations made by statements not followed by a block
390 // except if the current token is ELSE, CATCH or if we are in a switch/case
391 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
394 if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
398 indentationLevel += popInclusiveUntil(TokenNameif);
400 // case TokenNamecatch :
401 // indentationLevel += popInclusiveUntil(TokenNamecatch);
403 // case TokenNamefinally :
404 // indentationLevel += popInclusiveUntil(TokenNamecatch);
406 case TokenNamewhile :
407 if (nlicsToken == TokenNamedo) {
408 indentationLevel += pop(TokenNamedo);
412 indentationLevel += popExclusiveUntilBlockOrCase();
413 // clear until a CASE, DEFAULT or BLOCK is encountered.
414 // Thus, the indentationLevel is correctly cleared either
415 // in a switch/case statement or in any other situation.
417 clearNonBlockIndents = false;
419 // returns to the indentation level created by the SWITCH keyword
420 // if the current token is a CASE or a DEFAULT
421 if (token == TokenNamecase || token == TokenNamedefault) {
422 indentationLevel += pop(TokenNamecase);
424 // if (token == Scanner.TokenNamethrows) {
425 // inThrowsClause = true;
427 if ((token == Scanner.TokenNameclass // || token == Scanner.TokenNameinterface
429 && previousToken != Scanner.TokenNameDOT) {
430 inClassOrInterfaceHeader = true;
433 /* ## APPEND newlines and indentations to the output string
435 // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
436 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
437 // if (pendingNewlineAfterParen
438 // && previousCompilableToken == TokenNameelse
439 // && token == TokenNameif
440 // && options.compactElseIfMode) {
441 // pendingNewlineAfterParen = false;
442 // pendingNewLines = 0;
443 // indentationLevel += pop(TokenNameelse);
444 // // because else if is now one single statement,
445 // // the indentation level after it is increased by one and not by 2
446 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
448 // Add a newline & indent to the formatted source string if
449 // a for/if-else/while statement was scanned and there is no block
451 pendingNewlineAfterParen =
452 pendingNewlineAfterParen || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
453 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
454 pendingNewlineAfterParen = false;
456 // Do to add a newline & indent sequence if the current token is an
457 // open brace or a period or if the current token is a semi-colon and the
458 // previous token is a close paren.
459 // add a new line if a parenthesis belonging to a for() statement
460 // has been closed and the current token is not an opening brace
461 if (token != TokenNameLBRACE
462 && !isComment(token) // to avoid adding new line between else and a comment
463 && token != TokenNameDOT
464 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
466 currentLineIndentationLevel = indentationLevel;
468 pendingSpace = false;
470 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
472 if (constructionsCount > 0
473 && constructions[constructionsCount - 1] != BLOCK
474 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
475 currentLineIndentationLevel = indentationLevel - 1;
477 currentLineIndentationLevel = indentationLevel;
480 pendingSpace = false;
484 if (token == TokenNameLBRACE
485 && options.newLineBeforeOpeningBraceMode
486 && constructionsCount > 0
487 && constructions[constructionsCount - 1] == TokenNamedo) {
489 currentLineIndentationLevel = indentationLevel - 1;
491 pendingSpace = false;
494 if (token == TokenNameLBRACE && inThrowsClause) {
495 inThrowsClause = false;
496 if (options.newLineBeforeOpeningBraceMode) {
498 currentLineIndentationLevel = indentationLevel;
500 pendingSpace = false;
504 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
505 inClassOrInterfaceHeader = false;
506 if (options.newLineBeforeOpeningBraceMode) {
508 currentLineIndentationLevel = indentationLevel;
510 pendingSpace = false;
513 // Add pending new lines to the formatted source string.
514 // Note: pending new lines are not added if the current token
515 // is a single line comment or whitespace.
516 // if the comment is between parenthesis, there is no blank line preservation
517 // (if it's a one-line comment, a blank line is added after it).
518 if (((pendingNewLines > 0 && (!isComment(token)))
519 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
520 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
521 && token != Scanner.TokenNameWHITESPACE) {
523 // Do not add newline & indent between an adjoining close brace and
524 // close paren. Anonymous inner classes may use this form.
525 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
527 // OPTION (NewLineInCompoundStatement): do not add newline & indent
528 // between close brace and else, (do) while, catch, and finally if
529 // newlineInCompoundStatement is true.
530 boolean nlicsOption =
531 previousToken == TokenNameRBRACE
532 && !options.newlineInControlStatementMode
533 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo));
534 // || token == TokenNamecatch
535 // || token == TokenNamefinally);
537 // Do not add a newline & indent between a close brace and semi-colon.
538 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
540 // Do not add a new line & indent between a multiline comment and a opening brace
541 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
543 // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
544 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
546 // Add a newline and indent, if appropriate.
548 || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
550 // if clearAllBlankLinesMode=false, leaves the blank lines
551 // inserted by the user
552 // if clearAllBlankLinesMode=true, removes all of then
553 // and insert only blank lines required by the formatting.
554 if (!options.clearAllBlankLinesMode) {
555 // (isComment(token))
556 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
557 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
559 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
560 containsOpenCloseBraces = true;
561 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
562 if (isComment(previousToken)) {
563 newLine(pendingNewLines);
565 /* if (!(constructionsCount > 1
566 && constructions[constructionsCount-1] == NONINDENT_BLOCK
567 && (constructions[constructionsCount-2] == TokenNamefor
568 || constructions[constructionsCount-2] == TokenNamewhile))) {*/
569 if (options.newLineInEmptyBlockMode) {
570 if (inArrayAssignment) {
571 newLine(1); // array assigment with an empty block
573 newLine(pendingNewLines);
579 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
580 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
581 && token == TokenNameSEMICOLON)) {
582 newLine(pendingNewLines);
585 if (((previousCompilableToken == TokenNameSEMICOLON)
586 || (previousCompilableToken == TokenNameLBRACE)
587 || (previousCompilableToken == TokenNameRBRACE)
588 || (isComment(previousToken)))
589 && (token == TokenNameRBRACE)) {
590 indentationOffset = -1;
591 indentationLevel += popExclusiveUntilBlock();
593 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
595 currentLineIndentationLevel++;
597 currentLineIndentationLevel = indentationLevel + indentationOffset;
599 pendingSpace = false;
600 indentationOffset = 0;
603 newLinesInWhitespace = 0;
606 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
611 case TokenNameDOLLAR_LBRACE :
615 // case TokenNamefinally :
616 expectingOpenBrace = true;
617 pendingNewlineAfterParen = true;
618 indentationLevel += pushControlStatement(token);
621 case TokenNamedefault :
622 if (tokenBeforeColonCount == tokenBeforeColon.length) {
626 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
628 tokenBeforeColonCount);
630 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
631 indentationLevel += pushControlStatement(TokenNamecase);
633 case TokenNameQUESTION :
634 if (tokenBeforeColonCount == tokenBeforeColon.length) {
638 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
640 tokenBeforeColonCount);
642 tokenBeforeColon[tokenBeforeColonCount++] = token;
644 case TokenNameswitch :
647 case TokenNamewhile :
648 if (openParenthesisCount == openParenthesis.length) {
649 System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
651 openParenthesis[openParenthesisCount++] = 0;
652 expectingOpenBrace = true;
654 indentationLevel += pushControlStatement(token);
656 // case TokenNametry :
657 // pendingNewlineAfterParen = true;
658 // case TokenNamecatch :
659 // // several CATCH statements can be contiguous.
660 // // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
661 // // as CATCH and TRY are the same token in the stack).
662 // expectingOpenBrace = true;
663 // indentationLevel += pushControlStatement(TokenNamecatch);
667 expectingOpenBrace = true;
668 indentationLevel += pushControlStatement(token);
673 case TokenNameLPAREN :
674 // if (previousToken == TokenNamesynchronized) {
675 // indentationLevel += pushControlStatement(previousToken);
677 // Put a space between the previous and current token if the
678 // previous token was not a keyword, open paren, logical
679 // compliment (eg: !), semi-colon, open brace, close brace,
681 if (previousCompilableToken != TokenNameLBRACKET
682 && previousToken != TokenNameIdentifier
683 && previousToken != 0
684 && previousToken != TokenNameNOT
685 && previousToken != TokenNameLPAREN
686 && previousToken != TokenNameTWIDDLE
687 && previousToken != TokenNameSEMICOLON
688 && previousToken != TokenNameLBRACE
689 && previousToken != TokenNameRBRACE) {
690 // && previousToken != TokenNamesuper
691 // && previousToken != TokenNamethis) {
694 // If in a for/if/while statement, increase the parenthesis count
695 // for the current openParenthesisCount
696 // else increase the count for stand alone parenthesis.
697 if (openParenthesisCount > 0)
698 openParenthesis[openParenthesisCount - 1]++;
700 openParenthesis[0]++;
702 pendingSpace = false;
705 case TokenNameRPAREN :
707 // Decrease the parenthesis count
708 // if there is no more unclosed parenthesis,
709 // a new line and indent may be append (depending on the next token).
710 if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
711 openParenthesis[openParenthesisCount - 1]--;
712 if (openParenthesis[openParenthesisCount - 1] <= 0) {
713 pendingNewlineAfterParen = true;
714 inAssignment = false;
715 openParenthesisCount--;
718 openParenthesis[0]--;
720 pendingSpace = false;
722 case TokenNameLBRACE :
723 if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
724 // if (previousCompilableToken == TokenNameRBRACKET) {
725 inArrayAssignment = true;
726 inAssignment = false;
728 if (inArrayAssignment) {
729 indentationLevel += pushBlock();
731 // Add new line and increase indentation level after open brace.
733 indentationLevel += pushBlock();
736 case TokenNameRBRACE :
737 if (dollarBraceCount > 0) {
741 if (previousCompilableToken == TokenNameRPAREN) {
742 pendingSpace = false;
744 if (inArrayAssignment) {
745 inArrayAssignment = false;
747 indentationLevel += popInclusiveUntilBlock();
750 indentationLevel += popInclusiveUntilBlock();
752 if (previousCompilableToken == TokenNameRPAREN) {
753 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
754 currentLineBuffer.append(options.lineSeparatorSequence);
755 increaseLineDelta(options.lineSeparatorSequence.length);
757 if (constructionsCount > 0) {
758 switch (constructions[constructionsCount - 1]) {
760 //indentationLevel += popExclusiveUntilBlock();
762 case TokenNameswitch :
765 // case TokenNametry :
766 // case TokenNamecatch :
767 // case TokenNamefinally :
768 case TokenNamewhile :
770 // case TokenNamesynchronized :
771 clearNonBlockIndents = true;
778 case TokenNameLBRACKET :
780 pendingSpace = false;
782 case TokenNameRBRACKET :
783 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
784 // if there is no left bracket to close, the right bracket is ignored.
785 pendingSpace = false;
787 case TokenNameCOMMA :
789 pendingSpace = false;
791 case TokenNameSEMICOLON :
793 // Do not generate line terminators in the definition of
794 // the for statement.
795 // if not in this case, jump a line and reduce indentation after the brace
796 // if the block it closes belongs to a conditional statement (if, while, do...).
797 if (openParenthesisCount <= 1) {
799 if (expectingOpenBrace) {
800 clearNonBlockIndents = true;
801 expectingOpenBrace = false;
804 inAssignment = false;
805 pendingSpace = false;
807 case TokenNamePLUS_PLUS :
808 case TokenNameMINUS_MINUS :
810 // Do not put a space between a post-increment/decrement
811 // and the identifier being modified.
812 if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET) {
813 pendingSpace = false;
816 case TokenNamePLUS : // previously ADDITION
817 case TokenNameMINUS :
819 // Handle the unary operators plus and minus via a flag
820 if (!isLiteralToken(previousToken)
821 && previousToken != TokenNameIdentifier
822 && previousToken != TokenNameRPAREN
823 && previousToken != TokenNameRBRACKET) {
824 unarySignModifier = 1;
827 case TokenNameCOLON :
828 // In a switch/case statement, add a newline & indent
829 // when a colon is encountered.
830 if (tokenBeforeColonCount > 0) {
831 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
834 tokenBeforeColonCount--;
837 case TokenNameEQUAL :
840 case Scanner.TokenNameCOMMENT_LINE :
843 currentLineIndentationLevel++;
845 break; // a line is always inserted after a one-line comment
846 case Scanner.TokenNameCOMMENT_PHPDOC :
847 case Scanner.TokenNameCOMMENT_BLOCK :
848 currentCommentOffset = getCurrentCommentOffset();
851 case Scanner.TokenNameWHITESPACE :
853 // Count the number of line terminators in the whitespace so
854 // line spacing can be preserved near comments.
855 char[] source = scanner.source;
856 newLinesInWhitespace = 0;
857 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
858 if (source[i] == '\r') {
860 if (source[++i] == '\n') {
861 newLinesInWhitespace++;
863 newLinesInWhitespace++;
866 newLinesInWhitespace++;
868 } else if (source[i] == '\n') {
869 newLinesInWhitespace++;
872 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
874 // case TokenNameHTML :
875 // // Add the next token to the formatted source string.
876 // // outputCurrentToken(token);
877 // int startPosition = scanner.startPosition;
879 // for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
880 // char currentCharacter = scanner.source[i];
881 // updateMappedPositions(i);
882 // currentLineBuffer.append(currentCharacter);
886 if ((token == TokenNameIdentifier) || 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 = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
920 && insertSpaceAfter(previousToken)
921 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
922 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
923 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
926 // Add the next token to the formatted source string.
927 outputCurrentToken(token);
928 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
930 currentLineBuffer.append(options.lineSeparatorSequence);
931 increaseLineDelta(options.lineSeparatorSequence.length);
935 // Whitespace tokens do not need to be remembered.
936 if (token != Scanner.TokenNameWHITESPACE) {
937 previousToken = token;
938 if (token != Scanner.TokenNameCOMMENT_BLOCK
939 && token != Scanner.TokenNameCOMMENT_LINE
940 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
941 previousCompilableToken = token;
945 output(copyRemainingSource());
947 // dump the last token of the source in the formatted output.
948 } catch (InvalidInputException e) {
949 output(copyRemainingSource());
951 // dump the last token of the source in the formatted output.
956 * Formats the char array <code>sourceString</code>,
957 * and returns a string containing the formatted version.
958 * @return the formatted ouput.
960 public String formatSourceString(String sourceString) {
961 char[] sourceChars = sourceString.toCharArray();
962 formattedSource = new StringBuffer(sourceChars.length);
963 scanner.setSource(sourceChars);
965 return formattedSource.toString();
969 * Formats the char array <code>sourceString</code>,
970 * and returns a string containing the formatted version.
971 * @param string the string to format
972 * @param indentationLevel the initial indentation level
973 * @return the formatted ouput.
975 public String format(String string, int indentationLevel) {
976 return format(string, indentationLevel, (int[]) null);
980 * Formats the char array <code>sourceString</code>,
981 * and returns a string containing the formatted version.
982 * The positions array is modified to contain the mapped positions.
983 * @param string the string to format
984 * @param indentationLevel the initial indentation level
985 * @param positions the array of positions to map
986 * @return the formatted ouput.
988 public String format(String string, int indentationLevel, int[] positions) {
989 return this.format(string, indentationLevel, positions, null);
992 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
993 if (lineSeparator != null) {
994 this.options.setLineSeparator(lineSeparator);
996 if (positions != null) {
997 this.setPositionsToMap(positions);
998 this.setInitialIndentationLevel(indentationLevel);
999 String formattedString = this.formatSourceString(string);
1000 int[] mappedPositions = this.getMappedPositions();
1001 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1002 return formattedString;
1004 this.setInitialIndentationLevel(indentationLevel);
1005 return this.formatSourceString(string);
1009 * Formats the char array <code>sourceString</code>,
1010 * and returns a string containing the formatted version. The initial indentation level is 0.
1011 * @param string the string to format
1012 * @return the formatted ouput.
1014 public String format(String string) {
1015 return this.format(string, 0, (int[]) null);
1019 * Formats a given source string, starting indenting it at a particular
1020 * depth and using the given options
1022 * @deprecated backport 1.0 internal functionality
1024 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1025 CodeFormatter formatter = new CodeFormatter(options);
1026 formatter.setInitialIndentationLevel(initialIndentationLevel);
1027 return formatter.formatSourceString(sourceString);
1031 * Returns the number of characters and tab char between the beginning of the line
1032 * and the beginning of the comment.
1034 private int getCurrentCommentOffset() {
1035 int linePtr = scanner.linePtr;
1036 // if there is no beginning of line, return 0.
1040 int beginningOfLine = scanner.lineEnds[linePtr];
1041 int currentStartPosition = scanner.startPosition;
1042 char[] source = scanner.source;
1044 // find the position of the beginning of the line containing the comment
1045 while (beginningOfLine > currentStartPosition) {
1047 beginningOfLine = scanner.lineEnds[--linePtr];
1049 beginningOfLine = 0;
1053 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1054 char currentCharacter = source[i];
1055 switch (currentCharacter) {
1057 offset += options.tabSize;
1073 * Returns an array of descriptions for the configurable options.
1074 * The descriptions may be changed and passed back to a different
1077 * @deprecated backport 1.0 internal functionality
1079 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1080 String componentName = CodeFormatter.class.getName();
1081 FormatterOptions options = new FormatterOptions();
1082 return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
1083 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
1084 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
1085 // new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1086 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
1087 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
1088 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
1089 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
1090 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1095 * Returns the array of mapped positions.
1096 * Returns null is no positions have been set.
1098 * @deprecated There is no need to retrieve the mapped positions anymore.
1100 public int[] getMappedPositions() {
1101 return mappedPositions;
1105 * Returns the priority of the token given as argument<br>
1106 * The most prioritary the token is, the smallest the return value is.
1107 * @return the priority of <code>token</code>
1108 * @param token the token of which the priority is requested
1110 private static int getTokenPriority(int token) {
1112 case TokenNameextends :
1113 // case TokenNameimplements :
1114 // case TokenNamethrows :
1116 case TokenNameSEMICOLON : // ;
1118 case TokenNameCOMMA : // ,
1120 case TokenNameEQUAL : // =
1122 case TokenNameAND_AND : // &&
1123 case TokenNameOR_OR : // ||
1125 case TokenNameQUESTION : // ?
1126 case TokenNameCOLON : // :
1127 return 50; // it's better cutting on ?: than on ;
1128 case TokenNameEQUAL_EQUAL : // ==
1129 case TokenNameNOT_EQUAL : // !=
1131 case TokenNameLESS : // <
1132 case TokenNameLESS_EQUAL : // <=
1133 case TokenNameGREATER : // >
1134 case TokenNameGREATER_EQUAL : // >=
1135 // case TokenNameinstanceof : // instanceof
1137 case TokenNamePLUS : // +
1138 case TokenNameMINUS : // -
1140 case TokenNameMULTIPLY : // *
1141 case TokenNameDIVIDE : // /
1142 case TokenNameREMAINDER : // %
1144 case TokenNameLEFT_SHIFT : // <<
1145 case TokenNameRIGHT_SHIFT : // >>
1146 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1148 case TokenNameAND : // &
1149 case TokenNameOR : // |
1150 case TokenNameXOR : // ^
1152 case TokenNameMULTIPLY_EQUAL : // *=
1153 case TokenNameDIVIDE_EQUAL : // /=
1154 case TokenNameREMAINDER_EQUAL : // %=
1155 case TokenNamePLUS_EQUAL : // +=
1156 case TokenNameMINUS_EQUAL : // -=
1157 case TokenNameLEFT_SHIFT_EQUAL : // <<=
1158 case TokenNameRIGHT_SHIFT_EQUAL : // >>=
1159 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1160 case TokenNameAND_EQUAL : // &=
1161 case TokenNameXOR_EQUAL : // ^=
1162 case TokenNameOR_EQUAL : // |=
1164 case TokenNameDOT : // .
1167 return Integer.MAX_VALUE;
1172 * Handles the exception raised when an invalid token is encountered.
1173 * Returns true if the exception has been handled, false otherwise.
1175 private boolean handleInvalidToken(Exception e) {
1176 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1177 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1178 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1184 private final void increaseGlobalDelta(int offset) {
1185 globalDelta += offset;
1188 private final void increaseLineDelta(int offset) {
1189 lineDelta += offset;
1192 private final void increaseSplitDelta(int offset) {
1193 splitDelta += offset;
1197 * Returns true if a space has to be inserted after <code>operator</code>
1200 private boolean insertSpaceAfter(int token) {
1202 case TokenNameLPAREN :
1204 case TokenNameTWIDDLE :
1206 case 0 : // no token
1207 case TokenNameLBRACKET :
1208 case Scanner.TokenNameCOMMENT_LINE :
1216 * Returns true if a space has to be inserted before <code>operator</code>
1217 * false otherwise.<br>
1218 * Cannot be static as it uses the code formatter options
1219 * (to know if the compact assignment mode is on).
1221 private boolean insertSpaceBefore(int token) {
1223 case TokenNameEQUAL :
1224 return (!options.compactAssignmentMode);
1230 private static boolean isComment(int token) {
1232 token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE || token == Scanner.TokenNameCOMMENT_PHPDOC;
1236 private static boolean isLiteralToken(int token) {
1237 boolean result = token == TokenNameIntegerLiteral
1238 // || token == TokenNameLongLiteral
1239 // || token == TokenNameFloatingPointLiteral
1240 || token == TokenNameDoubleLiteral
1241 // || token == TokenNameCharacterLiteral
1242 || token == TokenNameStringLiteral;
1247 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1248 * it is split and the result is dumped in <code>formattedSource</code>
1249 * @param newLineCount the number of new lines to append
1251 private void newLine(int newLineCount) {
1253 // format current line
1255 beginningOfLineIndex = formattedSource.length();
1256 String currentLine = currentLineBuffer.toString();
1257 if (containsOpenCloseBraces) {
1258 containsOpenCloseBraces = false;
1259 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1260 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1262 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1264 // dump line break(s)
1265 for (int i = 0; i < newLineCount; i++) {
1266 formattedSource.append(options.lineSeparatorSequence);
1267 increaseSplitDelta(options.lineSeparatorSequence.length);
1269 // reset formatter for next line
1270 int currentLength = currentLine.length();
1271 currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1273 increaseGlobalDelta(splitDelta);
1274 increaseGlobalDelta(lineDelta);
1276 currentLineIndentationLevel = initialIndentationLevel;
1279 private String operatorString(int operator) {
1281 case TokenNameextends :
1282 return "extends"; //$NON-NLS-1$
1284 // case TokenNameimplements :
1285 // return "implements"; //$NON-NLS-1$
1287 // case TokenNamethrows :
1288 // return "throws"; //$NON-NLS-1$
1290 case TokenNameSEMICOLON : // ;
1291 return ";"; //$NON-NLS-1$
1293 case TokenNameCOMMA : // ,
1294 return ","; //$NON-NLS-1$
1296 case TokenNameEQUAL : // =
1297 return "="; //$NON-NLS-1$
1299 case TokenNameAND_AND : // && (15.22)
1300 return "&&"; //$NON-NLS-1$
1302 case TokenNameOR_OR : // || (15.23)
1303 return "||"; //$NON-NLS-1$
1305 case TokenNameQUESTION : // ? (15.24)
1306 return "?"; //$NON-NLS-1$
1308 case TokenNameCOLON : // : (15.24)
1309 return ":"; //$NON-NLS-1$
1311 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1312 return "=="; //$NON-NLS-1$
1314 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1315 return "!="; //$NON-NLS-1$
1317 case TokenNameLESS : // < (15.19.1)
1318 return "<"; //$NON-NLS-1$
1320 case TokenNameLESS_EQUAL : // <= (15.19.1)
1321 return "<="; //$NON-NLS-1$
1323 case TokenNameGREATER : // > (15.19.1)
1324 return ">"; //$NON-NLS-1$
1326 case TokenNameGREATER_EQUAL : // >= (15.19.1)
1327 return ">="; //$NON-NLS-1$
1329 // case TokenNameinstanceof : // instanceof
1330 // return "instanceof"; //$NON-NLS-1$
1332 case TokenNamePLUS : // + (15.17, 15.17.2)
1333 return "+"; //$NON-NLS-1$
1335 case TokenNameMINUS : // - (15.17.2)
1336 return "-"; //$NON-NLS-1$
1338 case TokenNameMULTIPLY : // * (15.16.1)
1339 return "*"; //$NON-NLS-1$
1341 case TokenNameDIVIDE : // / (15.16.2)
1342 return "/"; //$NON-NLS-1$
1344 case TokenNameREMAINDER : // % (15.16.3)
1345 return "%"; //$NON-NLS-1$
1347 case TokenNameLEFT_SHIFT : // << (15.18)
1348 return "<<"; //$NON-NLS-1$
1350 case TokenNameRIGHT_SHIFT : // >> (15.18)
1351 return ">>"; //$NON-NLS-1$
1353 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1354 // return ">>>"; //$NON-NLS-1$
1356 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
1357 return "&"; //$NON-NLS-1$
1359 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
1360 return "|"; //$NON-NLS-1$
1362 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
1363 return "^"; //$NON-NLS-1$
1365 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
1366 return "*="; //$NON-NLS-1$
1368 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
1369 return "/="; //$NON-NLS-1$
1371 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
1372 return "%="; //$NON-NLS-1$
1374 case TokenNamePLUS_EQUAL : // += (15.25.2)
1375 return "+="; //$NON-NLS-1$
1377 case TokenNameMINUS_EQUAL : // -= (15.25.2)
1378 return "-="; //$NON-NLS-1$
1380 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
1381 return "<<="; //$NON-NLS-1$
1383 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
1384 return ">>="; //$NON-NLS-1$
1386 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1387 // return ">>>="; //$NON-NLS-1$
1389 case TokenNameAND_EQUAL : // &= (15.25.2)
1390 return "&="; //$NON-NLS-1$
1392 case TokenNameXOR_EQUAL : // ^= (15.25.2)
1393 return "^="; //$NON-NLS-1$
1395 case TokenNameOR_EQUAL : // |= (15.25.2)
1396 return "|="; //$NON-NLS-1$
1398 case TokenNameDOT : // .
1399 return "."; //$NON-NLS-1$
1402 return ""; //$NON-NLS-1$
1407 * Appends <code>stringToOutput</code> to the formatted output.<br>
1408 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1410 private void output(String stringToOutput) {
1411 char currentCharacter;
1412 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1413 currentCharacter = stringToOutput.charAt(i);
1414 if (currentCharacter != '\t') {
1415 currentLineBuffer.append(currentCharacter);
1421 * Appends <code>token</code> to the formatted output.<br>
1422 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1424 private void outputCurrentToken(int token) {
1425 char[] source = scanner.source;
1426 int startPosition = scanner.startPosition;
1429 case Scanner.TokenNameCOMMENT_PHPDOC :
1430 case Scanner.TokenNameCOMMENT_BLOCK :
1431 case Scanner.TokenNameCOMMENT_LINE :
1432 boolean endOfLine = false;
1433 int currentCommentOffset = getCurrentCommentOffset();
1434 int beginningOfLineSpaces = 0;
1436 currentCommentOffset = getCurrentCommentOffset();
1437 beginningOfLineSpaces = 0;
1438 boolean pendingCarriageReturn = false;
1439 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1440 char currentCharacter = source[i];
1441 updateMappedPositions(i);
1442 switch (currentCharacter) {
1444 pendingCarriageReturn = true;
1448 if (pendingCarriageReturn) {
1449 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1451 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1453 pendingCarriageReturn = false;
1454 currentLineBuffer.append(options.lineSeparatorSequence);
1455 beginningOfLineSpaces = 0;
1459 if (pendingCarriageReturn) {
1460 pendingCarriageReturn = false;
1461 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1462 currentLineBuffer.append(options.lineSeparatorSequence);
1463 beginningOfLineSpaces = 0;
1467 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1468 beginningOfLineSpaces += options.tabSize;
1469 if (beginningOfLineSpaces > currentCommentOffset) {
1470 currentLineBuffer.append(currentCharacter);
1472 increaseGlobalDelta(-1);
1475 currentLineBuffer.append(currentCharacter);
1479 if (pendingCarriageReturn) {
1480 pendingCarriageReturn = false;
1481 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1482 currentLineBuffer.append(options.lineSeparatorSequence);
1483 beginningOfLineSpaces = 0;
1487 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1488 beginningOfLineSpaces++;
1489 if (beginningOfLineSpaces > currentCommentOffset) {
1490 currentLineBuffer.append(currentCharacter);
1492 increaseGlobalDelta(-1);
1495 currentLineBuffer.append(currentCharacter);
1499 if (pendingCarriageReturn) {
1500 pendingCarriageReturn = false;
1501 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1502 currentLineBuffer.append(options.lineSeparatorSequence);
1503 beginningOfLineSpaces = 0;
1506 beginningOfLineSpaces = 0;
1507 currentLineBuffer.append(currentCharacter);
1512 updateMappedPositions(scanner.currentPosition - 1);
1513 multipleLineCommentCounter++;
1516 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1517 char currentCharacter = source[i];
1518 updateMappedPositions(i);
1519 currentLineBuffer.append(currentCharacter);
1525 * Outputs <code>currentString</code>:<br>
1526 * <ul><li>If its length is < maxLineLength, output
1527 * <li>Otherwise it is split.</ul>
1528 * @param currentString string to output
1529 * @param preIndented whether the string to output was pre-indented
1530 * @param depth number of indentation to put in front of <code>currentString</code>
1531 * @param operator value of the operator belonging to <code>currentString</code>.
1533 private void outputLine(
1534 String currentString,
1535 boolean preIndented,
1539 int[] startSubstringIndexes,
1540 int offsetInGlobalLine) {
1542 boolean emptyFirstSubString = false;
1543 String operatorString = operatorString(operator);
1544 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1545 boolean placeOperatorAhead = !placeOperatorBehind;
1547 // dump prefix operator?
1548 if (placeOperatorAhead) {
1553 if (operator != 0) {
1554 if (insertSpaceBefore(operator)) {
1555 formattedSource.append(' ');
1556 increaseSplitDelta(1);
1558 formattedSource.append(operatorString);
1559 increaseSplitDelta(operatorString.length());
1561 if (insertSpaceAfter(operator) // && operator != TokenNameimplements
1562 && operator != TokenNameextends) {
1563 // && operator != TokenNamethrows) {
1564 formattedSource.append(' ');
1565 increaseSplitDelta(1);
1569 SplitLine splitLine = null;
1570 if (options.maxLineLength == 0
1571 || getLength(currentString, depth) < options.maxLineLength
1572 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1574 // depending on the type of operator, outputs new line before of after dumping it
1575 // indent before postfix operator
1576 // indent also when the line cannot be split
1577 if (operator == TokenNameextends) {
1578 // || operator == TokenNameimplements
1579 // || operator == TokenNamethrows) {
1580 formattedSource.append(' ');
1581 increaseSplitDelta(1);
1583 if (placeOperatorBehind) {
1588 int max = currentString.length();
1589 if (multipleLineCommentCounter != 0) {
1591 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1592 String line = reader.readLine();
1593 while (line != null) {
1594 updateMappedPositionsWhileSplitting(
1595 beginningOfLineIndex,
1596 beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length);
1597 formattedSource.append(line);
1598 beginningOfLineIndex = beginningOfLineIndex + line.length();
1599 if ((line = reader.readLine()) != null) {
1600 formattedSource.append(options.lineSeparatorSequence);
1601 beginningOfLineIndex += options.lineSeparatorSequence.length;
1602 dumpTab(currentLineIndentationLevel);
1606 } catch (IOException e) {
1607 e.printStackTrace();
1610 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1611 for (int i = 0; i < max; i++) {
1612 char currentChar = currentString.charAt(i);
1613 switch (currentChar) {
1618 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
1619 // a substring cannot end with a lineSeparatorSequence,
1620 // except if it has been added by format() after a one-line comment
1621 formattedSource.append(options.lineSeparatorSequence);
1623 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1628 formattedSource.append(currentChar);
1632 // update positions inside the mappedPositions table
1633 if (substringIndex != -1) {
1634 if (multipleLineCommentCounter == 0) {
1635 int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1636 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1639 // compute the splitDelta resulting with the operator and blank removal
1640 if (substringIndex + 1 != startSubstringIndexes.length) {
1641 increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1644 // dump postfix operator?
1645 if (placeOperatorBehind) {
1646 if (insertSpaceBefore(operator)) {
1647 formattedSource.append(' ');
1648 if (operator != 0) {
1649 increaseSplitDelta(1);
1652 formattedSource.append(operatorString);
1653 if (operator != 0) {
1654 increaseSplitDelta(operatorString.length());
1659 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1660 // extends has to stand alone on a line when currentString has been split.
1661 if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1662 // || operator == TokenNameimplements
1663 // || operator == TokenNamethrows)) {
1664 formattedSource.append(options.lineSeparatorSequence);
1665 increaseSplitDelta(options.lineSeparatorSequence.length);
1668 if (operator == TokenNameextends) {
1669 // || operator == TokenNameimplements
1670 // || operator == TokenNamethrows) {
1671 formattedSource.append(' ');
1672 increaseSplitDelta(1);
1675 // perform actual splitting
1676 String result[] = splitLine.substrings;
1677 int[] splitOperators = splitLine.operators;
1679 if (result[0].length() == 0) {
1680 // when the substring 0 is null, the substring 1 is correctly indented.
1682 emptyFirstSubString = true;
1684 // the operator going in front of the result[0] string is the operator parameter
1685 for (int i = 0, max = result.length; i < max; i++) {
1686 // the new depth is the current one if this is the first substring,
1687 // the current one + 1 otherwise.
1688 // if the substring is a comment, use the current indentation Level instead of the depth
1689 // (-1 because the ouputline increases depth).
1690 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
1691 String currentResult = result[i];
1693 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1694 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1695 || currentResult.startsWith("//")) //$NON-NLS-1$
1696 ? indentationLevel - 1 : depth;
1699 i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1700 i == 0 ? newDepth : newDepth + 1,
1703 splitLine.startSubstringsIndexes,
1704 currentString.indexOf(currentResult));
1706 formattedSource.append(options.lineSeparatorSequence);
1707 increaseSplitDelta(options.lineSeparatorSequence.length);
1711 if (result.length == splitOperators.length - 1) {
1712 int lastOperator = splitOperators[result.length];
1713 String lastOperatorString = operatorString(lastOperator);
1714 formattedSource.append(options.lineSeparatorSequence);
1715 increaseSplitDelta(options.lineSeparatorSequence.length);
1717 if (breakLineBeforeOperator(lastOperator)) {
1719 if (lastOperator != 0) {
1720 if (insertSpaceBefore(lastOperator)) {
1721 formattedSource.append(' ');
1722 increaseSplitDelta(1);
1724 formattedSource.append(lastOperatorString);
1725 increaseSplitDelta(lastOperatorString.length());
1727 if (insertSpaceAfter(lastOperator) // && lastOperator != TokenNameimplements
1728 && lastOperator != TokenNameextends) {
1729 // && lastOperator != TokenNamethrows) {
1730 formattedSource.append(' ');
1731 increaseSplitDelta(1);
1736 if (placeOperatorBehind) {
1737 if (insertSpaceBefore(operator)) {
1738 formattedSource.append(' ');
1739 increaseSplitDelta(1);
1741 formattedSource.append(operatorString);
1742 //increaseSplitDelta(operatorString.length());
1747 * Pops the top statement of the stack if it is <code>token</code>
1749 private int pop(int token) {
1751 if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1753 constructionsCount--;
1759 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1761 private int popBlock() {
1763 if ((constructionsCount > 0)
1764 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1765 if (constructions[constructionsCount - 1] == BLOCK)
1767 constructionsCount--;
1773 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1774 * Does not remove <code>token</code> from the stack.
1775 * @param token the token to be left as the top of the stack
1777 private int popExclusiveUntil(int token) {
1779 int startCount = constructionsCount;
1780 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1781 if (constructions[i] != NONINDENT_BLOCK)
1783 constructionsCount--;
1789 * Pops elements until the stack is empty or the top element is
1790 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1791 * Does not remove it from the stack.
1793 private int popExclusiveUntilBlock() {
1794 int startCount = constructionsCount;
1796 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1797 constructionsCount--;
1804 * Pops elements until the stack is empty or the top element is
1805 * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1806 * Does not remove it from the stack.
1808 private int popExclusiveUntilBlockOrCase() {
1809 int startCount = constructionsCount;
1811 for (int i = startCount - 1;
1812 i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK && constructions[i] != TokenNamecase;
1814 constructionsCount--;
1821 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1822 * Removes <code>token</code> from the stack too.
1823 * @param token the token to remove from the stack
1825 private int popInclusiveUntil(int token) {
1826 int startCount = constructionsCount;
1828 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1829 if (constructions[i] != NONINDENT_BLOCK)
1831 constructionsCount--;
1833 if (constructionsCount > 0) {
1834 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1836 constructionsCount--;
1842 * Pops elements until the stack is empty or the top element is
1843 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1844 * Does not remove it from the stack.
1846 private int popInclusiveUntilBlock() {
1847 int startCount = constructionsCount;
1849 for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1851 constructionsCount--;
1853 if (constructionsCount > 0) {
1854 if (constructions[constructionsCount - 1] == BLOCK)
1856 constructionsCount--;
1862 * Pushes a block in the stack.<br>
1863 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
1864 * pushes <code>NONINDENT_BLOCK</code> otherwise.
1865 * Creates a new bigger array if the current one is full.
1867 private int pushBlock() {
1869 if (constructionsCount == constructions.length)
1870 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1872 if ((constructionsCount == 0)
1873 || (constructions[constructionsCount - 1] == BLOCK)
1874 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1875 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1877 constructions[constructionsCount++] = BLOCK;
1879 constructions[constructionsCount++] = NONINDENT_BLOCK;
1885 * Pushes <code>token</code>.<br>
1886 * Creates a new bigger array if the current one is full.
1888 private int pushControlStatement(int token) {
1889 if (constructionsCount == constructions.length)
1890 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1891 constructions[constructionsCount++] = token;
1895 private static boolean separateFirstArgumentOn(int currentToken) {
1896 //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
1897 return currentToken != TokenNameif
1898 && currentToken != TokenNameLPAREN
1899 && currentToken != TokenNameNOT
1900 && currentToken != TokenNamewhile
1901 && currentToken != TokenNamefor
1902 && currentToken != TokenNameswitch;
1906 * Set the positions to map. The mapped positions should be retrieved using the
1907 * getMappedPositions() method.
1908 * @param positions int[]
1909 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1911 * @see #getMappedPositions()
1913 public void setPositionsToMap(int[] positions) {
1914 positionsToMap = positions;
1917 mappedPositions = new int[positions.length];
1921 * Appends a space character to the current line buffer.
1923 private void space() {
1924 currentLineBuffer.append(' ');
1925 increaseLineDelta(1);
1929 * Splits <code>stringToSplit</code> on the top level token<br>
1930 * If there are several identical token at the same level,
1931 * the string is cut into many pieces.
1932 * @return an object containing the operator and all the substrings
1933 * or null if the string cannot be split
1935 public SplitLine split(String stringToSplit) {
1936 return split(stringToSplit, 0);
1940 * Splits <code>stringToSplit</code> on the top level token<br>
1941 * If there are several identical token at the same level,
1942 * the string is cut into many pieces.
1943 * @return an object containing the operator and all the substrings
1944 * or null if the string cannot be split
1946 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1948 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
1949 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1951 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1955 int currentToken = 0;
1956 int splitTokenType = 0;
1957 int splitTokenDepth = Integer.MAX_VALUE;
1958 int splitTokenPriority = Integer.MAX_VALUE;
1960 int[] substringsStartPositions = new int[10];
1961 // contains the start position of substrings
1962 int[] substringsEndPositions = new int[10];
1963 // contains the start position of substrings
1964 int substringsCount = 1; // index in the substringsStartPosition array
1965 int[] splitOperators = new int[10];
1966 // contains the start position of substrings
1967 int splitOperatorsCount = 0; // index in the substringsStartPosition array
1968 int[] openParenthesisPosition = new int[10];
1969 int openParenthesisPositionCount = 0;
1971 int lastOpenParenthesisPosition = -1;
1972 // used to remember the position of the 1st open parenthesis
1973 // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
1974 // setup the scanner with a new source
1975 int lastCommentStartPosition = -1;
1976 // to remember the start position of the last comment
1977 int firstTokenOnLine = -1;
1978 // to remember the first token of the line
1979 int previousToken = -1;
1980 // to remember the previous token.
1981 splitScanner.setSource(stringToSplit.toCharArray());
1986 // takes the next token
1988 if (currentToken != Scanner.TokenNameWHITESPACE)
1989 previousToken = currentToken;
1990 currentToken = splitScanner.getNextToken();
1991 if (Scanner.DEBUG) {
1992 int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
1993 int currentStartPosition = splitScanner.getCurrentTokenStartPosition();
1995 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
1996 System.out.println(scanner.toStringAction(currentToken));
1998 } catch (InvalidInputException e) {
1999 if (!handleInvalidToken(e))
2002 // this value is not modify when an exception is raised.
2004 if (currentToken == TokenNameEOF)
2007 if (firstTokenOnLine == -1) {
2008 firstTokenOnLine = currentToken;
2010 switch (currentToken) {
2011 case TokenNameRBRACE :
2012 case TokenNameRPAREN :
2013 if (openParenthesisPositionCount > 0) {
2014 if (openParenthesisPositionCount == 1 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2015 lastOpenParenthesisPosition = openParenthesisPosition[0];
2017 (splitTokenDepth == Integer.MAX_VALUE)
2018 || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2020 splitTokenDepth = openParenthesisPositionCount;
2021 splitTokenPriority = Integer.MAX_VALUE;
2022 substringsStartPositions[0] = 0;
2023 // better token means the whole line until now is the first substring
2024 substringsCount = 1; // resets the count of substrings
2025 substringsEndPositions[0] = openParenthesisPosition[0];
2026 // substring ends on operator start
2027 position = openParenthesisPosition[0];
2028 // the string mustn't be cut before the closing parenthesis but after the opening one.
2029 splitOperatorsCount = 1; // resets the count of split operators
2030 splitOperators[0] = 0;
2032 openParenthesisPositionCount--;
2035 case TokenNameLBRACE :
2036 case TokenNameLPAREN :
2037 if (openParenthesisPositionCount == openParenthesisPosition.length) {
2039 openParenthesisPosition,
2041 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2043 openParenthesisPositionCount);
2045 openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2046 if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) {
2047 openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2050 case TokenNameSEMICOLON : // ;
2051 case TokenNameCOMMA : // ,
2052 case TokenNameEQUAL : // =
2053 if (openParenthesisPositionCount < splitTokenDepth
2054 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2055 // the current token is better than the one we currently have
2056 // (in level or in priority if same level)
2057 // reset the substringsCount
2058 splitTokenDepth = openParenthesisPositionCount;
2059 splitTokenType = currentToken;
2060 splitTokenPriority = getTokenPriority(currentToken);
2061 substringsStartPositions[0] = 0;
2062 // better token means the whole line until now is the first substring
2064 if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2065 substringsCount = 2; // resets the count of substrings
2067 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2068 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2069 substringsEndPositions[1] = splitScanner.startPosition;
2070 splitOperatorsCount = 2; // resets the count of split operators
2071 splitOperators[0] = 0;
2072 splitOperators[1] = currentToken;
2073 position = splitScanner.currentPosition;
2074 // next substring will start from operator end
2076 substringsCount = 1; // resets the count of substrings
2078 substringsEndPositions[0] = splitScanner.startPosition;
2079 // substring ends on operator start
2080 position = splitScanner.currentPosition;
2081 // next substring will start from operator end
2082 splitOperatorsCount = 1; // resets the count of split operators
2083 splitOperators[0] = currentToken;
2086 if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2087 && splitTokenType != TokenNameEQUAL
2088 && currentToken != TokenNameEQUAL) {
2089 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
2090 // take only the 1st = into account.
2091 // if another token with the same priority is found,
2092 // push the start position of the substring and
2093 // push the token into the stack.
2094 // create a new array object if the current one is full.
2095 if (substringsCount == substringsStartPositions.length) {
2097 substringsStartPositions,
2099 (substringsStartPositions = new int[substringsCount * 2]),
2103 substringsEndPositions,
2105 (substringsEndPositions = new int[substringsCount * 2]),
2109 if (splitOperatorsCount == splitOperators.length) {
2110 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2112 substringsStartPositions[substringsCount] = position;
2113 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2114 // substring ends on operator start
2115 position = splitScanner.currentPosition;
2116 // next substring will start from operator end
2117 splitOperators[splitOperatorsCount++] = currentToken;
2122 case TokenNameCOLON : // : (15.24)
2123 // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
2124 // indeed it might appear also behind a case statement, and we do not to break at this point.
2125 if ((splitOperatorsCount == 0) || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2128 case TokenNameextends :
2129 // case TokenNameimplements :
2130 // case TokenNamethrows :
2132 case TokenNameDOT : // .
2133 case TokenNameMULTIPLY : // * (15.16.1)
2134 case TokenNameDIVIDE : // / (15.16.2)
2135 case TokenNameREMAINDER : // % (15.16.3)
2136 case TokenNamePLUS : // + (15.17, 15.17.2)
2137 case TokenNameMINUS : // - (15.17.2)
2138 case TokenNameLEFT_SHIFT : // << (15.18)
2139 case TokenNameRIGHT_SHIFT : // >> (15.18)
2140 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2141 case TokenNameLESS : // < (15.19.1)
2142 case TokenNameLESS_EQUAL : // <= (15.19.1)
2143 case TokenNameGREATER : // > (15.19.1)
2144 case TokenNameGREATER_EQUAL : // >= (15.19.1)
2145 // case TokenNameinstanceof : // instanceof
2146 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2147 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2148 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
2149 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
2150 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
2151 case TokenNameAND_AND : // && (15.22)
2152 case TokenNameOR_OR : // || (15.23)
2153 case TokenNameQUESTION : // ? (15.24)
2154 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
2155 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
2156 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
2157 case TokenNamePLUS_EQUAL : // += (15.25.2)
2158 case TokenNameMINUS_EQUAL : // -= (15.25.2)
2159 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
2160 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
2161 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2162 case TokenNameAND_EQUAL : // &= (15.25.2)
2163 case TokenNameXOR_EQUAL : // ^= (15.25.2)
2164 case TokenNameOR_EQUAL : // |= (15.25.2)
2166 if ((openParenthesisPositionCount < splitTokenDepth
2167 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2168 && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS)
2169 && (previousToken == TokenNameLBRACE || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2170 // the current token is better than the one we currently have
2171 // (in level or in priority if same level)
2172 // reset the substringsCount
2173 splitTokenDepth = openParenthesisPositionCount;
2174 splitTokenType = currentToken;
2175 splitTokenPriority = getTokenPriority(currentToken);
2176 substringsStartPositions[0] = 0;
2177 // better token means the whole line until now is the first substring
2179 if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2180 substringsCount = 2; // resets the count of substrings
2182 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2183 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2184 substringsEndPositions[1] = splitScanner.startPosition;
2185 splitOperatorsCount = 3; // resets the count of split operators
2186 splitOperators[0] = 0;
2187 splitOperators[1] = 0;
2188 splitOperators[2] = currentToken;
2189 position = splitScanner.currentPosition;
2190 // next substring will start from operator end
2192 substringsCount = 1; // resets the count of substrings
2194 substringsEndPositions[0] = splitScanner.startPosition;
2195 // substring ends on operator start
2196 position = splitScanner.currentPosition;
2197 // next substring will start from operator end
2198 splitOperatorsCount = 2; // resets the count of split operators
2199 splitOperators[0] = 0;
2200 // nothing for first operand since operator will be inserted in front of the second operand
2201 splitOperators[1] = currentToken;
2205 if (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken)) {
2206 // if another token with the same priority is found,
2207 // push the start position of the substring and
2208 // push the token into the stack.
2209 // create a new array object if the current one is full.
2210 if (substringsCount == substringsStartPositions.length) {
2212 substringsStartPositions,
2214 (substringsStartPositions = new int[substringsCount * 2]),
2218 substringsEndPositions,
2220 (substringsEndPositions = new int[substringsCount * 2]),
2224 if (splitOperatorsCount == splitOperators.length) {
2225 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2227 substringsStartPositions[substringsCount] = position;
2228 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2229 // substring ends on operator start
2230 position = splitScanner.currentPosition;
2231 // next substring will start from operator end
2232 splitOperators[splitOperatorsCount++] = currentToken;
2238 if (isComment(currentToken)) {
2239 lastCommentStartPosition = splitScanner.startPosition;
2241 lastCommentStartPosition = -1;
2244 } catch (InvalidInputException e) {
2247 // if the string cannot be split, return null.
2248 if (splitOperatorsCount == 0)
2251 // ## SPECIAL CASES BEGIN
2252 if (((splitOperatorsCount == 2
2253 && splitOperators[1] == TokenNameDOT
2254 && splitTokenDepth == 0
2255 && lastOpenParenthesisPosition > -1)
2256 || (splitOperatorsCount > 2
2257 && splitOperators[1] == TokenNameDOT
2258 && splitTokenDepth == 0
2259 && lastOpenParenthesisPosition > -1
2260 && lastOpenParenthesisPosition <= options.maxLineLength)
2261 || (separateFirstArgumentOn(firstTokenOnLine) && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2262 && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2263 // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
2264 // only one split on a top level .
2265 // or more than one split on . and substring before open parenthesis fits one line.
2266 // or split inside parenthesis and first token is not a for/while/if
2267 SplitLine sl = split(stringToSplit.substring(lastOpenParenthesisPosition), lastOpenParenthesisPosition);
2268 if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2269 // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
2270 return new SplitLine(
2273 stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2274 stringToSplit.substring(lastOpenParenthesisPosition)},
2275 new int[] { offsetInGlobalLine, lastOpenParenthesisPosition + offsetInGlobalLine });
2277 // right substring can be split and is split on comma
2278 // copy substrings and operators
2279 // except if the 1st string is empty.
2280 int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2281 int subStringsLength = sl.substrings.length + 1 - startIndex;
2282 String[] result = new String[subStringsLength];
2283 int[] startIndexes = new int[subStringsLength];
2284 int operatorsLength = sl.operators.length + 1 - startIndex;
2285 int[] operators = new int[operatorsLength];
2287 result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2290 System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes, 1, subStringsLength - 1);
2291 for (int i = subStringsLength - 1; i >= 0; i--) {
2292 startIndexes[i] += offsetInGlobalLine;
2294 System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1);
2295 System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1);
2297 return new SplitLine(operators, result, startIndexes);
2300 // if the last token is a comment and the substring before the comment fits on a line,
2301 // split before the comment and return the result.
2302 if (lastCommentStartPosition > -1 && lastCommentStartPosition < options.maxLineLength && splitTokenPriority > 50) {
2303 int end = lastCommentStartPosition;
2304 int start = lastCommentStartPosition;
2305 if (stringToSplit.charAt(end - 1) == ' ') {
2308 if (start != end && stringToSplit.charAt(start) == ' ') {
2311 return new SplitLine(
2313 new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)},
2314 new int[] { 0, start });
2316 if (position != stringToSplit.length()) {
2317 if (substringsCount == substringsStartPositions.length) {
2319 substringsStartPositions,
2321 (substringsStartPositions = new int[substringsCount * 2]),
2324 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount * 2]), 0, substringsCount);
2326 // avoid empty extra substring, e.g. line terminated with a semi-colon
2327 substringsStartPositions[substringsCount] = position;
2328 substringsEndPositions[substringsCount++] = stringToSplit.length();
2330 if (splitOperatorsCount == splitOperators.length) {
2331 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2333 splitOperators[splitOperatorsCount] = 0;
2335 // the last element of the stack is the position of the end of StringToSPlit
2336 // +1 because the substring method excludes the last character
2337 String[] result = new String[substringsCount];
2338 for (int i = 0; i < substringsCount; i++) {
2339 int start = substringsStartPositions[i];
2340 int end = substringsEndPositions[i];
2341 if (stringToSplit.charAt(start) == ' ') {
2343 substringsStartPositions[i]++;
2345 if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2348 result[i] = stringToSplit.substring(start, end);
2349 substringsStartPositions[i] += offsetInGlobalLine;
2351 if (splitOperatorsCount > substringsCount) {
2352 System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[splitOperatorsCount]), 0, substringsCount);
2353 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[splitOperatorsCount]), 0, substringsCount);
2354 for (int i = substringsCount; i < splitOperatorsCount; i++) {
2355 substringsStartPositions[i] = position;
2356 substringsEndPositions[i] = position;
2358 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount]), 0, splitOperatorsCount);
2360 System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[substringsCount]), 0, substringsCount);
2361 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount]), 0, substringsCount);
2362 System.arraycopy(splitOperators, 0, (splitOperators = new int[substringsCount]), 0, substringsCount);
2364 SplitLine splitLine = new SplitLine(splitOperators, result, substringsStartPositions);
2368 private void updateMappedPositions(int startPosition) {
2369 if (positionsToMap == null) {
2372 char[] source = scanner.source;
2373 int sourceLength = source.length;
2374 while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2375 int posToMap = positionsToMap[indexToMap];
2376 if (posToMap < 0 || posToMap >= sourceLength) {
2377 // protection against out of bounds position
2378 if (posToMap == sourceLength) {
2379 mappedPositions[indexToMap] = formattedSource.length();
2381 indexToMap = positionsToMap.length; // no more mapping
2384 if (CharOperation.isWhitespace(source[posToMap])) {
2385 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2387 if (posToMap == sourceLength - 1) {
2388 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2390 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2397 private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2398 if (mappedPositions == null || mappedPositions.length == indexInMap)
2401 while (indexInMap < mappedPositions.length
2402 && startPosition <= mappedPositions[indexInMap]
2403 && mappedPositions[indexInMap] < endPosition
2404 && indexInMap < indexToMap) {
2405 mappedPositions[indexInMap] += splitDelta;
2410 private int getLength(String s, int tabDepth) {
2412 for (int i = 0; i < tabDepth; i++) {
2413 length += options.tabSize;
2415 for (int i = 0, max = s.length(); i < max; i++) {
2416 char currentChar = s.charAt(i);
2417 switch (currentChar) {
2419 length += options.tabSize;
2429 * Sets the initial indentation level
2430 * @param indentationLevel new indentation level
2434 public void setInitialIndentationLevel(int newIndentationLevel) {
2435 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;