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;
26 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
27 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
29 /** <h2>How to format a piece of code ?</h2>
30 * <ul><li>Create an instance of <code>CodeFormatter</code>
31 * <li>Use the method <code>void format(aString)</code>
32 * on this instance to format <code>aString</code>.
33 * It will return the formatted string.</ul>
35 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
37 public FormatterOptions options;
40 * Represents a block in the <code>constructions</code> stack.
42 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
45 * Represents a block following a control statement in the <code>constructions</code> stack.
47 public static final int NONINDENT_BLOCK = -100;
50 * Contains the formatted output.
52 StringBuffer formattedSource;
55 * Contains the current line.<br>
56 * Will be dumped at the next "newline"
58 StringBuffer currentLineBuffer;
61 * Used during the formatting to get each token.
66 * Contains the tokens responsible for the current indentation level
67 * and the blocks not closed yet.
69 private int[] constructions;
72 * Index in the <code>constructions</code> array.
74 private int constructionsCount;
77 * Level of indentation of the current token (number of tab char put in front of it).
79 private int indentationLevel;
82 * Regular level of indentation of all the lines
84 private int initialIndentationLevel;
87 * Used to split a line.
92 * To remember the offset between the beginning of the line and the
93 * beginning of the comment.
95 int currentCommentOffset;
96 int currentLineIndentationLevel;
98 private boolean containsOpenCloseBraces;
99 private int indentationLevelForOpenCloseBraces;
102 * Collections of positions to map
104 private int[] positionsToMap;
107 * Collections of mapped positions
109 private int[] mappedPositions;
111 private int indexToMap;
113 private int indexInMap;
115 private int globalDelta;
117 private int lineDelta;
119 private int splitDelta;
121 private int beginningOfLineIndex;
123 private int multipleLineCommentCounter;
126 * Creates a new instance of Code Formatter using the given settings.
128 * @deprecated backport 1.0 internal functionality
130 public CodeFormatter(ConfigurableOption[] settings) {
131 this(convertConfigurableOptions(settings));
135 * Creates a new instance of Code Formatter using the FormattingOptions object
137 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
139 public CodeFormatter() {
143 * Creates a new instance of Code Formatter using the given settings.
145 public CodeFormatter(Map settings) {
147 // initialize internal state
148 constructionsCount = 0;
149 constructions = new int[10];
150 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
151 currentCommentOffset = -1;
153 // initialize primary and secondary scanners
154 scanner = new Scanner(true /*comment*/
155 , true /*whitespace*/
158 ); // regular scanner for forming lines
159 scanner.recordLineSeparator = true;
161 // to remind of the position of the beginning of the line.
162 splitScanner = new Scanner(true /*comment*/
163 , true /*whitespace*/
167 // secondary scanner to split long lines formed by primary scanning
169 // initialize current line buffer
170 currentLineBuffer = new StringBuffer();
171 this.options = new FormatterOptions(settings);
175 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
178 private static boolean breakLineBeforeOperator(int operator) {
180 case TokenNameCOMMA :
181 case TokenNameSEMICOLON :
182 case TokenNameEQUAL :
190 * @deprecated backport 1.0 internal functionality
192 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
193 Hashtable options = new Hashtable(10);
195 for (int i = 0; i < settings.length; i++) {
196 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
197 String optionName = settings[i].getOptionName();
198 int valueIndex = settings[i].getCurrentValueIndex();
200 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
201 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
203 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
204 options.put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
206 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
207 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
209 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
210 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
212 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
213 options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
215 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
216 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
218 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
219 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
221 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
222 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
225 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
234 * Returns the end of the source code.
236 private final String copyRemainingSource() {
237 char str[] = scanner.source;
238 int startPosition = scanner.startPosition;
239 int length = str.length - startPosition;
240 StringBuffer bufr = new StringBuffer(length);
241 if (startPosition < str.length) {
242 bufr.append(str, startPosition, length);
244 return (bufr.toString());
248 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
250 private void dumpTab(int tabCount) {
251 if (options.indentWithTab) {
252 for (int j = 0; j < tabCount; j++) {
253 formattedSource.append('\t');
254 increaseSplitDelta(1);
257 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
258 formattedSource.append(' ');
259 increaseSplitDelta(1);
265 * Dumps <code>currentLineBuffer</code> into the formatted string.
267 private void flushBuffer() {
268 String currentString = currentLineBuffer.toString();
270 beginningOfLineIndex = formattedSource.length();
271 if (containsOpenCloseBraces) {
272 containsOpenCloseBraces = false;
273 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
274 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
276 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
278 int scannerSourceLength = scanner.source.length;
279 if (scannerSourceLength > 2) {
280 if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
281 formattedSource.append(options.lineSeparatorSequence);
282 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
283 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
284 formattedSource.append(options.lineSeparatorSequence);
285 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
286 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
287 formattedSource.append(options.lineSeparatorSequence);
288 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
291 updateMappedPositions(scanner.startPosition);
295 * Formats the input string.
297 private void format() {
299 int previousToken = 0;
300 int previousCompilableToken = 0;
301 int indentationOffset = 0;
302 int newLinesInWhitespace = 0;
304 // number of new lines in the previous whitespace token
305 // (used to leave blank lines before comments)
306 int pendingNewLines = 0;
307 boolean expectingOpenBrace = false;
308 boolean clearNonBlockIndents = false;
309 // true if all indentations till the 1st { (usefull after } or ;)
310 boolean pendingSpace = true;
311 boolean pendingNewlineAfterParen = false;
312 // true when a cr is to be put after a ) (in conditional statements)
313 boolean inAssignment = false;
314 boolean inArrayAssignment = false;
315 boolean inThrowsClause = false;
316 boolean inClassOrInterfaceHeader = false;
317 int dollarBraceCount = 0;
319 // openBracketCount is used to count the number of open brackets not closed yet.
320 int openBracketCount = 0;
321 int unarySignModifier = 0;
323 // openParenthesis[0] is used to count the parenthesis not belonging to a condition
324 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
325 int openParenthesisCount = 1;
326 int[] openParenthesis = new int[10];
328 // tokenBeforeColon is used to know what token goes along with the current :
329 // it can be case or ?
330 int tokenBeforeColonCount = 0;
331 int[] tokenBeforeColon = new int[10];
333 constructionsCount = 0; // initializes the constructions count.
335 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
338 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
339 boolean specialElse = false;
341 // OPTION (IndentationLevel): initial indentation level may be non-zero.
342 currentLineIndentationLevel += constructionsCount;
344 // An InvalidInputException exception might cause the termination of this loop.
347 // Get the next token. Catch invalid input and output it
348 // with minimal formatting, also catch end of input and
351 token = scanner.getNextToken();
353 int currentEndPosition = scanner.getCurrentTokenEndPosition();
354 int currentStartPosition = scanner.getCurrentTokenStartPosition();
356 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
357 System.out.println(scanner.toStringAction(token));
360 // Patch for line comment
361 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
362 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
363 int length = scanner.currentPosition;
364 loop : for (int index = length - 1; index >= 0; index--) {
365 switch (scanner.source[index]) {
368 scanner.currentPosition--;
375 } catch (InvalidInputException e) {
376 if (!handleInvalidToken(e)) {
381 if (token == Scanner.TokenNameEOF)
384 /* ## MODIFYING the indentation level before generating new lines
385 and indentation in the output string
388 // Removes all the indentations made by statements not followed by a block
389 // except if the current token is ELSE, CATCH or if we are in a switch/case
390 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
393 if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
397 indentationLevel += popInclusiveUntil(TokenNameif);
399 // case TokenNamecatch :
400 // indentationLevel += popInclusiveUntil(TokenNamecatch);
402 // case TokenNamefinally :
403 // indentationLevel += popInclusiveUntil(TokenNamecatch);
405 case TokenNamewhile :
406 if (nlicsToken == TokenNamedo) {
407 indentationLevel += pop(TokenNamedo);
411 indentationLevel += popExclusiveUntilBlockOrCase();
412 // clear until a CASE, DEFAULT or BLOCK is encountered.
413 // Thus, the indentationLevel is correctly cleared either
414 // in a switch/case statement or in any other situation.
416 clearNonBlockIndents = false;
418 // returns to the indentation level created by the SWITCH keyword
419 // if the current token is a CASE or a DEFAULT
420 if (token == TokenNamecase || token == TokenNamedefault) {
421 indentationLevel += pop(TokenNamecase);
423 // if (token == Scanner.TokenNamethrows) {
424 // inThrowsClause = true;
426 if ((token == Scanner.TokenNameclass // || token == Scanner.TokenNameinterface
428 && previousToken != Scanner.TokenNameDOT) {
429 inClassOrInterfaceHeader = true;
432 /* ## APPEND newlines and indentations to the output string
434 // Do not add a new line between ELSE and IF, if the option elseIfOnSameLine is true.
435 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
436 // if (pendingNewlineAfterParen
437 // && previousCompilableToken == TokenNameelse
438 // && token == TokenNameif
439 // && options.compactElseIfMode) {
440 // pendingNewlineAfterParen = false;
441 // pendingNewLines = 0;
442 // indentationLevel += pop(TokenNameelse);
443 // // because else if is now one single statement,
444 // // the indentation level after it is increased by one and not by 2
445 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
447 // Add a newline & indent to the formatted source string if
448 // a for/if-else/while statement was scanned and there is no block
450 pendingNewlineAfterParen =
451 pendingNewlineAfterParen || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
452 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
453 pendingNewlineAfterParen = false;
455 // Do to add a newline & indent sequence if the current token is an
456 // open brace or a period or if the current token is a semi-colon and the
457 // previous token is a close paren.
458 // add a new line if a parenthesis belonging to a for() statement
459 // has been closed and the current token is not an opening brace
460 if (token != TokenNameLBRACE
461 && !isComment(token) // to avoid adding new line between else and a comment
462 && token != TokenNameDOT
463 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
465 currentLineIndentationLevel = indentationLevel;
467 pendingSpace = false;
469 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
471 if (constructionsCount > 0
472 && constructions[constructionsCount - 1] != BLOCK
473 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
474 currentLineIndentationLevel = indentationLevel - 1;
476 currentLineIndentationLevel = indentationLevel;
479 pendingSpace = false;
483 if (token == TokenNameLBRACE
484 && options.newLineBeforeOpeningBraceMode
485 && constructionsCount > 0
486 && constructions[constructionsCount - 1] == TokenNamedo) {
488 currentLineIndentationLevel = indentationLevel - 1;
490 pendingSpace = false;
493 if (token == TokenNameLBRACE && inThrowsClause) {
494 inThrowsClause = false;
495 if (options.newLineBeforeOpeningBraceMode) {
497 currentLineIndentationLevel = indentationLevel;
499 pendingSpace = false;
503 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
504 inClassOrInterfaceHeader = false;
505 if (options.newLineBeforeOpeningBraceMode) {
507 currentLineIndentationLevel = indentationLevel;
509 pendingSpace = false;
512 // Add pending new lines to the formatted source string.
513 // Note: pending new lines are not added if the current token
514 // is a single line comment or whitespace.
515 // if the comment is between parenthesis, there is no blank line preservation
516 // (if it's a one-line comment, a blank line is added after it).
517 if (((pendingNewLines > 0 && (!isComment(token)))
518 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
519 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
520 && token != Scanner.TokenNameWHITESPACE) {
522 // Do not add newline & indent between an adjoining close brace and
523 // close paren. Anonymous inner classes may use this form.
524 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
526 // OPTION (NewLineInCompoundStatement): do not add newline & indent
527 // between close brace and else, (do) while, catch, and finally if
528 // newlineInCompoundStatement is true.
529 boolean nlicsOption =
530 previousToken == TokenNameRBRACE
531 && !options.newlineInControlStatementMode
532 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo));
533 // || token == TokenNamecatch
534 // || token == TokenNamefinally);
536 // Do not add a newline & indent between a close brace and semi-colon.
537 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
539 // Do not add a new line & indent between a multiline comment and a opening brace
540 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
542 // Do not add a newline & indent between a close brace and a colon (in array assignments, for example).
543 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
545 // Add a newline and indent, if appropriate.
547 || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
549 // if clearAllBlankLinesMode=false, leaves the blank lines
550 // inserted by the user
551 // if clearAllBlankLinesMode=true, removes all of then
552 // and insert only blank lines required by the formatting.
553 if (!options.clearAllBlankLinesMode) {
554 // (isComment(token))
555 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
556 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
558 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
559 containsOpenCloseBraces = true;
560 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
561 if (isComment(previousToken)) {
562 newLine(pendingNewLines);
564 /* if (!(constructionsCount > 1
565 && constructions[constructionsCount-1] == NONINDENT_BLOCK
566 && (constructions[constructionsCount-2] == TokenNamefor
567 || constructions[constructionsCount-2] == TokenNamewhile))) {*/
568 if (options.newLineInEmptyBlockMode) {
569 if (inArrayAssignment) {
570 newLine(1); // array assigment with an empty block
572 newLine(pendingNewLines);
578 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment before the ';'
579 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC)
580 && token == TokenNameSEMICOLON)) {
581 newLine(pendingNewLines);
584 if (((previousCompilableToken == TokenNameSEMICOLON)
585 || (previousCompilableToken == TokenNameLBRACE)
586 || (previousCompilableToken == TokenNameRBRACE)
587 || (isComment(previousToken)))
588 && (token == TokenNameRBRACE)) {
589 indentationOffset = -1;
590 indentationLevel += popExclusiveUntilBlock();
592 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
594 currentLineIndentationLevel++;
596 currentLineIndentationLevel = indentationLevel + indentationOffset;
598 pendingSpace = false;
599 indentationOffset = 0;
602 newLinesInWhitespace = 0;
605 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
610 case TokenNameDOLLAR_LBRACE :
614 // case TokenNamefinally :
615 expectingOpenBrace = true;
616 pendingNewlineAfterParen = true;
617 indentationLevel += pushControlStatement(token);
620 case TokenNamedefault :
621 if (tokenBeforeColonCount == tokenBeforeColon.length) {
625 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
627 tokenBeforeColonCount);
629 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
630 indentationLevel += pushControlStatement(TokenNamecase);
632 case TokenNameQUESTION :
633 if (tokenBeforeColonCount == tokenBeforeColon.length) {
637 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]),
639 tokenBeforeColonCount);
641 tokenBeforeColon[tokenBeforeColonCount++] = token;
643 case TokenNameswitch :
646 case TokenNamewhile :
647 if (openParenthesisCount == openParenthesis.length) {
648 System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
650 openParenthesis[openParenthesisCount++] = 0;
651 expectingOpenBrace = true;
653 indentationLevel += pushControlStatement(token);
655 // case TokenNametry :
656 // pendingNewlineAfterParen = true;
657 // case TokenNamecatch :
658 // // several CATCH statements can be contiguous.
659 // // a CATCH is encountered pop until first CATCH (if a CATCH follows a TRY it works the same way,
660 // // as CATCH and TRY are the same token in the stack).
661 // expectingOpenBrace = true;
662 // indentationLevel += pushControlStatement(TokenNamecatch);
666 expectingOpenBrace = true;
667 indentationLevel += pushControlStatement(token);
672 case TokenNameLPAREN :
673 // if (previousToken == TokenNamesynchronized) {
674 // indentationLevel += pushControlStatement(previousToken);
676 // Put a space between the previous and current token if the
677 // previous token was not a keyword, open paren, logical
678 // compliment (eg: !), semi-colon, open brace, close brace,
680 if (previousCompilableToken != TokenNameLBRACKET
681 && previousToken != TokenNameIdentifier
682 && previousToken != 0
683 && previousToken != TokenNameNOT
684 && previousToken != TokenNameLPAREN
685 && previousToken != TokenNameTWIDDLE
686 && previousToken != TokenNameSEMICOLON
687 && previousToken != TokenNameLBRACE
688 && previousToken != TokenNameRBRACE) {
689 // && previousToken != TokenNamesuper
690 // && previousToken != TokenNamethis) {
693 // If in a for/if/while statement, increase the parenthesis count
694 // for the current openParenthesisCount
695 // else increase the count for stand alone parenthesis.
696 if (openParenthesisCount > 0)
697 openParenthesis[openParenthesisCount - 1]++;
699 openParenthesis[0]++;
701 pendingSpace = false;
704 case TokenNameRPAREN :
706 // Decrease the parenthesis count
707 // if there is no more unclosed parenthesis,
708 // a new line and indent may be append (depending on the next token).
709 if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
710 openParenthesis[openParenthesisCount - 1]--;
711 if (openParenthesis[openParenthesisCount - 1] <= 0) {
712 pendingNewlineAfterParen = true;
713 inAssignment = false;
714 openParenthesisCount--;
717 openParenthesis[0]--;
719 pendingSpace = false;
721 case TokenNameLBRACE :
722 if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
723 // if (previousCompilableToken == TokenNameRBRACKET) {
724 inArrayAssignment = true;
725 inAssignment = false;
727 if (inArrayAssignment) {
728 indentationLevel += pushBlock();
730 // Add new line and increase indentation level after open brace.
732 indentationLevel += pushBlock();
735 case TokenNameRBRACE :
736 if (dollarBraceCount > 0) {
740 if (previousCompilableToken == TokenNameRPAREN) {
741 pendingSpace = false;
743 if (inArrayAssignment) {
744 inArrayAssignment = false;
746 indentationLevel += popInclusiveUntilBlock();
749 indentationLevel += popInclusiveUntilBlock();
751 if (previousCompilableToken == TokenNameRPAREN) {
752 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
753 currentLineBuffer.append(options.lineSeparatorSequence);
754 increaseLineDelta(options.lineSeparatorSequence.length);
756 if (constructionsCount > 0) {
757 switch (constructions[constructionsCount - 1]) {
759 //indentationLevel += popExclusiveUntilBlock();
761 case TokenNameswitch :
764 // case TokenNametry :
765 // case TokenNamecatch :
766 // case TokenNamefinally :
767 case TokenNamewhile :
769 // case TokenNamesynchronized :
770 clearNonBlockIndents = true;
777 case TokenNameLBRACKET :
779 pendingSpace = false;
781 case TokenNameRBRACKET :
782 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
783 // if there is no left bracket to close, the right bracket is ignored.
784 pendingSpace = false;
786 case TokenNameCOMMA :
788 pendingSpace = false;
790 case TokenNameSEMICOLON :
792 // Do not generate line terminators in the definition of
793 // the for statement.
794 // if not in this case, jump a line and reduce indentation after the brace
795 // if the block it closes belongs to a conditional statement (if, while, do...).
796 if (openParenthesisCount <= 1) {
798 if (expectingOpenBrace) {
799 clearNonBlockIndents = true;
800 expectingOpenBrace = false;
803 inAssignment = false;
804 pendingSpace = false;
806 case TokenNamePLUS_PLUS :
807 case TokenNameMINUS_MINUS :
809 // Do not put a space between a post-increment/decrement
810 // and the identifier being modified.
811 if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET) {
812 pendingSpace = false;
815 case TokenNamePLUS : // previously ADDITION
816 case TokenNameMINUS :
818 // Handle the unary operators plus and minus via a flag
819 if (!isLiteralToken(previousToken)
820 && previousToken != TokenNameIdentifier
821 && previousToken != TokenNameRPAREN
822 && previousToken != TokenNameRBRACKET) {
823 unarySignModifier = 1;
826 case TokenNameCOLON :
827 // In a switch/case statement, add a newline & indent
828 // when a colon is encountered.
829 if (tokenBeforeColonCount > 0) {
830 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
833 tokenBeforeColonCount--;
836 case TokenNameEQUAL :
839 case Scanner.TokenNameCOMMENT_LINE :
842 currentLineIndentationLevel++;
844 break; // a line is always inserted after a one-line comment
845 case Scanner.TokenNameCOMMENT_PHPDOC :
846 case Scanner.TokenNameCOMMENT_BLOCK :
847 currentCommentOffset = getCurrentCommentOffset();
850 case Scanner.TokenNameWHITESPACE :
852 // Count the number of line terminators in the whitespace so
853 // line spacing can be preserved near comments.
854 char[] source = scanner.source;
855 newLinesInWhitespace = 0;
856 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
857 if (source[i] == '\r') {
859 if (source[++i] == '\n') {
860 newLinesInWhitespace++;
862 newLinesInWhitespace++;
865 newLinesInWhitespace++;
867 } else if (source[i] == '\n') {
868 newLinesInWhitespace++;
871 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
873 // case TokenNameHTML :
874 // // Add the next token to the formatted source string.
875 // // outputCurrentToken(token);
876 // int startPosition = scanner.startPosition;
878 // for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
879 // char currentCharacter = scanner.source[i];
880 // updateMappedPositions(i);
881 // currentLineBuffer.append(currentCharacter);
885 if ((token == TokenNameIdentifier) || isLiteralToken(token)) {
886 // || token == TokenNamesuper
887 // || token == TokenNamethis) {
889 // Do not put a space between a unary operator
890 // (eg: ++, --, +, -) and the identifier being modified.
891 if (previousToken == TokenNamePLUS_PLUS
892 || previousToken == TokenNameMINUS_MINUS
893 || (previousToken == TokenNameMINUS_GREATER &&
894 options.compactDereferencingMode) // ->
895 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
896 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
897 pendingSpace = false;
899 unarySignModifier = 0;
903 // Do not output whitespace tokens.
904 if (token != Scanner.TokenNameWHITESPACE) {
906 /* Add pending space to the formatted source string.
907 Do not output a space under the following circumstances:
908 1) this is the first pass
909 2) previous token is an open paren
910 3) previous token is a period
911 4) previous token is the logical compliment (eg: !)
912 5) previous token is the bitwise compliment (eg: ~)
913 6) previous token is the open bracket (eg: [)
914 7) in an assignment statement, if the previous token is an
915 open brace or the current token is a close brace
916 8) previous token is a single line comment
917 9) current token is a '->'
919 if (token == TokenNameMINUS_GREATER &&
920 options.compactDereferencingMode) pendingSpace = false;
922 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
925 && insertSpaceAfter(previousToken)
926 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
927 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
928 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
931 // Add the next token to the formatted source string.
932 outputCurrentToken(token);
933 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
935 currentLineBuffer.append(options.lineSeparatorSequence);
936 increaseLineDelta(options.lineSeparatorSequence.length);
940 // Whitespace tokens do not need to be remembered.
941 if (token != Scanner.TokenNameWHITESPACE) {
942 previousToken = token;
943 if (token != Scanner.TokenNameCOMMENT_BLOCK
944 && token != Scanner.TokenNameCOMMENT_LINE
945 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
946 previousCompilableToken = token;
950 output(copyRemainingSource());
952 // dump the last token of the source in the formatted output.
953 } catch (InvalidInputException e) {
954 output(copyRemainingSource());
956 // dump the last token of the source in the formatted output.
961 * Formats the char array <code>sourceString</code>,
962 * and returns a string containing the formatted version.
963 * @return the formatted ouput.
965 public String formatSourceString(String sourceString) {
966 char[] sourceChars = sourceString.toCharArray();
967 formattedSource = new StringBuffer(sourceChars.length);
968 scanner.setSource(sourceChars);
970 return formattedSource.toString();
974 * Formats the char array <code>sourceString</code>,
975 * and returns a string containing the formatted version.
976 * @param string the string to format
977 * @param indentationLevel the initial indentation level
978 * @return the formatted ouput.
980 public String format(String string, int indentationLevel) {
981 return format(string, indentationLevel, (int[]) null);
985 * Formats the char array <code>sourceString</code>,
986 * and returns a string containing the formatted version.
987 * The positions array is modified to contain the mapped positions.
988 * @param string the string to format
989 * @param indentationLevel the initial indentation level
990 * @param positions the array of positions to map
991 * @return the formatted ouput.
993 public String format(String string, int indentationLevel, int[] positions) {
994 return this.format(string, indentationLevel, positions, null);
997 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
998 if (lineSeparator != null) {
999 this.options.setLineSeparator(lineSeparator);
1001 if (positions != null) {
1002 this.setPositionsToMap(positions);
1003 this.setInitialIndentationLevel(indentationLevel);
1004 String formattedString = this.formatSourceString(string);
1005 int[] mappedPositions = this.getMappedPositions();
1006 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1007 return formattedString;
1009 this.setInitialIndentationLevel(indentationLevel);
1010 return this.formatSourceString(string);
1014 * Formats the char array <code>sourceString</code>,
1015 * and returns a string containing the formatted version. The initial indentation level is 0.
1016 * @param string the string to format
1017 * @return the formatted ouput.
1019 public String format(String string) {
1020 return this.format(string, 0, (int[]) null);
1024 * Formats a given source string, starting indenting it at a particular
1025 * depth and using the given options
1027 * @deprecated backport 1.0 internal functionality
1029 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1030 CodeFormatter formatter = new CodeFormatter(options);
1031 formatter.setInitialIndentationLevel(initialIndentationLevel);
1032 return formatter.formatSourceString(sourceString);
1036 * Returns the number of characters and tab char between the beginning of the line
1037 * and the beginning of the comment.
1039 private int getCurrentCommentOffset() {
1040 int linePtr = scanner.linePtr;
1041 // if there is no beginning of line, return 0.
1045 int beginningOfLine = scanner.lineEnds[linePtr];
1046 int currentStartPosition = scanner.startPosition;
1047 char[] source = scanner.source;
1049 // find the position of the beginning of the line containing the comment
1050 while (beginningOfLine > currentStartPosition) {
1052 beginningOfLine = scanner.lineEnds[--linePtr];
1054 beginningOfLine = 0;
1058 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1059 char currentCharacter = source[i];
1060 switch (currentCharacter) {
1062 offset += options.tabSize;
1078 * Returns an array of descriptions for the configurable options.
1079 * The descriptions may be changed and passed back to a different
1082 * @deprecated backport 1.0 internal functionality
1084 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1085 String componentName = CodeFormatter.class.getName();
1086 FormatterOptions options = new FormatterOptions();
1087 return new ConfigurableOption[] { new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1), //$NON-NLS-1$
1088 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1), //$NON-NLS-1$
1089 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1), //$NON-NLS-1$
1090 // new ConfigurableOption(componentName, "newline.elseIf", locale, options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1091 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1), //$NON-NLS-1$
1092 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength), //$NON-NLS-1$
1093 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1), //$NON-NLS-1$
1094 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1), //$NON-NLS-1$
1095 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1100 * Returns the array of mapped positions.
1101 * Returns null is no positions have been set.
1103 * @deprecated There is no need to retrieve the mapped positions anymore.
1105 public int[] getMappedPositions() {
1106 return mappedPositions;
1110 * Returns the priority of the token given as argument<br>
1111 * The most prioritary the token is, the smallest the return value is.
1112 * @return the priority of <code>token</code>
1113 * @param token the token of which the priority is requested
1115 private static int getTokenPriority(int token) {
1117 case TokenNameextends :
1118 // case TokenNameimplements :
1119 // case TokenNamethrows :
1121 case TokenNameSEMICOLON : // ;
1123 case TokenNameCOMMA : // ,
1125 case TokenNameEQUAL : // =
1127 case TokenNameAND_AND : // &&
1128 case TokenNameOR_OR : // ||
1130 case TokenNameQUESTION : // ?
1131 case TokenNameCOLON : // :
1132 return 50; // it's better cutting on ?: than on ;
1133 case TokenNameEQUAL_EQUAL : // ==
1134 case TokenNameEQUAL_EQUAL_EQUAL : // ===
1135 case TokenNameNOT_EQUAL : // !=
1136 case TokenNameNOT_EQUAL_EQUAL : // !=
1138 case TokenNameLESS : // <
1139 case TokenNameLESS_EQUAL : // <=
1140 case TokenNameGREATER : // >
1141 case TokenNameGREATER_EQUAL : // >=
1142 // case TokenNameinstanceof : // instanceof
1144 case TokenNamePLUS : // +
1145 case TokenNameMINUS : // -
1147 case TokenNameMULTIPLY : // *
1148 case TokenNameDIVIDE : // /
1149 case TokenNameREMAINDER : // %
1151 case TokenNameLEFT_SHIFT : // <<
1152 case TokenNameRIGHT_SHIFT : // >>
1153 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1155 case TokenNameAND : // &
1156 case TokenNameOR : // |
1157 case TokenNameXOR : // ^
1159 case TokenNameMULTIPLY_EQUAL : // *=
1160 case TokenNameDIVIDE_EQUAL : // /=
1161 case TokenNameREMAINDER_EQUAL : // %=
1162 case TokenNamePLUS_EQUAL : // +=
1163 case TokenNameMINUS_EQUAL : // -=
1164 case TokenNameLEFT_SHIFT_EQUAL : // <<=
1165 case TokenNameRIGHT_SHIFT_EQUAL : // >>=
1166 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1167 case TokenNameAND_EQUAL : // &=
1168 case TokenNameXOR_EQUAL : // ^=
1169 case TokenNameOR_EQUAL : // |=
1171 case TokenNameDOT : // .
1174 return Integer.MAX_VALUE;
1179 * Handles the exception raised when an invalid token is encountered.
1180 * Returns true if the exception has been handled, false otherwise.
1182 private boolean handleInvalidToken(Exception e) {
1183 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1184 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1185 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1191 private final void increaseGlobalDelta(int offset) {
1192 globalDelta += offset;
1195 private final void increaseLineDelta(int offset) {
1196 lineDelta += offset;
1199 private final void increaseSplitDelta(int offset) {
1200 splitDelta += offset;
1204 * Returns true if a space has to be inserted after <code>operator</code>
1207 private boolean insertSpaceAfter(int token) {
1209 case TokenNameLPAREN :
1211 case TokenNameTWIDDLE :
1213 case 0 : // no token
1214 case TokenNameLBRACKET :
1215 case Scanner.TokenNameCOMMENT_LINE :
1223 * Returns true if a space has to be inserted before <code>operator</code>
1224 * false otherwise.<br>
1225 * Cannot be static as it uses the code formatter options
1226 * (to know if the compact assignment mode is on).
1228 private boolean insertSpaceBefore(int token) {
1230 case TokenNameEQUAL :
1231 return (!options.compactAssignmentMode);
1237 private static boolean isComment(int token) {
1239 token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE || token == Scanner.TokenNameCOMMENT_PHPDOC;
1243 private static boolean isLiteralToken(int token) {
1244 boolean result = token == TokenNameIntegerLiteral
1245 // || token == TokenNameLongLiteral
1246 // || token == TokenNameFloatingPointLiteral
1247 || token == TokenNameDoubleLiteral
1248 // || token == TokenNameCharacterLiteral
1249 || token == TokenNameStringLiteral;
1254 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1255 * it is split and the result is dumped in <code>formattedSource</code>
1256 * @param newLineCount the number of new lines to append
1258 private void newLine(int newLineCount) {
1260 // format current line
1262 beginningOfLineIndex = formattedSource.length();
1263 String currentLine = currentLineBuffer.toString();
1264 if (containsOpenCloseBraces) {
1265 containsOpenCloseBraces = false;
1266 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1267 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1269 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1271 // dump line break(s)
1272 for (int i = 0; i < newLineCount; i++) {
1273 formattedSource.append(options.lineSeparatorSequence);
1274 increaseSplitDelta(options.lineSeparatorSequence.length);
1276 // reset formatter for next line
1277 int currentLength = currentLine.length();
1278 currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1280 increaseGlobalDelta(splitDelta);
1281 increaseGlobalDelta(lineDelta);
1283 currentLineIndentationLevel = initialIndentationLevel;
1286 private String operatorString(int operator) {
1288 case TokenNameextends :
1289 return "extends"; //$NON-NLS-1$
1291 // case TokenNameimplements :
1292 // return "implements"; //$NON-NLS-1$
1294 // case TokenNamethrows :
1295 // return "throws"; //$NON-NLS-1$
1297 case TokenNameSEMICOLON : // ;
1298 return ";"; //$NON-NLS-1$
1300 case TokenNameCOMMA : // ,
1301 return ","; //$NON-NLS-1$
1303 case TokenNameEQUAL : // =
1304 return "="; //$NON-NLS-1$
1306 case TokenNameAND_AND : // && (15.22)
1307 return "&&"; //$NON-NLS-1$
1309 case TokenNameOR_OR : // || (15.23)
1310 return "||"; //$NON-NLS-1$
1312 case TokenNameQUESTION : // ? (15.24)
1313 return "?"; //$NON-NLS-1$
1315 case TokenNameCOLON : // : (15.24)
1316 return ":"; //$NON-NLS-1$
1318 case TokenNamePAAMAYIM_NEKUDOTAYIM : // : (15.24)
1319 return "::"; //$NON-NLS-1$
1321 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1322 return "=="; //$NON-NLS-1$
1324 case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1325 return "==="; //$NON-NLS-1$
1327 case TokenNameEQUAL_GREATER : // -= (15.25.2)
1328 return "=>"; //$NON-NLS-1$
1330 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1331 return "!="; //$NON-NLS-1$
1333 case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1334 return "!=="; //$NON-NLS-1$
1336 case TokenNameLESS : // < (15.19.1)
1337 return "<"; //$NON-NLS-1$
1339 case TokenNameLESS_EQUAL : // <= (15.19.1)
1340 return "<="; //$NON-NLS-1$
1342 case TokenNameGREATER : // > (15.19.1)
1343 return ">"; //$NON-NLS-1$
1345 case TokenNameGREATER_EQUAL : // >= (15.19.1)
1346 return ">="; //$NON-NLS-1$
1348 // case TokenNameinstanceof : // instanceof
1349 // return "instanceof"; //$NON-NLS-1$
1351 case TokenNamePLUS : // + (15.17, 15.17.2)
1352 return "+"; //$NON-NLS-1$
1354 case TokenNameMINUS : // - (15.17.2)
1355 return "-"; //$NON-NLS-1$
1357 case TokenNameMULTIPLY : // * (15.16.1)
1358 return "*"; //$NON-NLS-1$
1360 case TokenNameDIVIDE : // / (15.16.2)
1361 return "/"; //$NON-NLS-1$
1363 case TokenNameREMAINDER : // % (15.16.3)
1364 return "%"; //$NON-NLS-1$
1366 case TokenNameLEFT_SHIFT : // << (15.18)
1367 return "<<"; //$NON-NLS-1$
1369 case TokenNameRIGHT_SHIFT : // >> (15.18)
1370 return ">>"; //$NON-NLS-1$
1372 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1373 // return ">>>"; //$NON-NLS-1$
1375 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
1376 return "&"; //$NON-NLS-1$
1378 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
1379 return "|"; //$NON-NLS-1$
1381 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
1382 return "^"; //$NON-NLS-1$
1384 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
1385 return "*="; //$NON-NLS-1$
1387 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
1388 return "/="; //$NON-NLS-1$
1390 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
1391 return "%="; //$NON-NLS-1$
1393 case TokenNamePLUS_EQUAL : // += (15.25.2)
1394 return "+="; //$NON-NLS-1$
1396 case TokenNameMINUS_EQUAL : // -= (15.25.2)
1397 return "-="; //$NON-NLS-1$
1399 case TokenNameMINUS_GREATER : // -= (15.25.2)
1400 return "->"; //$NON-NLS-1$
1402 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
1403 return "<<="; //$NON-NLS-1$
1405 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
1406 return ">>="; //$NON-NLS-1$
1408 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1409 // return ">>>="; //$NON-NLS-1$
1411 case TokenNameAND_EQUAL : // &= (15.25.2)
1412 return "&="; //$NON-NLS-1$
1414 case TokenNameXOR_EQUAL : // ^= (15.25.2)
1415 return "^="; //$NON-NLS-1$
1417 case TokenNameOR_EQUAL : // |= (15.25.2)
1418 return "|="; //$NON-NLS-1$
1420 case TokenNameDOT : // .
1421 return "."; //$NON-NLS-1$
1424 return ""; //$NON-NLS-1$
1429 * Appends <code>stringToOutput</code> to the formatted output.<br>
1430 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1432 private void output(String stringToOutput) {
1433 char currentCharacter;
1434 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1435 currentCharacter = stringToOutput.charAt(i);
1436 if (currentCharacter != '\t') {
1437 currentLineBuffer.append(currentCharacter);
1443 * Appends <code>token</code> to the formatted output.<br>
1444 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1446 private void outputCurrentToken(int token) {
1447 char[] source = scanner.source;
1448 int startPosition = scanner.startPosition;
1451 case Scanner.TokenNameCOMMENT_PHPDOC :
1452 case Scanner.TokenNameCOMMENT_BLOCK :
1453 case Scanner.TokenNameCOMMENT_LINE :
1454 boolean endOfLine = false;
1455 int currentCommentOffset = getCurrentCommentOffset();
1456 int beginningOfLineSpaces = 0;
1458 currentCommentOffset = getCurrentCommentOffset();
1459 beginningOfLineSpaces = 0;
1460 boolean pendingCarriageReturn = false;
1461 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1462 char currentCharacter = source[i];
1463 updateMappedPositions(i);
1464 switch (currentCharacter) {
1466 pendingCarriageReturn = true;
1470 if (pendingCarriageReturn) {
1471 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1473 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1475 pendingCarriageReturn = false;
1476 currentLineBuffer.append(options.lineSeparatorSequence);
1477 beginningOfLineSpaces = 0;
1481 if (pendingCarriageReturn) {
1482 pendingCarriageReturn = false;
1483 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1484 currentLineBuffer.append(options.lineSeparatorSequence);
1485 beginningOfLineSpaces = 0;
1489 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1490 beginningOfLineSpaces += options.tabSize;
1491 if (beginningOfLineSpaces > currentCommentOffset) {
1492 currentLineBuffer.append(currentCharacter);
1494 increaseGlobalDelta(-1);
1497 currentLineBuffer.append(currentCharacter);
1501 if (pendingCarriageReturn) {
1502 pendingCarriageReturn = false;
1503 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1504 currentLineBuffer.append(options.lineSeparatorSequence);
1505 beginningOfLineSpaces = 0;
1509 // we remove a maximum of currentCommentOffset characters (tabs are converted to space numbers).
1510 beginningOfLineSpaces++;
1511 if (beginningOfLineSpaces > currentCommentOffset) {
1512 currentLineBuffer.append(currentCharacter);
1514 increaseGlobalDelta(-1);
1517 currentLineBuffer.append(currentCharacter);
1521 if (pendingCarriageReturn) {
1522 pendingCarriageReturn = false;
1523 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1524 currentLineBuffer.append(options.lineSeparatorSequence);
1525 beginningOfLineSpaces = 0;
1528 beginningOfLineSpaces = 0;
1529 currentLineBuffer.append(currentCharacter);
1534 updateMappedPositions(scanner.currentPosition - 1);
1535 multipleLineCommentCounter++;
1538 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1539 char currentCharacter = source[i];
1540 updateMappedPositions(i);
1541 currentLineBuffer.append(currentCharacter);
1547 * Outputs <code>currentString</code>:<br>
1548 * <ul><li>If its length is < maxLineLength, output
1549 * <li>Otherwise it is split.</ul>
1550 * @param currentString string to output
1551 * @param preIndented whether the string to output was pre-indented
1552 * @param depth number of indentation to put in front of <code>currentString</code>
1553 * @param operator value of the operator belonging to <code>currentString</code>.
1555 private void outputLine(
1556 String currentString,
1557 boolean preIndented,
1561 int[] startSubstringIndexes,
1562 int offsetInGlobalLine) {
1564 boolean emptyFirstSubString = false;
1565 String operatorString = operatorString(operator);
1566 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1567 boolean placeOperatorAhead = !placeOperatorBehind;
1569 // dump prefix operator?
1570 if (placeOperatorAhead) {
1575 if (operator != 0) {
1576 if (insertSpaceBefore(operator)) {
1577 formattedSource.append(' ');
1578 increaseSplitDelta(1);
1580 formattedSource.append(operatorString);
1581 increaseSplitDelta(operatorString.length());
1583 if (insertSpaceAfter(operator) // && operator != TokenNameimplements
1584 && operator != TokenNameextends) {
1585 // && operator != TokenNamethrows) {
1586 formattedSource.append(' ');
1587 increaseSplitDelta(1);
1591 SplitLine splitLine = null;
1592 if (options.maxLineLength == 0
1593 || getLength(currentString, depth) < options.maxLineLength
1594 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1596 // depending on the type of operator, outputs new line before of after dumping it
1597 // indent before postfix operator
1598 // indent also when the line cannot be split
1599 if (operator == TokenNameextends) {
1600 // || operator == TokenNameimplements
1601 // || operator == TokenNamethrows) {
1602 formattedSource.append(' ');
1603 increaseSplitDelta(1);
1605 if (placeOperatorBehind) {
1610 int max = currentString.length();
1611 if (multipleLineCommentCounter != 0) {
1613 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1614 String line = reader.readLine();
1615 while (line != null) {
1616 updateMappedPositionsWhileSplitting(
1617 beginningOfLineIndex,
1618 beginningOfLineIndex + line.length() + options.lineSeparatorSequence.length);
1619 formattedSource.append(line);
1620 beginningOfLineIndex = beginningOfLineIndex + line.length();
1621 if ((line = reader.readLine()) != null) {
1622 formattedSource.append(options.lineSeparatorSequence);
1623 beginningOfLineIndex += options.lineSeparatorSequence.length;
1624 dumpTab(currentLineIndentationLevel);
1628 } catch (IOException e) {
1629 e.printStackTrace();
1632 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1633 for (int i = 0; i < max; i++) {
1634 char currentChar = currentString.charAt(i);
1635 switch (currentChar) {
1640 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when split with a comment inside a condition
1641 // a substring cannot end with a lineSeparatorSequence,
1642 // except if it has been added by format() after a one-line comment
1643 formattedSource.append(options.lineSeparatorSequence);
1645 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1650 formattedSource.append(currentChar);
1654 // update positions inside the mappedPositions table
1655 if (substringIndex != -1) {
1656 if (multipleLineCommentCounter == 0) {
1657 int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1658 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1661 // compute the splitDelta resulting with the operator and blank removal
1662 if (substringIndex + 1 != startSubstringIndexes.length) {
1663 increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1666 // dump postfix operator?
1667 if (placeOperatorBehind) {
1668 if (insertSpaceBefore(operator)) {
1669 formattedSource.append(' ');
1670 if (operator != 0) {
1671 increaseSplitDelta(1);
1674 formattedSource.append(operatorString);
1675 if (operator != 0) {
1676 increaseSplitDelta(operatorString.length());
1681 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1682 // extends has to stand alone on a line when currentString has been split.
1683 if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1684 // || operator == TokenNameimplements
1685 // || operator == TokenNamethrows)) {
1686 formattedSource.append(options.lineSeparatorSequence);
1687 increaseSplitDelta(options.lineSeparatorSequence.length);
1690 if (operator == TokenNameextends) {
1691 // || operator == TokenNameimplements
1692 // || operator == TokenNamethrows) {
1693 formattedSource.append(' ');
1694 increaseSplitDelta(1);
1697 // perform actual splitting
1698 String result[] = splitLine.substrings;
1699 int[] splitOperators = splitLine.operators;
1701 if (result[0].length() == 0) {
1702 // when the substring 0 is null, the substring 1 is correctly indented.
1704 emptyFirstSubString = true;
1706 // the operator going in front of the result[0] string is the operator parameter
1707 for (int i = 0, max = result.length; i < max; i++) {
1708 // the new depth is the current one if this is the first substring,
1709 // the current one + 1 otherwise.
1710 // if the substring is a comment, use the current indentation Level instead of the depth
1711 // (-1 because the ouputline increases depth).
1712 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line comments)
1713 String currentResult = result[i];
1715 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1716 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1717 || currentResult.startsWith("//")) //$NON-NLS-1$
1718 ? indentationLevel - 1 : depth;
1721 i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1722 i == 0 ? newDepth : newDepth + 1,
1725 splitLine.startSubstringsIndexes,
1726 currentString.indexOf(currentResult));
1728 formattedSource.append(options.lineSeparatorSequence);
1729 increaseSplitDelta(options.lineSeparatorSequence.length);
1733 if (result.length == splitOperators.length - 1) {
1734 int lastOperator = splitOperators[result.length];
1735 String lastOperatorString = operatorString(lastOperator);
1736 formattedSource.append(options.lineSeparatorSequence);
1737 increaseSplitDelta(options.lineSeparatorSequence.length);
1739 if (breakLineBeforeOperator(lastOperator)) {
1741 if (lastOperator != 0) {
1742 if (insertSpaceBefore(lastOperator)) {
1743 formattedSource.append(' ');
1744 increaseSplitDelta(1);
1746 formattedSource.append(lastOperatorString);
1747 increaseSplitDelta(lastOperatorString.length());
1749 if (insertSpaceAfter(lastOperator) // && lastOperator != TokenNameimplements
1750 && lastOperator != TokenNameextends) {
1751 // && lastOperator != TokenNamethrows) {
1752 formattedSource.append(' ');
1753 increaseSplitDelta(1);
1758 if (placeOperatorBehind) {
1759 if (insertSpaceBefore(operator)) {
1760 formattedSource.append(' ');
1761 increaseSplitDelta(1);
1763 formattedSource.append(operatorString);
1764 //increaseSplitDelta(operatorString.length());
1769 * Pops the top statement of the stack if it is <code>token</code>
1771 private int pop(int token) {
1773 if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1775 constructionsCount--;
1781 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1783 private int popBlock() {
1785 if ((constructionsCount > 0)
1786 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1787 if (constructions[constructionsCount - 1] == BLOCK)
1789 constructionsCount--;
1795 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1796 * Does not remove <code>token</code> from the stack.
1797 * @param token the token to be left as the top of the stack
1799 private int popExclusiveUntil(int token) {
1801 int startCount = constructionsCount;
1802 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1803 if (constructions[i] != NONINDENT_BLOCK)
1805 constructionsCount--;
1811 * Pops elements until the stack is empty or the top element is
1812 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1813 * Does not remove it from the stack.
1815 private int popExclusiveUntilBlock() {
1816 int startCount = constructionsCount;
1818 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1819 constructionsCount--;
1826 * Pops elements until the stack is empty or the top element is
1827 * a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1828 * Does not remove it from the stack.
1830 private int popExclusiveUntilBlockOrCase() {
1831 int startCount = constructionsCount;
1833 for (int i = startCount - 1;
1834 i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK && constructions[i] != TokenNamecase;
1836 constructionsCount--;
1843 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1844 * Removes <code>token</code> from the stack too.
1845 * @param token the token to remove from the stack
1847 private int popInclusiveUntil(int token) {
1848 int startCount = constructionsCount;
1850 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1851 if (constructions[i] != NONINDENT_BLOCK)
1853 constructionsCount--;
1855 if (constructionsCount > 0) {
1856 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1858 constructionsCount--;
1864 * Pops elements until the stack is empty or the top element is
1865 * a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1866 * Does not remove it from the stack.
1868 private int popInclusiveUntilBlock() {
1869 int startCount = constructionsCount;
1871 for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1873 constructionsCount--;
1875 if (constructionsCount > 0) {
1876 if (constructions[constructionsCount - 1] == BLOCK)
1878 constructionsCount--;
1884 * Pushes a block in the stack.<br>
1885 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>,
1886 * pushes <code>NONINDENT_BLOCK</code> otherwise.
1887 * Creates a new bigger array if the current one is full.
1889 private int pushBlock() {
1891 if (constructionsCount == constructions.length)
1892 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1894 if ((constructionsCount == 0)
1895 || (constructions[constructionsCount - 1] == BLOCK)
1896 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1897 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1899 constructions[constructionsCount++] = BLOCK;
1901 constructions[constructionsCount++] = NONINDENT_BLOCK;
1907 * Pushes <code>token</code>.<br>
1908 * Creates a new bigger array if the current one is full.
1910 private int pushControlStatement(int token) {
1911 if (constructionsCount == constructions.length)
1912 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1913 constructions[constructionsCount++] = token;
1917 private static boolean separateFirstArgumentOn(int currentToken) {
1918 //return (currentToken == TokenNameCOMMA || currentToken == TokenNameSEMICOLON);
1919 return currentToken != TokenNameif
1920 && currentToken != TokenNameLPAREN
1921 && currentToken != TokenNameNOT
1922 && currentToken != TokenNamewhile
1923 && currentToken != TokenNamefor
1924 && currentToken != TokenNameswitch;
1928 * Set the positions to map. The mapped positions should be retrieved using the
1929 * getMappedPositions() method.
1930 * @param positions int[]
1931 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1933 * @see #getMappedPositions()
1935 public void setPositionsToMap(int[] positions) {
1936 positionsToMap = positions;
1939 mappedPositions = new int[positions.length];
1943 * Appends a space character to the current line buffer.
1945 private void space() {
1946 currentLineBuffer.append(' ');
1947 increaseLineDelta(1);
1951 * Splits <code>stringToSplit</code> on the top level token<br>
1952 * If there are several identical token at the same level,
1953 * the string is cut into many pieces.
1954 * @return an object containing the operator and all the substrings
1955 * or null if the string cannot be split
1957 public SplitLine split(String stringToSplit) {
1958 return split(stringToSplit, 0);
1962 * Splits <code>stringToSplit</code> on the top level token<br>
1963 * If there are several identical token at the same level,
1964 * the string is cut into many pieces.
1965 * @return an object containing the operator and all the substrings
1966 * or null if the string cannot be split
1968 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1970 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
1971 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1973 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1977 int currentToken = 0;
1978 int splitTokenType = 0;
1979 int splitTokenDepth = Integer.MAX_VALUE;
1980 int splitTokenPriority = Integer.MAX_VALUE;
1982 int[] substringsStartPositions = new int[10];
1983 // contains the start position of substrings
1984 int[] substringsEndPositions = new int[10];
1985 // contains the start position of substrings
1986 int substringsCount = 1; // index in the substringsStartPosition array
1987 int[] splitOperators = new int[10];
1988 // contains the start position of substrings
1989 int splitOperatorsCount = 0; // index in the substringsStartPosition array
1990 int[] openParenthesisPosition = new int[10];
1991 int openParenthesisPositionCount = 0;
1993 int lastOpenParenthesisPosition = -1;
1994 // used to remember the position of the 1st open parenthesis
1995 // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
1996 // setup the scanner with a new source
1997 int lastCommentStartPosition = -1;
1998 // to remember the start position of the last comment
1999 int firstTokenOnLine = -1;
2000 // to remember the first token of the line
2001 int previousToken = -1;
2002 // to remember the previous token.
2003 splitScanner.setSource(stringToSplit.toCharArray());
2008 // takes the next token
2010 if (currentToken != Scanner.TokenNameWHITESPACE)
2011 previousToken = currentToken;
2012 currentToken = splitScanner.getNextToken();
2013 if (Scanner.DEBUG) {
2014 int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2015 int currentStartPosition = splitScanner.getCurrentTokenStartPosition();
2017 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
2018 System.out.println(scanner.toStringAction(currentToken));
2020 } catch (InvalidInputException e) {
2021 if (!handleInvalidToken(e))
2024 // this value is not modify when an exception is raised.
2026 if (currentToken == TokenNameEOF)
2029 if (firstTokenOnLine == -1) {
2030 firstTokenOnLine = currentToken;
2032 switch (currentToken) {
2033 case TokenNameRBRACE :
2034 case TokenNameRPAREN :
2035 if (openParenthesisPositionCount > 0) {
2036 if (openParenthesisPositionCount == 1 && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2037 lastOpenParenthesisPosition = openParenthesisPosition[0];
2039 (splitTokenDepth == Integer.MAX_VALUE)
2040 || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2042 splitTokenDepth = openParenthesisPositionCount;
2043 splitTokenPriority = Integer.MAX_VALUE;
2044 substringsStartPositions[0] = 0;
2045 // better token means the whole line until now is the first substring
2046 substringsCount = 1; // resets the count of substrings
2047 substringsEndPositions[0] = openParenthesisPosition[0];
2048 // substring ends on operator start
2049 position = openParenthesisPosition[0];
2050 // the string mustn't be cut before the closing parenthesis but after the opening one.
2051 splitOperatorsCount = 1; // resets the count of split operators
2052 splitOperators[0] = 0;
2054 openParenthesisPositionCount--;
2057 case TokenNameLBRACE :
2058 case TokenNameLPAREN :
2059 if (openParenthesisPositionCount == openParenthesisPosition.length) {
2061 openParenthesisPosition,
2063 (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2065 openParenthesisPositionCount);
2067 openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2068 if (currentToken == TokenNameLPAREN && previousToken == TokenNameRPAREN) {
2069 openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2072 case TokenNameSEMICOLON : // ;
2073 case TokenNameCOMMA : // ,
2074 case TokenNameEQUAL : // =
2075 if (openParenthesisPositionCount < splitTokenDepth
2076 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2077 // the current token is better than the one we currently have
2078 // (in level or in priority if same level)
2079 // reset the substringsCount
2080 splitTokenDepth = openParenthesisPositionCount;
2081 splitTokenType = currentToken;
2082 splitTokenPriority = getTokenPriority(currentToken);
2083 substringsStartPositions[0] = 0;
2084 // better token means the whole line until now is the first substring
2086 if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2087 substringsCount = 2; // resets the count of substrings
2089 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2090 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2091 substringsEndPositions[1] = splitScanner.startPosition;
2092 splitOperatorsCount = 2; // resets the count of split operators
2093 splitOperators[0] = 0;
2094 splitOperators[1] = currentToken;
2095 position = splitScanner.currentPosition;
2096 // next substring will start from operator end
2098 substringsCount = 1; // resets the count of substrings
2100 substringsEndPositions[0] = splitScanner.startPosition;
2101 // substring ends on operator start
2102 position = splitScanner.currentPosition;
2103 // next substring will start from operator end
2104 splitOperatorsCount = 1; // resets the count of split operators
2105 splitOperators[0] = currentToken;
2108 if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2109 && splitTokenType != TokenNameEQUAL
2110 && currentToken != TokenNameEQUAL) {
2111 // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after split
2112 // take only the 1st = into account.
2113 // if another token with the same priority is found,
2114 // push the start position of the substring and
2115 // push the token into the stack.
2116 // create a new array object if the current one is full.
2117 if (substringsCount == substringsStartPositions.length) {
2119 substringsStartPositions,
2121 (substringsStartPositions = new int[substringsCount * 2]),
2125 substringsEndPositions,
2127 (substringsEndPositions = new int[substringsCount * 2]),
2131 if (splitOperatorsCount == splitOperators.length) {
2132 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2134 substringsStartPositions[substringsCount] = position;
2135 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2136 // substring ends on operator start
2137 position = splitScanner.currentPosition;
2138 // next substring will start from operator end
2139 splitOperators[splitOperatorsCount++] = currentToken;
2144 case TokenNameCOLON : // : (15.24)
2145 // see 1FK7C5R, we only split on a colon, when it is associated with a question-mark.
2146 // indeed it might appear also behind a case statement, and we do not to break at this point.
2147 if ((splitOperatorsCount == 0) || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2150 case TokenNameextends :
2151 // case TokenNameimplements :
2152 // case TokenNamethrows :
2154 case TokenNameDOT : // .
2155 case TokenNameMULTIPLY : // * (15.16.1)
2156 case TokenNameDIVIDE : // / (15.16.2)
2157 case TokenNameREMAINDER : // % (15.16.3)
2158 case TokenNamePLUS : // + (15.17, 15.17.2)
2159 case TokenNameMINUS : // - (15.17.2)
2160 case TokenNameLEFT_SHIFT : // << (15.18)
2161 case TokenNameRIGHT_SHIFT : // >> (15.18)
2162 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2163 case TokenNameLESS : // < (15.19.1)
2164 case TokenNameLESS_EQUAL : // <= (15.19.1)
2165 case TokenNameGREATER : // > (15.19.1)
2166 case TokenNameGREATER_EQUAL : // >= (15.19.1)
2167 // case TokenNameinstanceof : // instanceof
2168 case TokenNameEQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2169 case TokenNameEQUAL_EQUAL_EQUAL : // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2170 case TokenNameNOT_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2171 case TokenNameNOT_EQUAL_EQUAL : // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2172 case TokenNameAND : // & (15.21, 15.21.1, 15.21.2)
2173 case TokenNameOR : // | (15.21, 15.21.1, 15.21.2)
2174 case TokenNameXOR : // ^ (15.21, 15.21.1, 15.21.2)
2175 case TokenNameAND_AND : // && (15.22)
2176 case TokenNameOR_OR : // || (15.23)
2177 case TokenNameQUESTION : // ? (15.24)
2178 case TokenNameMULTIPLY_EQUAL : // *= (15.25.2)
2179 case TokenNameDIVIDE_EQUAL : // /= (15.25.2)
2180 case TokenNameREMAINDER_EQUAL : // %= (15.25.2)
2181 case TokenNamePLUS_EQUAL : // += (15.25.2)
2182 case TokenNameMINUS_EQUAL : // -= (15.25.2)
2183 case TokenNameLEFT_SHIFT_EQUAL : // <<= (15.25.2)
2184 case TokenNameRIGHT_SHIFT_EQUAL : // >>= (15.25.2)
2185 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2186 case TokenNameAND_EQUAL : // &= (15.25.2)
2187 case TokenNameXOR_EQUAL : // ^= (15.25.2)
2188 case TokenNameOR_EQUAL : // |= (15.25.2)
2190 if ((openParenthesisPositionCount < splitTokenDepth
2191 || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2192 && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS)
2193 && (previousToken == TokenNameLBRACE || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2194 // the current token is better than the one we currently have
2195 // (in level or in priority if same level)
2196 // reset the substringsCount
2197 splitTokenDepth = openParenthesisPositionCount;
2198 splitTokenType = currentToken;
2199 splitTokenPriority = getTokenPriority(currentToken);
2200 substringsStartPositions[0] = 0;
2201 // better token means the whole line until now is the first substring
2203 if (separateFirstArgumentOn(firstTokenOnLine) && openParenthesisPositionCount > 0) {
2204 substringsCount = 2; // resets the count of substrings
2206 substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2207 substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2208 substringsEndPositions[1] = splitScanner.startPosition;
2209 splitOperatorsCount = 3; // resets the count of split operators
2210 splitOperators[0] = 0;
2211 splitOperators[1] = 0;
2212 splitOperators[2] = currentToken;
2213 position = splitScanner.currentPosition;
2214 // next substring will start from operator end
2216 substringsCount = 1; // resets the count of substrings
2218 substringsEndPositions[0] = splitScanner.startPosition;
2219 // substring ends on operator start
2220 position = splitScanner.currentPosition;
2221 // next substring will start from operator end
2222 splitOperatorsCount = 2; // resets the count of split operators
2223 splitOperators[0] = 0;
2224 // nothing for first operand since operator will be inserted in front of the second operand
2225 splitOperators[1] = currentToken;
2229 if (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken)) {
2230 // if another token with the same priority is found,
2231 // push the start position of the substring and
2232 // push the token into the stack.
2233 // create a new array object if the current one is full.
2234 if (substringsCount == substringsStartPositions.length) {
2236 substringsStartPositions,
2238 (substringsStartPositions = new int[substringsCount * 2]),
2242 substringsEndPositions,
2244 (substringsEndPositions = new int[substringsCount * 2]),
2248 if (splitOperatorsCount == splitOperators.length) {
2249 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2251 substringsStartPositions[substringsCount] = position;
2252 substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2253 // substring ends on operator start
2254 position = splitScanner.currentPosition;
2255 // next substring will start from operator end
2256 splitOperators[splitOperatorsCount++] = currentToken;
2262 if (isComment(currentToken)) {
2263 lastCommentStartPosition = splitScanner.startPosition;
2265 lastCommentStartPosition = -1;
2268 } catch (InvalidInputException e) {
2271 // if the string cannot be split, return null.
2272 if (splitOperatorsCount == 0)
2275 // ## SPECIAL CASES BEGIN
2276 if (((splitOperatorsCount == 2
2277 && splitOperators[1] == TokenNameDOT
2278 && splitTokenDepth == 0
2279 && lastOpenParenthesisPosition > -1)
2280 || (splitOperatorsCount > 2
2281 && splitOperators[1] == TokenNameDOT
2282 && splitTokenDepth == 0
2283 && lastOpenParenthesisPosition > -1
2284 && lastOpenParenthesisPosition <= options.maxLineLength)
2285 || (separateFirstArgumentOn(firstTokenOnLine) && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2286 && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2287 // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should not be broken on two lines
2288 // only one split on a top level .
2289 // or more than one split on . and substring before open parenthesis fits one line.
2290 // or split inside parenthesis and first token is not a for/while/if
2291 SplitLine sl = split(stringToSplit.substring(lastOpenParenthesisPosition), lastOpenParenthesisPosition);
2292 if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2293 // trim() is used to remove the extra blanks at the end of the substring. See PR 1FGYPI1
2294 return new SplitLine(
2297 stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2298 stringToSplit.substring(lastOpenParenthesisPosition)},
2299 new int[] { offsetInGlobalLine, lastOpenParenthesisPosition + offsetInGlobalLine });
2301 // right substring can be split and is split on comma
2302 // copy substrings and operators
2303 // except if the 1st string is empty.
2304 int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2305 int subStringsLength = sl.substrings.length + 1 - startIndex;
2306 String[] result = new String[subStringsLength];
2307 int[] startIndexes = new int[subStringsLength];
2308 int operatorsLength = sl.operators.length + 1 - startIndex;
2309 int[] operators = new int[operatorsLength];
2311 result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2314 System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes, 1, subStringsLength - 1);
2315 for (int i = subStringsLength - 1; i >= 0; i--) {
2316 startIndexes[i] += offsetInGlobalLine;
2318 System.arraycopy(sl.substrings, startIndex, result, 1, subStringsLength - 1);
2319 System.arraycopy(sl.operators, startIndex, operators, 1, operatorsLength - 1);
2321 return new SplitLine(operators, result, startIndexes);
2324 // if the last token is a comment and the substring before the comment fits on a line,
2325 // split before the comment and return the result.
2326 if (lastCommentStartPosition > -1 && lastCommentStartPosition < options.maxLineLength && splitTokenPriority > 50) {
2327 int end = lastCommentStartPosition;
2328 int start = lastCommentStartPosition;
2329 if (stringToSplit.charAt(end - 1) == ' ') {
2332 if (start != end && stringToSplit.charAt(start) == ' ') {
2335 return new SplitLine(
2337 new String[] { stringToSplit.substring(0, end), stringToSplit.substring(start)},
2338 new int[] { 0, start });
2340 if (position != stringToSplit.length()) {
2341 if (substringsCount == substringsStartPositions.length) {
2343 substringsStartPositions,
2345 (substringsStartPositions = new int[substringsCount * 2]),
2348 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount * 2]), 0, substringsCount);
2350 // avoid empty extra substring, e.g. line terminated with a semi-colon
2351 substringsStartPositions[substringsCount] = position;
2352 substringsEndPositions[substringsCount++] = stringToSplit.length();
2354 if (splitOperatorsCount == splitOperators.length) {
2355 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount * 2]), 0, splitOperatorsCount);
2357 splitOperators[splitOperatorsCount] = 0;
2359 // the last element of the stack is the position of the end of StringToSPlit
2360 // +1 because the substring method excludes the last character
2361 String[] result = new String[substringsCount];
2362 for (int i = 0; i < substringsCount; i++) {
2363 int start = substringsStartPositions[i];
2364 int end = substringsEndPositions[i];
2365 if (stringToSplit.charAt(start) == ' ') {
2367 substringsStartPositions[i]++;
2369 if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2372 result[i] = stringToSplit.substring(start, end);
2373 substringsStartPositions[i] += offsetInGlobalLine;
2375 if (splitOperatorsCount > substringsCount) {
2376 System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[splitOperatorsCount]), 0, substringsCount);
2377 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[splitOperatorsCount]), 0, substringsCount);
2378 for (int i = substringsCount; i < splitOperatorsCount; i++) {
2379 substringsStartPositions[i] = position;
2380 substringsEndPositions[i] = position;
2382 System.arraycopy(splitOperators, 0, (splitOperators = new int[splitOperatorsCount]), 0, splitOperatorsCount);
2384 System.arraycopy(substringsStartPositions, 0, (substringsStartPositions = new int[substringsCount]), 0, substringsCount);
2385 System.arraycopy(substringsEndPositions, 0, (substringsEndPositions = new int[substringsCount]), 0, substringsCount);
2386 System.arraycopy(splitOperators, 0, (splitOperators = new int[substringsCount]), 0, substringsCount);
2388 SplitLine splitLine = new SplitLine(splitOperators, result, substringsStartPositions);
2392 private void updateMappedPositions(int startPosition) {
2393 if (positionsToMap == null) {
2396 char[] source = scanner.source;
2397 int sourceLength = source.length;
2398 while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2399 int posToMap = positionsToMap[indexToMap];
2400 if (posToMap < 0 || posToMap >= sourceLength) {
2401 // protection against out of bounds position
2402 if (posToMap == sourceLength) {
2403 mappedPositions[indexToMap] = formattedSource.length();
2405 indexToMap = positionsToMap.length; // no more mapping
2408 if (CharOperation.isWhitespace(source[posToMap])) {
2409 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2411 if (posToMap == sourceLength - 1) {
2412 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2414 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2421 private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2422 if (mappedPositions == null || mappedPositions.length == indexInMap)
2425 while (indexInMap < mappedPositions.length
2426 && startPosition <= mappedPositions[indexInMap]
2427 && mappedPositions[indexInMap] < endPosition
2428 && indexInMap < indexToMap) {
2429 mappedPositions[indexInMap] += splitDelta;
2434 private int getLength(String s, int tabDepth) {
2436 for (int i = 0; i < tabDepth; i++) {
2437 length += options.tabSize;
2439 for (int i = 0, max = s.length(); i < max; i++) {
2440 char currentChar = s.charAt(i);
2441 switch (currentChar) {
2443 length += options.tabSize;
2453 * Sets the initial indentation level
2454 * @param indentationLevel new indentation level
2458 public void setInitialIndentationLevel(int newIndentationLevel) {
2459 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;