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;
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.StringReader;
15 import java.util.Hashtable;
16 import java.util.Locale;
18 import net.sourceforge.phpdt.core.ICodeFormatter;
19 import net.sourceforge.phpdt.core.compiler.CharOperation;
20 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
21 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
22 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
23 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
24 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
25 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
27 * <h2>How to format a piece of code ?</h2>
29 * <li>Create an instance of <code>CodeFormatter</code>
30 * <li>Use the method <code>void format(aString)</code> on this instance to
31 * format <code>aString</code>. It will return the formatted string.
34 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
35 public FormatterOptions options;
37 * Represents a block in the <code>constructions</code> stack.
39 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
41 * Represents a block following a control statement in the <code>constructions</code>
44 public static final int NONINDENT_BLOCK = -100;
46 * Contains the formatted output.
48 StringBuffer formattedSource;
50 * Contains the current line. <br>
51 * Will be dumped at the next "newline"
53 StringBuffer currentLineBuffer;
55 * Used during the formatting to get each token.
59 * Contains the tokens responsible for the current indentation level and the
60 * blocks not closed yet.
62 private int[] constructions;
64 * Index in the <code>constructions</code> array.
66 private int constructionsCount;
68 * Level of indentation of the current token (number of tab char put in front
71 private int indentationLevel;
73 * Regular level of indentation of all the lines
75 private int initialIndentationLevel;
77 * Used to split a line.
81 * To remember the offset between the beginning of the line and the beginning
84 int currentCommentOffset;
85 int currentLineIndentationLevel;
87 private boolean containsOpenCloseBraces;
88 private int indentationLevelForOpenCloseBraces;
90 * Collections of positions to map
92 private int[] positionsToMap;
94 * Collections of mapped positions
96 private int[] mappedPositions;
97 private int indexToMap;
98 private int indexInMap;
99 private int globalDelta;
100 private int lineDelta;
101 private int splitDelta;
102 private int beginningOfLineIndex;
103 private int multipleLineCommentCounter;
105 * Creates a new instance of Code Formatter using the given settings.
107 * @deprecated backport 1.0 internal functionality
109 public CodeFormatter(ConfigurableOption[] settings) {
110 this(convertConfigurableOptions(settings));
113 * Creates a new instance of Code Formatter using the FormattingOptions
114 * object given as argument
116 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
118 public CodeFormatter() {
122 * Creates a new instance of Code Formatter using the given settings.
124 public CodeFormatter(Map settings) {
125 // initialize internal state
126 constructionsCount = 0;
127 constructions = new int[10];
128 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
129 currentCommentOffset = -1;
130 // initialize primary and secondary scanners
131 scanner = new Scanner(true /* comment */
132 , true /* whitespace */
135 ); // regular scanner for forming lines
136 scanner.recordLineSeparator = true;
137 // to remind of the position of the beginning of the line.
138 splitScanner = new Scanner(true /* comment */
139 , true /* whitespace */
143 // secondary scanner to split long lines formed by primary scanning
144 // initialize current line buffer
145 currentLineBuffer = new StringBuffer();
146 this.options = new FormatterOptions(settings);
149 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
152 private static boolean breakLineBeforeOperator(int operator) {
154 case TokenNameCOMMA :
155 case TokenNameSEMICOLON :
156 case TokenNameEQUAL :
163 * @deprecated backport 1.0 internal functionality
165 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
166 Hashtable options = new Hashtable(10);
167 for (int i = 0; i < settings.length; i++) {
168 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
169 String optionName = settings[i].getOptionName();
170 int valueIndex = settings[i].getCurrentValueIndex();
171 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
173 "net.sourceforge.phpdt.core.formatter.newline.openingBrace",
174 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
175 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
177 "net.sourceforge.phpdt.core.formatter.newline.controlStatement",
178 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
179 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
180 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll",
181 valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
182 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
183 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf",
184 valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
185 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
187 "net.sourceforge.phpdt.core.formatter.newline.emptyBlock",
188 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
189 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
190 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String
191 .valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
192 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
193 options.put("net.sourceforge.phpdt.core.formatter.style.assignment",
194 valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
195 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
196 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char",
197 valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
198 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
199 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size",
200 String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
207 * Returns the end of the source code.
209 private final String copyRemainingSource() {
210 char str[] = scanner.source;
211 int startPosition = scanner.startPosition;
212 int length = str.length - startPosition;
213 StringBuffer bufr = new StringBuffer(length);
214 if (startPosition < str.length) {
215 bufr.append(str, startPosition, length);
217 return (bufr.toString());
220 * Inserts <code>tabCount</code> tab character or their equivalent number
223 private void dumpTab(int tabCount) {
224 if (options.indentWithTab) {
225 for (int j = 0; j < tabCount; j++) {
226 formattedSource.append('\t');
227 increaseSplitDelta(1);
230 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
231 formattedSource.append(' ');
232 increaseSplitDelta(1);
237 * Dumps <code>currentLineBuffer</code> into the formatted string.
239 private void flushBuffer() {
240 String currentString = currentLineBuffer.toString();
242 beginningOfLineIndex = formattedSource.length();
243 if (containsOpenCloseBraces) {
244 containsOpenCloseBraces = false;
245 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0,
247 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
249 outputLine(currentString, false, currentLineIndentationLevel, 0, -1,
252 int scannerSourceLength = scanner.source.length;
253 if (scannerSourceLength > 2) {
254 if (scanner.source[scannerSourceLength - 1] == '\n'
255 && scanner.source[scannerSourceLength - 2] == '\r') {
256 formattedSource.append(options.lineSeparatorSequence);
257 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
258 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
259 formattedSource.append(options.lineSeparatorSequence);
260 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
261 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
262 formattedSource.append(options.lineSeparatorSequence);
263 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
266 updateMappedPositions(scanner.startPosition);
269 * Formats the input string.
271 private void format() {
273 int previousToken = 0;
274 int previousCompilableToken = 0;
275 int indentationOffset = 0;
276 int newLinesInWhitespace = 0;
277 // number of new lines in the previous whitespace token
278 // (used to leave blank lines before comments)
279 int pendingNewLines = 0;
280 boolean expectingOpenBrace = false;
281 boolean clearNonBlockIndents = false;
282 // true if all indentations till the 1st { (usefull after } or ;)
283 boolean pendingSpace = true;
284 boolean pendingNewlineAfterParen = false;
285 // true when a cr is to be put after a ) (in conditional statements)
286 boolean inAssignment = false;
287 boolean inArrayAssignment = false;
288 boolean inThrowsClause = false;
289 boolean inClassOrInterfaceHeader = false;
290 int dollarBraceCount = 0;
291 // openBracketCount is used to count the number of open brackets not closed
293 int openBracketCount = 0;
294 int unarySignModifier = 0;
295 // openParenthesis[0] is used to count the parenthesis not belonging to a
297 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
298 int openParenthesisCount = 1;
299 int[] openParenthesis = new int[10];
300 // tokenBeforeColon is used to know what token goes along with the current
302 // it can be case or ?
303 int tokenBeforeColonCount = 0;
304 int[] tokenBeforeColon = new int[10];
305 constructionsCount = 0; // initializes the constructions count.
306 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
308 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
309 boolean specialElse = false;
310 // OPTION (IndentationLevel): initial indentation level may be non-zero.
311 currentLineIndentationLevel += constructionsCount;
312 // An InvalidInputException exception might cause the termination of this
316 // Get the next token. Catch invalid input and output it
317 // with minimal formatting, also catch end of input and
320 token = scanner.getNextToken();
322 int currentEndPosition = scanner.getCurrentTokenEndPosition();
323 int currentStartPosition = scanner.getCurrentTokenStartPosition();
324 System.out.print(currentStartPosition + "," + currentEndPosition
326 System.out.println(scanner.toStringAction(token));
328 // Patch for line comment
329 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
330 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
331 int length = scanner.currentPosition;
332 loop : for (int index = length - 1; index >= 0; index--) {
333 switch (scanner.source[index]) {
336 scanner.currentPosition--;
343 } catch (InvalidInputException e) {
344 if (!handleInvalidToken(e)) {
349 if (token == Scanner.TokenNameEOF)
352 * ## MODIFYING the indentation level before generating new lines and
353 * indentation in the output string
355 // Removes all the indentations made by statements not followed by a
357 // except if the current token is ELSE, CATCH or if we are in a
359 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
362 if (constructionsCount > 0
363 && constructions[constructionsCount - 1] == TokenNameelse) {
367 indentationLevel += popInclusiveUntil(TokenNameif);
369 // case TokenNamecatch :
370 // indentationLevel += popInclusiveUntil(TokenNamecatch);
372 // case TokenNamefinally :
373 // indentationLevel += popInclusiveUntil(TokenNamecatch);
375 case TokenNamewhile :
376 if (nlicsToken == TokenNamedo) {
377 indentationLevel += pop(TokenNamedo);
381 indentationLevel += popExclusiveUntilBlockOrCase();
382 // clear until a CASE, DEFAULT or BLOCK is encountered.
383 // Thus, the indentationLevel is correctly cleared either
384 // in a switch/case statement or in any other situation.
386 clearNonBlockIndents = false;
388 // returns to the indentation level created by the SWITCH keyword
389 // if the current token is a CASE or a DEFAULT
390 if (token == TokenNamecase || token == TokenNamedefault) {
391 indentationLevel += pop(TokenNamecase);
393 // if (token == Scanner.TokenNamethrows) {
394 // inThrowsClause = true;
396 if ((token == Scanner.TokenNameclass // || token ==
397 // Scanner.TokenNameinterface
398 ) && previousToken != Scanner.TokenNameDOT) {
399 inClassOrInterfaceHeader = true;
402 * ## APPEND newlines and indentations to the output string
404 // Do not add a new line between ELSE and IF, if the option
405 // elseIfOnSameLine is true.
406 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
407 // if (pendingNewlineAfterParen
408 // && previousCompilableToken == TokenNameelse
409 // && token == TokenNameif
410 // && options.compactElseIfMode) {
411 // pendingNewlineAfterParen = false;
412 // pendingNewLines = 0;
413 // indentationLevel += pop(TokenNameelse);
414 // // because else if is now one single statement,
415 // // the indentation level after it is increased by one and not by 2
416 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
418 // Add a newline & indent to the formatted source string if
419 // a for/if-else/while statement was scanned and there is no block
421 pendingNewlineAfterParen = pendingNewlineAfterParen
422 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
423 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
424 pendingNewlineAfterParen = false;
425 // Do to add a newline & indent sequence if the current token is an
426 // open brace or a period or if the current token is a semi-colon and
428 // previous token is a close paren.
429 // add a new line if a parenthesis belonging to a for() statement
430 // has been closed and the current token is not an opening brace
431 if (token != TokenNameLBRACE
433 // to avoid adding new line between else and a comment
434 && token != TokenNameDOT
435 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
437 currentLineIndentationLevel = indentationLevel;
439 pendingSpace = false;
441 if (token == TokenNameLBRACE
442 && options.newLineBeforeOpeningBraceMode) {
444 if (constructionsCount > 0
445 && constructions[constructionsCount - 1] != BLOCK
446 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
447 currentLineIndentationLevel = indentationLevel - 1;
449 currentLineIndentationLevel = indentationLevel;
452 pendingSpace = false;
456 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode
457 && constructionsCount > 0
458 && constructions[constructionsCount - 1] == TokenNamedo) {
460 currentLineIndentationLevel = indentationLevel - 1;
462 pendingSpace = false;
465 if (token == TokenNameLBRACE && inThrowsClause) {
466 inThrowsClause = false;
467 if (options.newLineBeforeOpeningBraceMode) {
469 currentLineIndentationLevel = indentationLevel;
471 pendingSpace = false;
475 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
476 inClassOrInterfaceHeader = false;
477 if (options.newLineBeforeOpeningBraceMode) {
479 currentLineIndentationLevel = indentationLevel;
481 pendingSpace = false;
484 // Add pending new lines to the formatted source string.
485 // Note: pending new lines are not added if the current token
486 // is a single line comment or whitespace.
487 // if the comment is between parenthesis, there is no blank line
489 // (if it's a one-line comment, a blank line is added after it).
490 if (((pendingNewLines > 0 && (!isComment(token)))
491 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
492 && token != Scanner.TokenNameWHITESPACE) {
493 // Do not add newline & indent between an adjoining close brace and
494 // close paren. Anonymous inner classes may use this form.
495 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
496 && token == TokenNameRPAREN;
497 // OPTION (NewLineInCompoundStatement): do not add newline & indent
498 // between close brace and else, (do) while, catch, and finally if
499 // newlineInCompoundStatement is true.
500 boolean nlicsOption = previousToken == TokenNameRBRACE
501 && !options.newlineInControlStatementMode
502 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo));
503 // || token == TokenNamecatch
504 // || token == TokenNamefinally);
505 // Do not add a newline & indent between a close brace and
507 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
508 && token == TokenNameSEMICOLON;
509 // Do not add a new line & indent between a multiline comment and a
511 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
512 && token == TokenNameLBRACE;
513 // Do not add a newline & indent between a close brace and a colon
514 // (in array assignments, for example).
515 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
516 && token == TokenNameCOMMA;
517 // Add a newline and indent, if appropriate.
519 || (!commentAndOpenBrace && !closeBraceAndCloseParen
520 && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
521 // if clearAllBlankLinesMode=false, leaves the blank lines
522 // inserted by the user
523 // if clearAllBlankLinesMode=true, removes all of then
524 // and insert only blank lines required by the formatting.
525 if (!options.clearAllBlankLinesMode) {
526 // (isComment(token))
527 pendingNewLines = (pendingNewLines < newLinesInWhitespace)
528 ? newLinesInWhitespace
530 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
532 if (previousCompilableToken == TokenNameLBRACE
533 && token == TokenNameRBRACE) {
534 containsOpenCloseBraces = true;
535 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
536 if (isComment(previousToken)) {
537 newLine(pendingNewLines);
540 * if (!(constructionsCount > 1 &&
541 * constructions[constructionsCount-1] == NONINDENT_BLOCK &&
542 * (constructions[constructionsCount-2] == TokenNamefor
544 if (options.newLineInEmptyBlockMode) {
545 if (inArrayAssignment) {
546 newLine(1); // array assigment with an empty block
548 newLine(pendingNewLines);
554 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
556 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
557 newLine(pendingNewLines);
560 if (((previousCompilableToken == TokenNameSEMICOLON)
561 || (previousCompilableToken == TokenNameLBRACE)
562 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
563 && (token == TokenNameRBRACE)) {
564 indentationOffset = -1;
565 indentationLevel += popExclusiveUntilBlock();
567 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
569 currentLineIndentationLevel++;
571 currentLineIndentationLevel = indentationLevel
574 pendingSpace = false;
575 indentationOffset = 0;
578 newLinesInWhitespace = 0;
580 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
585 // case TokenNameDOLLAR :
586 // dollarBraceCount++;
589 // case TokenNamefinally :
590 expectingOpenBrace = true;
591 pendingNewlineAfterParen = true;
592 indentationLevel += pushControlStatement(token);
595 case TokenNamedefault :
596 if (tokenBeforeColonCount == tokenBeforeColon.length) {
597 System.arraycopy(tokenBeforeColon, 0,
598 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
599 tokenBeforeColonCount);
601 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
602 indentationLevel += pushControlStatement(TokenNamecase);
604 case TokenNameQUESTION :
605 if (tokenBeforeColonCount == tokenBeforeColon.length) {
606 System.arraycopy(tokenBeforeColon, 0,
607 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
608 tokenBeforeColonCount);
610 tokenBeforeColon[tokenBeforeColonCount++] = token;
612 case TokenNameswitch :
615 case TokenNamewhile :
616 if (openParenthesisCount == openParenthesis.length) {
617 System.arraycopy(openParenthesis, 0,
618 (openParenthesis = new int[openParenthesisCount * 2]), 0,
619 openParenthesisCount);
621 openParenthesis[openParenthesisCount++] = 0;
622 expectingOpenBrace = true;
623 indentationLevel += pushControlStatement(token);
625 // case TokenNametry :
626 // pendingNewlineAfterParen = true;
627 // case TokenNamecatch :
628 // // several CATCH statements can be contiguous.
629 // // a CATCH is encountered pop until first CATCH (if a CATCH
630 // follows a TRY it works the same way,
631 // // as CATCH and TRY are the same token in the stack).
632 // expectingOpenBrace = true;
633 // indentationLevel += pushControlStatement(TokenNamecatch);
636 expectingOpenBrace = true;
637 indentationLevel += pushControlStatement(token);
642 case TokenNameLPAREN :
643 // if (previousToken == TokenNamesynchronized) {
644 // indentationLevel += pushControlStatement(previousToken);
646 // Put a space between the previous and current token if the
647 // previous token was not a keyword, open paren, logical
648 // compliment (eg: !), semi-colon, open brace, close brace,
650 if (previousCompilableToken != TokenNameLBRACKET
651 && previousToken != TokenNameIdentifier && previousToken != 0
652 && previousToken != TokenNameNOT
653 && previousToken != TokenNameLPAREN
654 && previousToken != TokenNameTWIDDLE
655 && previousToken != TokenNameSEMICOLON
656 && previousToken != TokenNameLBRACE
657 && previousToken != TokenNameRBRACE) {
658 // && previousToken != TokenNamesuper
659 // && previousToken != TokenNamethis) {
662 // If in a for/if/while statement, increase the parenthesis count
663 // for the current openParenthesisCount
664 // else increase the count for stand alone parenthesis.
665 if (openParenthesisCount > 0)
666 openParenthesis[openParenthesisCount - 1]++;
668 openParenthesis[0]++;
669 pendingSpace = false;
672 case TokenNameRPAREN :
673 // Decrease the parenthesis count
674 // if there is no more unclosed parenthesis,
675 // a new line and indent may be append (depending on the next
677 if ((openParenthesisCount > 1)
678 && (openParenthesis[openParenthesisCount - 1] > 0)) {
679 openParenthesis[openParenthesisCount - 1]--;
680 if (openParenthesis[openParenthesisCount - 1] <= 0) {
681 pendingNewlineAfterParen = true;
682 inAssignment = false;
683 openParenthesisCount--;
686 openParenthesis[0]--;
688 pendingSpace = false;
690 case TokenNameLBRACE :
691 if (previousCompilableToken == TokenNameDOLLAR) {
694 if ((previousCompilableToken == TokenNameRBRACKET)
695 || (previousCompilableToken == TokenNameEQUAL)) {
696 // if (previousCompilableToken == TokenNameRBRACKET) {
697 inArrayAssignment = true;
698 inAssignment = false;
700 if (inArrayAssignment) {
701 indentationLevel += pushBlock();
703 // Add new line and increase indentation level after open brace.
705 indentationLevel += pushBlock();
708 case TokenNameRBRACE :
709 if (dollarBraceCount > 0) {
713 if (previousCompilableToken == TokenNameRPAREN) {
714 pendingSpace = false;
716 if (inArrayAssignment) {
717 inArrayAssignment = false;
719 indentationLevel += popInclusiveUntilBlock();
722 indentationLevel += popInclusiveUntilBlock();
723 if (previousCompilableToken == TokenNameRPAREN) {
724 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
726 currentLineBuffer.append(options.lineSeparatorSequence);
727 increaseLineDelta(options.lineSeparatorSequence.length);
729 if (constructionsCount > 0) {
730 switch (constructions[constructionsCount - 1]) {
732 //indentationLevel += popExclusiveUntilBlock();
734 case TokenNameswitch :
737 // case TokenNametry :
738 // case TokenNamecatch :
739 // case TokenNamefinally :
740 case TokenNamewhile :
742 // case TokenNamesynchronized :
743 clearNonBlockIndents = true;
750 case TokenNameLBRACKET :
752 pendingSpace = false;
754 case TokenNameRBRACKET :
755 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
756 // if there is no left bracket to close, the right bracket is
758 pendingSpace = false;
760 case TokenNameCOMMA :
762 pendingSpace = false;
764 case TokenNameSEMICOLON :
765 // Do not generate line terminators in the definition of
766 // the for statement.
767 // if not in this case, jump a line and reduce indentation after
769 // if the block it closes belongs to a conditional statement (if,
771 if (openParenthesisCount <= 1) {
773 if (expectingOpenBrace) {
774 clearNonBlockIndents = true;
775 expectingOpenBrace = false;
778 inAssignment = false;
779 pendingSpace = false;
781 case TokenNamePLUS_PLUS :
782 case TokenNameMINUS_MINUS :
783 // Do not put a space between a post-increment/decrement
784 // and the identifier being modified.
785 if (previousToken == TokenNameIdentifier
786 || previousToken == TokenNameRBRACKET) {
787 pendingSpace = false;
791 // previously ADDITION
792 case TokenNameMINUS :
793 // Handle the unary operators plus and minus via a flag
794 if (!isLiteralToken(previousToken)
795 && previousToken != TokenNameIdentifier
796 && previousToken != TokenNameRPAREN
797 && previousToken != TokenNameRBRACKET) {
798 unarySignModifier = 1;
801 case TokenNameCOLON :
802 // In a switch/case statement, add a newline & indent
803 // when a colon is encountered.
804 if (tokenBeforeColonCount > 0) {
805 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
808 tokenBeforeColonCount--;
811 case TokenNameEQUAL :
814 case Scanner.TokenNameCOMMENT_LINE :
817 currentLineIndentationLevel++;
819 break; // a line is always inserted after a one-line comment
820 case Scanner.TokenNameCOMMENT_PHPDOC :
821 case Scanner.TokenNameCOMMENT_BLOCK :
822 currentCommentOffset = getCurrentCommentOffset();
825 case Scanner.TokenNameWHITESPACE :
826 // Count the number of line terminators in the whitespace so
827 // line spacing can be preserved near comments.
828 char[] source = scanner.source;
829 newLinesInWhitespace = 0;
830 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
831 if (source[i] == '\r') {
833 if (source[++i] == '\n') {
834 newLinesInWhitespace++;
836 newLinesInWhitespace++;
839 newLinesInWhitespace++;
841 } else if (source[i] == '\n') {
842 newLinesInWhitespace++;
845 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
847 // case TokenNameHTML :
848 // // Add the next token to the formatted source string.
849 // // outputCurrentToken(token);
850 // int startPosition = scanner.startPosition;
852 // for (int i = startPosition, max = scanner.currentPosition; i <
854 // char currentCharacter = scanner.source[i];
855 // updateMappedPositions(i);
856 // currentLineBuffer.append(currentCharacter);
860 if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
861 // || token == TokenNamesuper
862 // || token == TokenNamethis) {
863 // Do not put a space between a unary operator
864 // (eg: ++, --, +, -) and the identifier being modified.
865 if (previousToken == TokenNamePLUS_PLUS
866 || previousToken == TokenNameMINUS_MINUS
867 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
868 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
869 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
870 pendingSpace = false;
872 unarySignModifier = 0;
876 // Do not output whitespace tokens.
877 if (token != Scanner.TokenNameWHITESPACE) {
879 * Add pending space to the formatted source string. Do not output a
880 * space under the following circumstances: 1) this is the first pass 2)
881 * previous token is an open paren 3) previous token is a period 4)
882 * previous token is the logical compliment (eg: !) 5) previous token
883 * is the bitwise compliment (eg: ~) 6) previous token is the open
884 * bracket (eg: [) 7) in an assignment statement, if the previous
885 * token is an open brace or the current token is a close brace 8)
886 * previous token is a single line comment 9) current token is a '->'
888 if (token == TokenNameMINUS_GREATER
889 && options.compactDereferencingMode)
890 pendingSpace = false;
891 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
892 && token == TokenNameRBRACE;
894 && insertSpaceAfter(previousToken)
895 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
896 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
897 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
898 && !openAndCloseBrace)
901 // Add the next token to the formatted source string.
902 outputCurrentToken(token);
903 if (token == Scanner.TokenNameCOMMENT_LINE
904 && openParenthesisCount > 1) {
906 currentLineBuffer.append(options.lineSeparatorSequence);
907 increaseLineDelta(options.lineSeparatorSequence.length);
911 // Whitespace tokens do not need to be remembered.
912 if (token != Scanner.TokenNameWHITESPACE) {
913 previousToken = token;
914 if (token != Scanner.TokenNameCOMMENT_BLOCK
915 && token != Scanner.TokenNameCOMMENT_LINE
916 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
917 previousCompilableToken = token;
921 output(copyRemainingSource());
923 // dump the last token of the source in the formatted output.
924 } catch (InvalidInputException e) {
925 output(copyRemainingSource());
927 // dump the last token of the source in the formatted output.
931 * Formats the char array <code>sourceString</code>, and returns a string
932 * containing the formatted version.
934 * @return the formatted ouput.
936 public String formatSourceString(String sourceString) {
937 char[] sourceChars = sourceString.toCharArray();
938 formattedSource = new StringBuffer(sourceChars.length);
939 scanner.setSource(sourceChars);
941 return formattedSource.toString();
944 * Formats the char array <code>sourceString</code>, and returns a string
945 * containing the formatted version.
948 * the string to format
949 * @param indentationLevel
950 * the initial indentation level
951 * @return the formatted ouput.
953 public String format(String string, int indentationLevel) {
954 return format(string, indentationLevel, (int[]) null);
957 * Formats the char array <code>sourceString</code>, and returns a string
958 * containing the formatted version. The positions array is modified to
959 * contain the mapped positions.
962 * the string to format
963 * @param indentationLevel
964 * the initial indentation level
966 * the array of positions to map
967 * @return the formatted ouput.
969 public String format(String string, int indentationLevel, int[] positions) {
970 return this.format(string, indentationLevel, positions, null);
972 public String format(String string, int indentationLevel, int[] positions,
973 String lineSeparator) {
974 if (lineSeparator != null) {
975 this.options.setLineSeparator(lineSeparator);
977 if (positions != null) {
978 this.setPositionsToMap(positions);
979 this.setInitialIndentationLevel(indentationLevel);
980 String formattedString = this.formatSourceString(string);
981 int[] mappedPositions = this.getMappedPositions();
982 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
983 return formattedString;
985 this.setInitialIndentationLevel(indentationLevel);
986 return this.formatSourceString(string);
990 * Formats the char array <code>sourceString</code>, and returns a string
991 * containing the formatted version. The initial indentation level is 0.
994 * the string to format
995 * @return the formatted ouput.
997 public String format(String string) {
998 return this.format(string, 0, (int[]) null);
1001 * Formats a given source string, starting indenting it at a particular depth
1002 * and using the given options
1004 * @deprecated backport 1.0 internal functionality
1006 public static String format(String sourceString, int initialIndentationLevel,
1007 ConfigurableOption[] options) {
1008 CodeFormatter formatter = new CodeFormatter(options);
1009 formatter.setInitialIndentationLevel(initialIndentationLevel);
1010 return formatter.formatSourceString(sourceString);
1013 * Returns the number of characters and tab char between the beginning of the
1014 * line and the beginning of the comment.
1016 private int getCurrentCommentOffset() {
1017 int linePtr = scanner.linePtr;
1018 // if there is no beginning of line, return 0.
1022 int beginningOfLine = scanner.lineEnds[linePtr];
1023 int currentStartPosition = scanner.startPosition;
1024 char[] source = scanner.source;
1025 // find the position of the beginning of the line containing the comment
1026 while (beginningOfLine > currentStartPosition) {
1028 beginningOfLine = scanner.lineEnds[--linePtr];
1030 beginningOfLine = 0;
1034 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1035 char currentCharacter = source[i];
1036 switch (currentCharacter) {
1038 offset += options.tabSize;
1053 * Returns an array of descriptions for the configurable options. The
1054 * descriptions may be changed and passed back to a different compiler.
1056 * @deprecated backport 1.0 internal functionality
1058 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1059 String componentName = CodeFormatter.class.getName();
1060 FormatterOptions options = new FormatterOptions();
1061 return new ConfigurableOption[]{
1062 new ConfigurableOption(componentName, "newline.openingBrace", locale,
1063 options.newLineBeforeOpeningBraceMode ? 0 : 1),
1065 new ConfigurableOption(componentName, "newline.controlStatement",
1066 locale, options.newlineInControlStatementMode ? 0 : 1),
1068 new ConfigurableOption(componentName, "newline.clearAll", locale,
1069 options.clearAllBlankLinesMode ? 0 : 1),
1071 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1072 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1073 new ConfigurableOption(componentName, "newline.emptyBlock", locale,
1074 options.newLineInEmptyBlockMode ? 0 : 1),
1076 new ConfigurableOption(componentName, "line.split", locale,
1077 options.maxLineLength),
1079 new ConfigurableOption(componentName, "style.compactAssignment",
1080 locale, options.compactAssignmentMode ? 0 : 1),
1082 new ConfigurableOption(componentName, "tabulation.char", locale,
1083 options.indentWithTab ? 0 : 1),
1085 new ConfigurableOption(componentName, "tabulation.size", locale,
1086 options.tabSize) //$NON-NLS-1$
1090 * Returns the array of mapped positions. Returns null is no positions have
1094 * @deprecated There is no need to retrieve the mapped positions anymore.
1096 public int[] getMappedPositions() {
1097 return mappedPositions;
1100 * Returns the priority of the token given as argument <br>
1101 * The most prioritary the token is, the smallest the return value is.
1103 * @return the priority of <code>token</code>
1105 * the token of which the priority is requested
1107 private static int getTokenPriority(int token) {
1109 case TokenNameextends :
1110 // case TokenNameimplements :
1111 // case TokenNamethrows :
1113 case TokenNameSEMICOLON :
1116 case TokenNameCOMMA :
1119 case TokenNameEQUAL :
1122 case TokenNameAND_AND :
1124 case TokenNameOR_OR :
1127 case TokenNameQUESTION :
1129 case TokenNameCOLON :
1131 return 50; // it's better cutting on ?: than on ;
1132 case TokenNameEQUAL_EQUAL :
1134 case TokenNameEQUAL_EQUAL_EQUAL :
1136 case TokenNameNOT_EQUAL :
1138 case TokenNameNOT_EQUAL_EQUAL :
1141 case TokenNameLESS :
1143 case TokenNameLESS_EQUAL :
1145 case TokenNameGREATER :
1147 case TokenNameGREATER_EQUAL :
1149 // case TokenNameinstanceof : // instanceof
1151 case TokenNamePLUS :
1153 case TokenNameMINUS :
1156 case TokenNameMULTIPLY :
1158 case TokenNameDIVIDE :
1160 case TokenNameREMAINDER :
1163 case TokenNameLEFT_SHIFT :
1165 case TokenNameRIGHT_SHIFT :
1167 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1176 case TokenNameMULTIPLY_EQUAL :
1178 case TokenNameDIVIDE_EQUAL :
1180 case TokenNameREMAINDER_EQUAL :
1182 case TokenNamePLUS_EQUAL :
1184 case TokenNameMINUS_EQUAL :
1186 case TokenNameLEFT_SHIFT_EQUAL :
1188 case TokenNameRIGHT_SHIFT_EQUAL :
1190 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1191 case TokenNameAND_EQUAL :
1193 case TokenNameXOR_EQUAL :
1195 case TokenNameOR_EQUAL :
1202 return Integer.MAX_VALUE;
1206 * Handles the exception raised when an invalid token is encountered. Returns
1207 * true if the exception has been handled, false otherwise.
1209 private boolean handleInvalidToken(Exception e) {
1210 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1211 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1212 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1217 private final void increaseGlobalDelta(int offset) {
1218 globalDelta += offset;
1220 private final void increaseLineDelta(int offset) {
1221 lineDelta += offset;
1223 private final void increaseSplitDelta(int offset) {
1224 splitDelta += offset;
1227 * Returns true if a space has to be inserted after <code>operator</code>
1230 private boolean insertSpaceAfter(int token) {
1232 case TokenNameLPAREN :
1234 case TokenNameTWIDDLE :
1238 case TokenNameLBRACKET :
1239 case Scanner.TokenNameCOMMENT_LINE :
1246 * Returns true if a space has to be inserted before <code>operator</code>
1247 * false otherwise. <br>
1248 * Cannot be static as it uses the code formatter options (to know if the
1249 * compact assignment mode is on).
1251 private boolean insertSpaceBefore(int token) {
1253 case TokenNameEQUAL :
1254 return (!options.compactAssignmentMode);
1259 private static boolean isComment(int token) {
1260 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1261 || token == Scanner.TokenNameCOMMENT_LINE
1262 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1265 private static boolean isLiteralToken(int token) {
1266 boolean result = token == TokenNameIntegerLiteral
1267 // || token == TokenNameLongLiteral
1268 // || token == TokenNameFloatingPointLiteral
1269 || token == TokenNameDoubleLiteral
1270 // || token == TokenNameCharacterLiteral
1271 || token == TokenNameStringLiteral;
1275 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1276 * it is split and the result is dumped in <code>formattedSource</code>
1278 * @param newLineCount
1279 * the number of new lines to append
1281 private void newLine(int newLineCount) {
1282 // format current line
1284 beginningOfLineIndex = formattedSource.length();
1285 String currentLine = currentLineBuffer.toString();
1286 if (containsOpenCloseBraces) {
1287 containsOpenCloseBraces = false;
1288 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1,
1290 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1292 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null,
1295 // dump line break(s)
1296 for (int i = 0; i < newLineCount; i++) {
1297 formattedSource.append(options.lineSeparatorSequence);
1298 increaseSplitDelta(options.lineSeparatorSequence.length);
1300 // reset formatter for next line
1301 int currentLength = currentLine.length();
1302 currentLineBuffer = new StringBuffer(currentLength > maxLineSize
1303 ? maxLineSize = currentLength
1305 increaseGlobalDelta(splitDelta);
1306 increaseGlobalDelta(lineDelta);
1308 currentLineIndentationLevel = initialIndentationLevel;
1310 private String operatorString(int operator) {
1312 case TokenNameextends :
1313 return "extends"; //$NON-NLS-1$
1314 // case TokenNameimplements :
1315 // return "implements"; //$NON-NLS-1$
1317 // case TokenNamethrows :
1318 // return "throws"; //$NON-NLS-1$
1319 case TokenNameSEMICOLON :
1321 return ";"; //$NON-NLS-1$
1322 case TokenNameCOMMA :
1324 return ","; //$NON-NLS-1$
1325 case TokenNameEQUAL :
1327 return "="; //$NON-NLS-1$
1328 case TokenNameAND_AND :
1330 return "&&"; //$NON-NLS-1$
1331 case TokenNameOR_OR :
1333 return "||"; //$NON-NLS-1$
1334 case TokenNameQUESTION :
1336 return "?"; //$NON-NLS-1$
1337 case TokenNameCOLON :
1339 return ":"; //$NON-NLS-1$
1340 case TokenNamePAAMAYIM_NEKUDOTAYIM :
1342 return "::"; //$NON-NLS-1$
1343 case TokenNameEQUAL_EQUAL :
1344 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1345 return "=="; //$NON-NLS-1$
1346 case TokenNameEQUAL_EQUAL_EQUAL :
1347 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1348 return "==="; //$NON-NLS-1$
1349 case TokenNameEQUAL_GREATER :
1351 return "=>"; //$NON-NLS-1$
1352 case TokenNameNOT_EQUAL :
1353 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1354 return "!="; //$NON-NLS-1$
1355 case TokenNameNOT_EQUAL_EQUAL :
1356 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1357 return "!=="; //$NON-NLS-1$
1358 case TokenNameLESS :
1360 return "<"; //$NON-NLS-1$
1361 case TokenNameLESS_EQUAL :
1363 return "<="; //$NON-NLS-1$
1364 case TokenNameGREATER :
1366 return ">"; //$NON-NLS-1$
1367 case TokenNameGREATER_EQUAL :
1369 return ">="; //$NON-NLS-1$
1370 // case TokenNameinstanceof : // instanceof
1371 // return "instanceof"; //$NON-NLS-1$
1372 case TokenNamePLUS :
1373 // + (15.17, 15.17.2)
1374 return "+"; //$NON-NLS-1$
1375 case TokenNameMINUS :
1377 return "-"; //$NON-NLS-1$
1378 case TokenNameMULTIPLY :
1380 return "*"; //$NON-NLS-1$
1381 case TokenNameDIVIDE :
1383 return "/"; //$NON-NLS-1$
1384 case TokenNameREMAINDER :
1386 return "%"; //$NON-NLS-1$
1387 case TokenNameLEFT_SHIFT :
1389 return "<<"; //$NON-NLS-1$
1390 case TokenNameRIGHT_SHIFT :
1392 return ">>"; //$NON-NLS-1$
1393 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1394 // return ">>>"; //$NON-NLS-1$
1396 // & (15.21, 15.21.1, 15.21.2)
1397 return "&"; //$NON-NLS-1$
1399 // | (15.21, 15.21.1, 15.21.2)
1400 return "|"; //$NON-NLS-1$
1402 // ^ (15.21, 15.21.1, 15.21.2)
1403 return "^"; //$NON-NLS-1$
1404 case TokenNameMULTIPLY_EQUAL :
1406 return "*="; //$NON-NLS-1$
1407 case TokenNameDIVIDE_EQUAL :
1409 return "/="; //$NON-NLS-1$
1410 case TokenNameREMAINDER_EQUAL :
1412 return "%="; //$NON-NLS-1$
1413 case TokenNamePLUS_EQUAL :
1415 return "+="; //$NON-NLS-1$
1416 case TokenNameMINUS_EQUAL :
1418 return "-="; //$NON-NLS-1$
1419 case TokenNameMINUS_GREATER :
1421 return "->"; //$NON-NLS-1$
1422 case TokenNameLEFT_SHIFT_EQUAL :
1424 return "<<="; //$NON-NLS-1$
1425 case TokenNameRIGHT_SHIFT_EQUAL :
1427 return ">>="; //$NON-NLS-1$
1428 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1429 // return ">>>="; //$NON-NLS-1$
1430 case TokenNameAND_EQUAL :
1432 return "&="; //$NON-NLS-1$
1433 case TokenNameXOR_EQUAL :
1435 return "^="; //$NON-NLS-1$
1436 case TokenNameOR_EQUAL :
1438 return "|="; //$NON-NLS-1$
1441 return "."; //$NON-NLS-1$
1443 return ""; //$NON-NLS-1$
1447 * Appends <code>stringToOutput</code> to the formatted output. <br>
1448 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1450 private void output(String stringToOutput) {
1451 char currentCharacter;
1452 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1453 currentCharacter = stringToOutput.charAt(i);
1454 if (currentCharacter != '\t') {
1455 currentLineBuffer.append(currentCharacter);
1460 * Appends <code>token</code> to the formatted output. <br>
1461 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1464 private void outputCurrentToken(int token) {
1465 char[] source = scanner.source;
1466 int startPosition = scanner.startPosition;
1468 case Scanner.TokenNameCOMMENT_PHPDOC :
1469 case Scanner.TokenNameCOMMENT_BLOCK :
1470 case Scanner.TokenNameCOMMENT_LINE :
1471 boolean endOfLine = false;
1472 int currentCommentOffset = getCurrentCommentOffset();
1473 int beginningOfLineSpaces = 0;
1475 currentCommentOffset = getCurrentCommentOffset();
1476 beginningOfLineSpaces = 0;
1477 boolean pendingCarriageReturn = false;
1478 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1479 char currentCharacter = source[i];
1480 updateMappedPositions(i);
1481 switch (currentCharacter) {
1483 pendingCarriageReturn = true;
1487 if (pendingCarriageReturn) {
1488 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1490 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1492 pendingCarriageReturn = false;
1493 currentLineBuffer.append(options.lineSeparatorSequence);
1494 beginningOfLineSpaces = 0;
1498 if (pendingCarriageReturn) {
1499 pendingCarriageReturn = false;
1500 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1501 currentLineBuffer.append(options.lineSeparatorSequence);
1502 beginningOfLineSpaces = 0;
1506 // we remove a maximum of currentCommentOffset characters (tabs
1507 // are converted to space numbers).
1508 beginningOfLineSpaces += options.tabSize;
1509 if (beginningOfLineSpaces > currentCommentOffset) {
1510 currentLineBuffer.append(currentCharacter);
1512 increaseGlobalDelta(-1);
1515 currentLineBuffer.append(currentCharacter);
1519 if (pendingCarriageReturn) {
1520 pendingCarriageReturn = false;
1521 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1522 currentLineBuffer.append(options.lineSeparatorSequence);
1523 beginningOfLineSpaces = 0;
1527 // we remove a maximum of currentCommentOffset characters (tabs
1528 // are converted to space numbers).
1529 beginningOfLineSpaces++;
1530 if (beginningOfLineSpaces > currentCommentOffset) {
1531 currentLineBuffer.append(currentCharacter);
1533 increaseGlobalDelta(-1);
1536 currentLineBuffer.append(currentCharacter);
1540 if (pendingCarriageReturn) {
1541 pendingCarriageReturn = false;
1542 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1543 currentLineBuffer.append(options.lineSeparatorSequence);
1544 beginningOfLineSpaces = 0;
1547 beginningOfLineSpaces = 0;
1548 currentLineBuffer.append(currentCharacter);
1553 updateMappedPositions(scanner.currentPosition - 1);
1554 multipleLineCommentCounter++;
1557 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1558 char currentCharacter = source[i];
1559 updateMappedPositions(i);
1560 currentLineBuffer.append(currentCharacter);
1565 * Outputs <code>currentString</code>:<br>
1567 * <li>If its length is < maxLineLength, output
1568 * <li>Otherwise it is split.
1571 * @param currentString
1573 * @param preIndented
1574 * whether the string to output was pre-indented
1576 * number of indentation to put in front of <code>currentString</code>
1578 * value of the operator belonging to <code>currentString</code>.
1580 private void outputLine(String currentString, boolean preIndented, int depth,
1581 int operator, int substringIndex, int[] startSubstringIndexes,
1582 int offsetInGlobalLine) {
1583 boolean emptyFirstSubString = false;
1584 String operatorString = operatorString(operator);
1585 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1586 boolean placeOperatorAhead = !placeOperatorBehind;
1587 // dump prefix operator?
1588 if (placeOperatorAhead) {
1593 if (operator != 0) {
1594 if (insertSpaceBefore(operator)) {
1595 formattedSource.append(' ');
1596 increaseSplitDelta(1);
1598 formattedSource.append(operatorString);
1599 increaseSplitDelta(operatorString.length());
1600 if (insertSpaceAfter(operator) // && operator != TokenNameimplements
1601 && operator != TokenNameextends) {
1602 // && operator != TokenNamethrows) {
1603 formattedSource.append(' ');
1604 increaseSplitDelta(1);
1608 SplitLine splitLine = null;
1609 if (options.maxLineLength == 0
1610 || getLength(currentString, depth) < options.maxLineLength
1611 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1612 // depending on the type of operator, outputs new line before of after
1614 // indent before postfix operator
1615 // indent also when the line cannot be split
1616 if (operator == TokenNameextends) {
1617 // || operator == TokenNameimplements
1618 // || operator == TokenNamethrows) {
1619 formattedSource.append(' ');
1620 increaseSplitDelta(1);
1622 if (placeOperatorBehind) {
1627 int max = currentString.length();
1628 if (multipleLineCommentCounter != 0) {
1630 BufferedReader reader = new BufferedReader(new StringReader(
1632 String line = reader.readLine();
1633 while (line != null) {
1634 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1635 beginningOfLineIndex + line.length()
1636 + options.lineSeparatorSequence.length);
1637 formattedSource.append(line);
1638 beginningOfLineIndex = beginningOfLineIndex + line.length();
1639 if ((line = reader.readLine()) != null) {
1640 formattedSource.append(options.lineSeparatorSequence);
1641 beginningOfLineIndex += options.lineSeparatorSequence.length;
1642 dumpTab(currentLineIndentationLevel);
1646 } catch (IOException e) {
1647 e.printStackTrace();
1650 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1651 beginningOfLineIndex + max);
1652 for (int i = 0; i < max; i++) {
1653 char currentChar = currentString.charAt(i);
1654 switch (currentChar) {
1659 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1660 // split with a comment inside a condition
1661 // a substring cannot end with a lineSeparatorSequence,
1662 // except if it has been added by format() after a one-line
1664 formattedSource.append(options.lineSeparatorSequence);
1665 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1670 formattedSource.append(currentChar);
1674 // update positions inside the mappedPositions table
1675 if (substringIndex != -1) {
1676 if (multipleLineCommentCounter == 0) {
1677 int startPosition = beginningOfLineIndex
1678 + startSubstringIndexes[substringIndex];
1679 updateMappedPositionsWhileSplitting(startPosition, startPosition
1682 // compute the splitDelta resulting with the operator and blank removal
1683 if (substringIndex + 1 != startSubstringIndexes.length) {
1684 increaseSplitDelta(startSubstringIndexes[substringIndex] + max
1685 - startSubstringIndexes[substringIndex + 1]);
1688 // dump postfix operator?
1689 if (placeOperatorBehind) {
1690 if (insertSpaceBefore(operator)) {
1691 formattedSource.append(' ');
1692 if (operator != 0) {
1693 increaseSplitDelta(1);
1696 formattedSource.append(operatorString);
1697 if (operator != 0) {
1698 increaseSplitDelta(operatorString.length());
1703 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1704 // extends has to stand alone on a line when currentString has been split.
1705 if (options.maxLineLength != 0 && splitLine != null
1706 && (operator == TokenNameextends)) {
1707 // || operator == TokenNameimplements
1708 // || operator == TokenNamethrows)) {
1709 formattedSource.append(options.lineSeparatorSequence);
1710 increaseSplitDelta(options.lineSeparatorSequence.length);
1713 if (operator == TokenNameextends) {
1714 // || operator == TokenNameimplements
1715 // || operator == TokenNamethrows) {
1716 formattedSource.append(' ');
1717 increaseSplitDelta(1);
1720 // perform actual splitting
1721 String result[] = splitLine.substrings;
1722 int[] splitOperators = splitLine.operators;
1723 if (result[0].length() == 0) {
1724 // when the substring 0 is null, the substring 1 is correctly indented.
1726 emptyFirstSubString = true;
1728 // the operator going in front of the result[0] string is the operator
1730 for (int i = 0, max = result.length; i < max; i++) {
1731 // the new depth is the current one if this is the first substring,
1732 // the current one + 1 otherwise.
1733 // if the substring is a comment, use the current indentation Level
1734 // instead of the depth
1735 // (-1 because the ouputline increases depth).
1736 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1738 String currentResult = result[i];
1739 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1740 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1741 || currentResult.startsWith("//")) //$NON-NLS-1$
1742 ? indentationLevel - 1 : depth;
1743 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString)
1745 : false, i == 0 ? newDepth : newDepth + 1, splitOperators[i], i,
1746 splitLine.startSubstringsIndexes, currentString
1747 .indexOf(currentResult));
1749 formattedSource.append(options.lineSeparatorSequence);
1750 increaseSplitDelta(options.lineSeparatorSequence.length);
1754 if (result.length == splitOperators.length - 1) {
1755 int lastOperator = splitOperators[result.length];
1756 String lastOperatorString = operatorString(lastOperator);
1757 formattedSource.append(options.lineSeparatorSequence);
1758 increaseSplitDelta(options.lineSeparatorSequence.length);
1759 if (breakLineBeforeOperator(lastOperator)) {
1761 if (lastOperator != 0) {
1762 if (insertSpaceBefore(lastOperator)) {
1763 formattedSource.append(' ');
1764 increaseSplitDelta(1);
1766 formattedSource.append(lastOperatorString);
1767 increaseSplitDelta(lastOperatorString.length());
1768 if (insertSpaceAfter(lastOperator) // && lastOperator !=
1769 // TokenNameimplements
1770 && lastOperator != TokenNameextends) {
1771 // && lastOperator != TokenNamethrows) {
1772 formattedSource.append(' ');
1773 increaseSplitDelta(1);
1778 if (placeOperatorBehind) {
1779 if (insertSpaceBefore(operator)) {
1780 formattedSource.append(' ');
1781 increaseSplitDelta(1);
1783 formattedSource.append(operatorString);
1784 //increaseSplitDelta(operatorString.length());
1788 * Pops the top statement of the stack if it is <code>token</code>
1790 private int pop(int token) {
1792 if ((constructionsCount > 0)
1793 && (constructions[constructionsCount - 1] == token)) {
1795 constructionsCount--;
1800 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
1801 * <code>NONINDENT_BLOCK</code>.
1803 private int popBlock() {
1805 if ((constructionsCount > 0)
1806 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1807 if (constructions[constructionsCount - 1] == BLOCK)
1809 constructionsCount--;
1814 * Pops elements until the stack is empty or the top element is <code>token</code>.
1816 * Does not remove <code>token</code> from the stack.
1819 * the token to be left as the top of the stack
1821 private int popExclusiveUntil(int token) {
1823 int startCount = constructionsCount;
1824 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1825 if (constructions[i] != NONINDENT_BLOCK)
1827 constructionsCount--;
1832 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1833 * or a <code>NONINDENT_BLOCK</code>.<br>
1834 * Does not remove it from the stack.
1836 private int popExclusiveUntilBlock() {
1837 int startCount = constructionsCount;
1839 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1840 && constructions[i] != NONINDENT_BLOCK; i--) {
1841 constructionsCount--;
1847 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>,
1848 * a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1849 * Does not remove it from the stack.
1851 private int popExclusiveUntilBlockOrCase() {
1852 int startCount = constructionsCount;
1854 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1855 && constructions[i] != NONINDENT_BLOCK
1856 && constructions[i] != TokenNamecase; i--) {
1857 constructionsCount--;
1863 * Pops elements until the stack is empty or the top element is <code>token</code>.
1865 * Removes <code>token</code> from the stack too.
1868 * the token to remove from the stack
1870 private int popInclusiveUntil(int token) {
1871 int startCount = constructionsCount;
1873 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1874 if (constructions[i] != NONINDENT_BLOCK)
1876 constructionsCount--;
1878 if (constructionsCount > 0) {
1879 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1881 constructionsCount--;
1886 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1887 * or a <code>NONINDENT_BLOCK</code>.<br>
1888 * Does not remove it from the stack.
1890 private int popInclusiveUntilBlock() {
1891 int startCount = constructionsCount;
1893 for (int i = startCount - 1; i >= 0
1894 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1896 constructionsCount--;
1898 if (constructionsCount > 0) {
1899 if (constructions[constructionsCount - 1] == BLOCK)
1901 constructionsCount--;
1906 * Pushes a block in the stack. <br>
1907 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
1908 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
1909 * otherwise. Creates a new bigger array if the current one is full.
1911 private int pushBlock() {
1913 if (constructionsCount == constructions.length)
1914 System.arraycopy(constructions, 0,
1915 (constructions = new int[constructionsCount * 2]), 0,
1916 constructionsCount);
1917 if ((constructionsCount == 0)
1918 || (constructions[constructionsCount - 1] == BLOCK)
1919 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1920 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1922 constructions[constructionsCount++] = BLOCK;
1924 constructions[constructionsCount++] = NONINDENT_BLOCK;
1929 * Pushes <code>token</code>.<br>
1930 * Creates a new bigger array if the current one is full.
1932 private int pushControlStatement(int token) {
1933 if (constructionsCount == constructions.length)
1934 System.arraycopy(constructions, 0,
1935 (constructions = new int[constructionsCount * 2]), 0,
1936 constructionsCount);
1937 constructions[constructionsCount++] = token;
1940 private static boolean separateFirstArgumentOn(int currentToken) {
1941 //return (currentToken == TokenNameCOMMA || currentToken ==
1942 // TokenNameSEMICOLON);
1943 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
1944 && currentToken != TokenNameNOT && currentToken != TokenNamewhile
1945 && currentToken != TokenNamefor && currentToken != TokenNameswitch;
1948 * Set the positions to map. The mapped positions should be retrieved using
1949 * the getMappedPositions() method.
1953 * @deprecated Set the positions to map using the format(String, int, int[])
1956 * @see #getMappedPositions()
1958 public void setPositionsToMap(int[] positions) {
1959 positionsToMap = positions;
1962 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);
1972 * Splits <code>stringToSplit</code> on the top level token <br>
1973 * If there are several identical token at the same level, the string is cut
1976 * @return an object containing the operator and all the substrings or null
1977 * if the string cannot be split
1979 public SplitLine split(String stringToSplit) {
1980 return split(stringToSplit, 0);
1983 * Splits <code>stringToSplit</code> on the top level token <br>
1984 * If there are several identical token at the same level, the string is cut
1987 * @return an object containing the operator and all the substrings or null
1988 * 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;
2003 int[] substringsStartPositions = new int[10];
2004 // contains the start position of substrings
2005 int[] substringsEndPositions = new int[10];
2006 // contains the start position of substrings
2007 int substringsCount = 1; // index in the substringsStartPosition array
2008 int[] splitOperators = new int[10];
2009 // contains the start position of substrings
2010 int splitOperatorsCount = 0; // index in the substringsStartPosition array
2011 int[] openParenthesisPosition = new int[10];
2012 int openParenthesisPositionCount = 0;
2014 int lastOpenParenthesisPosition = -1;
2015 // used to remember the position of the 1st open parenthesis
2016 // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2017 // setup the scanner with a new source
2018 int lastCommentStartPosition = -1;
2019 // to remember the start position of the last comment
2020 int firstTokenOnLine = -1;
2021 // to remember the first token of the line
2022 int previousToken = -1;
2023 // to remember the previous token.
2024 splitScanner.setSource(stringToSplit.toCharArray());
2028 // takes the next token
2030 if (currentToken != Scanner.TokenNameWHITESPACE)
2031 previousToken = currentToken;
2032 currentToken = splitScanner.getNextToken();
2033 if (Scanner.DEBUG) {
2034 int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2035 int currentStartPosition = splitScanner
2036 .getCurrentTokenStartPosition();
2037 System.out.print(currentStartPosition + "," + currentEndPosition
2039 System.out.println(scanner.toStringAction(currentToken));
2041 } catch (InvalidInputException e) {
2042 if (!handleInvalidToken(e))
2045 // this value is not modify when an exception is raised.
2047 if (currentToken == TokenNameEOF)
2049 if (firstTokenOnLine == -1) {
2050 firstTokenOnLine = currentToken;
2052 switch (currentToken) {
2053 case TokenNameRBRACE :
2054 case TokenNameRPAREN :
2055 if (openParenthesisPositionCount > 0) {
2056 if (openParenthesisPositionCount == 1
2057 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2058 lastOpenParenthesisPosition = openParenthesisPosition[0];
2059 } else if ((splitTokenDepth == Integer.MAX_VALUE)
2060 || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2062 splitTokenDepth = openParenthesisPositionCount;
2063 splitTokenPriority = Integer.MAX_VALUE;
2064 substringsStartPositions[0] = 0;
2065 // better token means the whole line until now is the first
2067 substringsCount = 1; // resets the count of substrings
2068 substringsEndPositions[0] = openParenthesisPosition[0];
2069 // substring ends on operator start
2070 position = openParenthesisPosition[0];
2071 // the string mustn't be cut before the closing parenthesis but
2072 // after the opening one.
2073 splitOperatorsCount = 1; // resets the count of split operators
2074 splitOperators[0] = 0;
2076 openParenthesisPositionCount--;
2079 case TokenNameLBRACE :
2080 case TokenNameLPAREN :
2081 if (openParenthesisPositionCount == openParenthesisPosition.length) {
2084 openParenthesisPosition,
2086 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2087 0, openParenthesisPositionCount);
2089 openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2090 if (currentToken == TokenNameLPAREN
2091 && previousToken == TokenNameRPAREN) {
2092 openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2095 case TokenNameSEMICOLON :
2097 case TokenNameCOMMA :
2099 case TokenNameEQUAL :
2101 if (openParenthesisPositionCount < splitTokenDepth
2102 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2103 // the current token is better than the one we currently have
2104 // (in level or in priority if same level)
2105 // reset the substringsCount
2106 splitTokenDepth = openParenthesisPositionCount;
2107 splitTokenType = currentToken;
2108 splitTokenPriority = getTokenPriority(currentToken);
2109 substringsStartPositions[0] = 0;
2110 // better token means the whole line until now is the first
2112 if (separateFirstArgumentOn(firstTokenOnLine)
2113 && openParenthesisPositionCount > 0) {
2114 substringsCount = 2; // resets the count of substrings
2115 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2116 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2117 substringsEndPositions[1] = splitScanner.startPosition;
2118 splitOperatorsCount = 2; // resets the count of split operators
2119 splitOperators[0] = 0;
2120 splitOperators[1] = currentToken;
2121 position = splitScanner.currentPosition;
2122 // next substring will start from operator end
2124 substringsCount = 1; // resets the count of substrings
2125 substringsEndPositions[0] = splitScanner.startPosition;
2126 // substring ends on operator start
2127 position = splitScanner.currentPosition;
2128 // next substring will start from operator end
2129 splitOperatorsCount = 1; // resets the count of split operators
2130 splitOperators[0] = currentToken;
2133 if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2134 && splitTokenType != TokenNameEQUAL
2135 && currentToken != TokenNameEQUAL) {
2136 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2138 // take only the 1st = into account.
2139 // if another token with the same priority is found,
2140 // push the start position of the substring and
2141 // push the token into the stack.
2142 // create a new array object if the current one is full.
2143 if (substringsCount == substringsStartPositions.length) {
2146 substringsStartPositions,
2148 (substringsStartPositions = new int[substringsCount * 2]),
2149 0, substringsCount);
2150 System.arraycopy(substringsEndPositions, 0,
2151 (substringsEndPositions = new int[substringsCount * 2]),
2152 0, substringsCount);
2154 if (splitOperatorsCount == splitOperators.length) {
2155 System.arraycopy(splitOperators, 0,
2156 (splitOperators = new int[splitOperatorsCount * 2]), 0,
2157 splitOperatorsCount);
2159 substringsStartPositions[substringsCount] = position;
2160 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2161 // substring ends on operator start
2162 position = splitScanner.currentPosition;
2163 // next substring will start from operator end
2164 splitOperators[splitOperatorsCount++] = currentToken;
2168 case TokenNameCOLON :
2170 // see 1FK7C5R, we only split on a colon, when it is associated
2171 // with a question-mark.
2172 // indeed it might appear also behind a case statement, and we do
2173 // not to break at this point.
2174 if ((splitOperatorsCount == 0)
2175 || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2178 case TokenNameextends :
2179 // case TokenNameimplements :
2180 // case TokenNamethrows :
2183 case TokenNameMULTIPLY :
2185 case TokenNameDIVIDE :
2187 case TokenNameREMAINDER :
2189 case TokenNamePLUS :
2190 // + (15.17, 15.17.2)
2191 case TokenNameMINUS :
2193 case TokenNameLEFT_SHIFT :
2195 case TokenNameRIGHT_SHIFT :
2197 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2198 case TokenNameLESS :
2200 case TokenNameLESS_EQUAL :
2202 case TokenNameGREATER :
2204 case TokenNameGREATER_EQUAL :
2206 // case TokenNameinstanceof : // instanceof
2207 case TokenNameEQUAL_EQUAL :
2208 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2209 case TokenNameEQUAL_EQUAL_EQUAL :
2210 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2211 case TokenNameNOT_EQUAL :
2212 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2213 case TokenNameNOT_EQUAL_EQUAL :
2214 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2216 // & (15.21, 15.21.1, 15.21.2)
2218 // | (15.21, 15.21.1, 15.21.2)
2220 // ^ (15.21, 15.21.1, 15.21.2)
2221 case TokenNameAND_AND :
2223 case TokenNameOR_OR :
2225 case TokenNameQUESTION :
2227 case TokenNameMULTIPLY_EQUAL :
2229 case TokenNameDIVIDE_EQUAL :
2231 case TokenNameREMAINDER_EQUAL :
2233 case TokenNamePLUS_EQUAL :
2235 case TokenNameMINUS_EQUAL :
2237 case TokenNameLEFT_SHIFT_EQUAL :
2239 case TokenNameRIGHT_SHIFT_EQUAL :
2241 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2242 case TokenNameAND_EQUAL :
2244 case TokenNameXOR_EQUAL :
2246 case TokenNameOR_EQUAL :
2248 if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2249 && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2250 || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2251 // the current token is better than the one we currently have
2252 // (in level or in priority if same level)
2253 // reset the substringsCount
2254 splitTokenDepth = openParenthesisPositionCount;
2255 splitTokenType = currentToken;
2256 splitTokenPriority = getTokenPriority(currentToken);
2257 substringsStartPositions[0] = 0;
2258 // better token means the whole line until now is the first
2260 if (separateFirstArgumentOn(firstTokenOnLine)
2261 && openParenthesisPositionCount > 0) {
2262 substringsCount = 2; // resets the count of substrings
2263 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2264 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2265 substringsEndPositions[1] = splitScanner.startPosition;
2266 splitOperatorsCount = 3; // resets the count of split operators
2267 splitOperators[0] = 0;
2268 splitOperators[1] = 0;
2269 splitOperators[2] = currentToken;
2270 position = splitScanner.currentPosition;
2271 // next substring will start from operator end
2273 substringsCount = 1; // resets the count of substrings
2274 substringsEndPositions[0] = splitScanner.startPosition;
2275 // substring ends on operator start
2276 position = splitScanner.currentPosition;
2277 // next substring will start from operator end
2278 splitOperatorsCount = 2; // resets the count of split operators
2279 splitOperators[0] = 0;
2280 // nothing for first operand since operator will be inserted in
2281 // front of the second operand
2282 splitOperators[1] = currentToken;
2285 if (openParenthesisPositionCount == splitTokenDepth
2286 && splitTokenPriority == getTokenPriority(currentToken)) {
2287 // if another token with the same priority is found,
2288 // push the start position of the substring and
2289 // push the token into the stack.
2290 // create a new array object if the current one is full.
2291 if (substringsCount == substringsStartPositions.length) {
2294 substringsStartPositions,
2296 (substringsStartPositions = new int[substringsCount * 2]),
2297 0, substringsCount);
2298 System.arraycopy(substringsEndPositions, 0,
2299 (substringsEndPositions = new int[substringsCount * 2]),
2300 0, substringsCount);
2302 if (splitOperatorsCount == splitOperators.length) {
2303 System.arraycopy(splitOperators, 0,
2304 (splitOperators = new int[splitOperatorsCount * 2]), 0,
2305 splitOperatorsCount);
2307 substringsStartPositions[substringsCount] = position;
2308 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2309 // substring ends on operator start
2310 position = splitScanner.currentPosition;
2311 // next substring will start from operator end
2312 splitOperators[splitOperatorsCount++] = currentToken;
2318 if (isComment(currentToken)) {
2319 lastCommentStartPosition = splitScanner.startPosition;
2321 lastCommentStartPosition = -1;
2324 } catch (InvalidInputException e) {
2327 // if the string cannot be split, return null.
2328 if (splitOperatorsCount == 0)
2330 // ## SPECIAL CASES BEGIN
2331 if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2332 && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2333 || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2334 && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) || (separateFirstArgumentOn(firstTokenOnLine)
2335 && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2336 && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2337 // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2338 // not be broken on two lines
2339 // only one split on a top level .
2340 // or more than one split on . and substring before open parenthesis fits
2342 // or split inside parenthesis and first token is not a for/while/if
2343 SplitLine sl = split(
2344 stringToSplit.substring(lastOpenParenthesisPosition),
2345 lastOpenParenthesisPosition);
2346 if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2347 // trim() is used to remove the extra blanks at the end of the
2348 // substring. See PR 1FGYPI1
2349 return new SplitLine(new int[]{0, 0}, new String[]{
2350 stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2351 stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2353 lastOpenParenthesisPosition + offsetInGlobalLine});
2355 // right substring can be split and is split on comma
2356 // copy substrings and operators
2357 // except if the 1st string is empty.
2358 int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2359 int subStringsLength = sl.substrings.length + 1 - startIndex;
2360 String[] result = new String[subStringsLength];
2361 int[] startIndexes = new int[subStringsLength];
2362 int operatorsLength = sl.operators.length + 1 - startIndex;
2363 int[] operators = new int[operatorsLength];
2364 result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2366 System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2367 1, subStringsLength - 1);
2368 for (int i = subStringsLength - 1; i >= 0; i--) {
2369 startIndexes[i] += offsetInGlobalLine;
2371 System.arraycopy(sl.substrings, startIndex, result, 1,
2372 subStringsLength - 1);
2373 System.arraycopy(sl.operators, startIndex, operators, 1,
2374 operatorsLength - 1);
2375 return new SplitLine(operators, result, startIndexes);
2378 // if the last token is a comment and the substring before the comment fits
2380 // split before the comment and return the result.
2381 if (lastCommentStartPosition > -1
2382 && lastCommentStartPosition < options.maxLineLength
2383 && splitTokenPriority > 50) {
2384 int end = lastCommentStartPosition;
2385 int start = lastCommentStartPosition;
2386 if (stringToSplit.charAt(end - 1) == ' ') {
2389 if (start != end && stringToSplit.charAt(start) == ' ') {
2392 return new SplitLine(new int[]{0, 0}, new String[]{
2393 stringToSplit.substring(0, end), stringToSplit.substring(start)},
2394 new int[]{0, start});
2396 if (position != stringToSplit.length()) {
2397 if (substringsCount == substringsStartPositions.length) {
2398 System.arraycopy(substringsStartPositions, 0,
2399 (substringsStartPositions = new int[substringsCount * 2]), 0,
2401 System.arraycopy(substringsEndPositions, 0,
2402 (substringsEndPositions = new int[substringsCount * 2]), 0,
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) {
2410 System.arraycopy(splitOperators, 0,
2411 (splitOperators = new int[splitOperatorsCount * 2]), 0,
2412 splitOperatorsCount);
2414 splitOperators[splitOperatorsCount] = 0;
2415 // the last element of the stack is the position of the end of
2417 // +1 because the substring method excludes the last character
2418 String[] result = new String[substringsCount];
2419 for (int i = 0; i < substringsCount; i++) {
2420 int start = substringsStartPositions[i];
2421 int end = substringsEndPositions[i];
2422 if (stringToSplit.charAt(start) == ' ') {
2424 substringsStartPositions[i]++;
2426 if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2429 result[i] = stringToSplit.substring(start, end);
2430 substringsStartPositions[i] += offsetInGlobalLine;
2432 if (splitOperatorsCount > substringsCount) {
2433 System.arraycopy(substringsStartPositions, 0,
2434 (substringsStartPositions = new int[splitOperatorsCount]), 0,
2436 System.arraycopy(substringsEndPositions, 0,
2437 (substringsEndPositions = new int[splitOperatorsCount]), 0,
2439 for (int i = substringsCount; i < splitOperatorsCount; i++) {
2440 substringsStartPositions[i] = position;
2441 substringsEndPositions[i] = position;
2443 System.arraycopy(splitOperators, 0,
2444 (splitOperators = new int[splitOperatorsCount]), 0,
2445 splitOperatorsCount);
2447 System.arraycopy(substringsStartPositions, 0,
2448 (substringsStartPositions = new int[substringsCount]), 0,
2450 System.arraycopy(substringsEndPositions, 0,
2451 (substringsEndPositions = new int[substringsCount]), 0,
2453 System.arraycopy(splitOperators, 0,
2454 (splitOperators = new int[substringsCount]), 0, substringsCount);
2456 SplitLine splitLine = new SplitLine(splitOperators, result,
2457 substringsStartPositions);
2460 private void updateMappedPositions(int startPosition) {
2461 if (positionsToMap == null) {
2464 char[] source = scanner.source;
2465 int sourceLength = source.length;
2466 while (indexToMap < positionsToMap.length
2467 && positionsToMap[indexToMap] <= startPosition) {
2468 int posToMap = positionsToMap[indexToMap];
2469 if (posToMap < 0 || posToMap >= sourceLength) {
2470 // protection against out of bounds position
2471 if (posToMap == sourceLength) {
2472 mappedPositions[indexToMap] = formattedSource.length();
2474 indexToMap = positionsToMap.length; // no more mapping
2477 if (CharOperation.isWhitespace(source[posToMap])) {
2478 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2480 if (posToMap == sourceLength - 1) {
2481 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2483 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2489 private void updateMappedPositionsWhileSplitting(int startPosition,
2491 if (mappedPositions == null || mappedPositions.length == indexInMap)
2493 while (indexInMap < mappedPositions.length
2494 && startPosition <= mappedPositions[indexInMap]
2495 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2496 mappedPositions[indexInMap] += splitDelta;
2500 private int getLength(String s, int tabDepth) {
2502 for (int i = 0; i < tabDepth; i++) {
2503 length += options.tabSize;
2505 for (int i = 0, max = s.length(); i < max; i++) {
2506 char currentChar = s.charAt(i);
2507 switch (currentChar) {
2509 length += options.tabSize;
2518 * Sets the initial indentation level
2520 * @param indentationLevel
2521 * new indentation level
2525 public void setInitialIndentationLevel(int newIndentationLevel) {
2526 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;