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
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/cpl-v05.html
10 * IBM Corporation - initial API and implementation
11 ******************************************************************************/
12 package net.sourceforge.phpdt.internal.formatter;
14 import java.io.BufferedReader;
15 import java.io.IOException;
16 import java.io.StringReader;
17 import java.util.Hashtable;
18 import java.util.Locale;
21 import net.sourceforge.phpdt.core.ICodeFormatter;
22 import net.sourceforge.phpdt.core.compiler.CharOperation;
23 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
24 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
25 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
26 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
27 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
28 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
31 * <h2>How to format a piece of code ?</h2>
33 * <li>Create an instance of <code>CodeFormatter</code>
34 * <li>Use the method <code>void format(aString)</code> on this instance to format <code>aString</code>. It will return the
38 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
39 // IContentFormatterExtension {
40 public FormatterOptions options;
43 * Represents a block in the <code>constructions</code> stack.
45 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
48 * Represents a block following a control statement in the <code>constructions</code> stack.
50 public static final int NONINDENT_BLOCK = -100;
53 * Contains the formatted output.
55 StringBuffer formattedSource;
58 * Contains the current line. <br>
59 * Will be dumped at the next "newline"
61 StringBuffer currentLineBuffer;
64 * Used during the formatting to get each token.
69 * Contains the tokens responsible for the current indentation level and the blocks not closed yet.
71 private int[] constructions;
74 * Index in the <code>constructions</code> array.
76 private int constructionsCount;
79 * Level of indentation of the current token (number of tab char put in front of it).
81 private int indentationLevel;
84 * Regular level of indentation of all the lines
86 private int initialIndentationLevel;
89 * Used to split a line.
94 * To remember the offset between the beginning of the line and the beginning of the comment.
96 int currentCommentOffset;
98 int currentLineIndentationLevel;
100 int maxLineSize = 30;
102 private boolean containsOpenCloseBraces;
104 private int indentationLevelForOpenCloseBraces;
107 * Collections of positions to map
109 private int[] positionsToMap;
112 * Collections of mapped positions
114 private int[] mappedPositions;
116 private int indexToMap;
118 private int indexInMap;
120 private int globalDelta;
122 private int lineDelta;
124 private int splitDelta;
126 private int beginningOfLineIndex;
128 private int multipleLineCommentCounter;
131 * Creates a new instance of Code Formatter using the given settings.
133 * @deprecated backport 1.0 internal functionality
135 public CodeFormatter(ConfigurableOption[] settings) {
136 this(convertConfigurableOptions(settings));
140 * Creates a new instance of Code Formatter using the FormattingOptions object given as argument
142 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
144 public CodeFormatter() {
149 * Creates a new instance of Code Formatter using the given settings.
151 public CodeFormatter(Map settings) {
152 // initialize internal state
153 constructionsCount = 0;
154 constructions = new int[10];
155 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
156 currentCommentOffset = -1;
157 // initialize primary and secondary scanners
158 scanner = new Scanner(true /* comment */
159 , true /* whitespace */
162 , true, /* tokenizeStrings */
163 null, null, true /*taskCaseSensitive*/); // regular scanner for forming lines
164 scanner.recordLineSeparator = true;
165 scanner.ignorePHPOneLiner = true;
166 // to remind of the position of the beginning of the line.
167 splitScanner = new Scanner(true /* comment */
168 , true /* whitespace */
171 , true, /* tokenizeStrings */
172 null, null, true /*taskCaseSensitive*/);
173 splitScanner.ignorePHPOneLiner = true;
174 // secondary scanner to split long lines formed by primary scanning
175 // initialize current line buffer
176 currentLineBuffer = new StringBuffer();
177 this.options = new FormatterOptions(settings);
181 * Returns true if a lineSeparator has to be inserted before <code>operator</code> false otherwise.
183 private static boolean breakLineBeforeOperator(int operator) {
186 case TokenNameSEMICOLON:
195 * @deprecated backport 1.0 internal functionality
197 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
198 Hashtable options = new Hashtable(10);
199 for (int i = 0; i < settings.length; i++) {
200 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
201 String optionName = settings[i].getOptionName();
202 int valueIndex = settings[i].getCurrentValueIndex();
203 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
204 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
205 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
207 .put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
208 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
209 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
210 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
211 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
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$
214 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
215 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
216 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
217 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
218 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
219 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
220 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
221 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
229 * Returns the end of the source code.
231 private final String copyRemainingSource() {
232 char str[] = scanner.source;
233 int startPosition = scanner.startPosition;
234 int length = str.length - startPosition;
235 StringBuffer bufr = new StringBuffer(length);
236 if (startPosition < str.length) {
237 bufr.append(str, startPosition, length);
239 return (bufr.toString());
243 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
245 private void dumpTab(int tabCount) {
246 if (options.indentWithTab) {
247 for (int j = 0; j < tabCount; j++) {
248 formattedSource.append('\t');
249 increaseSplitDelta(1);
252 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
253 formattedSource.append(' ');
254 increaseSplitDelta(1);
260 * Dumps <code>currentLineBuffer</code> into the formatted string.
262 private void flushBuffer() {
263 String currentString = currentLineBuffer.toString();
265 beginningOfLineIndex = formattedSource.length();
266 if (containsOpenCloseBraces) {
267 containsOpenCloseBraces = false;
268 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
269 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
271 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
273 int scannerSourceLength = scanner.source.length;
274 if ((scannerSourceLength > 2) && (scanner.startPosition < scannerSourceLength)) {
275 if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
276 formattedSource.append(options.lineSeparatorSequence);
277 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
278 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
279 formattedSource.append(options.lineSeparatorSequence);
280 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
281 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
282 formattedSource.append(options.lineSeparatorSequence);
283 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
286 updateMappedPositions(scanner.startPosition);
290 * Formats the input string.
292 private void format() {
294 int previousToken = 0;
295 int previousCompilableToken = 0;
296 int indentationOffset = 0;
297 int newLinesInWhitespace = 0;
298 // number of new lines in the previous whitespace token
299 // (used to leave blank lines before comments)
300 int pendingNewLines = 0;
301 boolean expectingOpenBrace = false;
302 boolean clearNonBlockIndents = false;
303 // true if all indentations till the 1st { (usefull after } or ;)
304 boolean pendingSpace = true;
305 boolean pendingNewlineAfterParen = false;
306 // true when a cr is to be put after a ) (in conditional statements)
307 boolean inAssignment = false;
308 boolean inArrayAssignment = false;
309 boolean inThrowsClause = false;
310 boolean inClassOrInterfaceHeader = false;
311 int dollarBraceCount = 0;
312 // openBracketCount is used to count the number of open brackets not closed
314 int openBracketCount = 0;
315 int unarySignModifier = 0;
316 // openParenthesis[0] is used to count the parenthesis not belonging to a
318 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
319 int openParenthesisCount = 1;
320 int[] openParenthesis = new int[10];
321 // tokenBeforeColon is used to know what token goes along with the current
323 // it can be case or ?
324 int tokenBeforeColonCount = 0;
325 int[] tokenBeforeColon = new int[10];
326 constructionsCount = 0; // initializes the constructions count.
327 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
329 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
330 boolean specialElse = false;
331 // OPTION (IndentationLevel): initial indentation level may be non-zero.
332 currentLineIndentationLevel += constructionsCount;
333 // An InvalidInputException exception might cause the termination of this
335 int arrayDeclarationCount=0;
336 int[] arrayDeclarationParenthesis=new int[10];
339 // Get the next token. Catch invalid input and output it
340 // with minimal formatting, also catch end of input and
343 token = scanner.getNextToken();
345 int currentEndPosition = scanner.getCurrentTokenEndPosition();
346 int currentStartPosition = scanner.getCurrentTokenStartPosition();
347 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
348 System.out.println(scanner.toStringAction(token));
350 // Patch for line comment
351 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
352 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
353 int length = scanner.currentPosition;
354 loop: for (int index = length - 1; index >= 0; index--) {
355 switch (scanner.source[index]) {
358 scanner.currentPosition--;
365 } catch (InvalidInputException e) {
366 if (!handleInvalidToken(e)) {
371 if (token == Scanner.TokenNameEOF) {
373 } else if (token == Scanner.TokenNameHEREDOC) {
374 // no indentation for heredocs and HTML !
375 outputCurrentTokenWithoutIndent(Scanner.TokenNameHEREDOC, 0);
377 } else if (token == Scanner.TokenNameINLINE_HTML) {
378 // no indentation for heredocs and HTML !
379 int newLineCount = 1;
380 if (scanner.startPosition==0) {
383 outputCurrentTokenWithoutIndent(Scanner.TokenNameINLINE_HTML, newLineCount);
384 int srcLen = scanner.source.length;
385 if (scanner.currentPosition < srcLen-1) {
391 * ## MODIFYING the indentation level before generating new lines and indentation in the output string
393 // Removes all the indentations made by statements not followed by a
395 // except if the current token is ELSE, CATCH or if we are in a
397 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
400 if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
404 indentationLevel += popInclusiveUntil(TokenNameif);
406 // case TokenNamecatch :
407 // indentationLevel += popInclusiveUntil(TokenNamecatch);
409 // case TokenNamefinally :
410 // indentationLevel += popInclusiveUntil(TokenNamecatch);
413 if (nlicsToken == TokenNamedo) {
414 indentationLevel += pop(TokenNamedo);
418 indentationLevel += popExclusiveUntilBlockOrCase();
419 // clear until a CASE, DEFAULT or BLOCK is encountered.
420 // Thus, the indentationLevel is correctly cleared either
421 // in a switch/case statement or in any other situation.
423 clearNonBlockIndents = false;
425 // returns to the indentation level created by the SWITCH keyword
426 // if the current token is a CASE or a DEFAULT
427 if (token == TokenNamecase || token == TokenNamedefault) {
428 indentationLevel += pop(TokenNamecase);
430 // if (token == Scanner.TokenNamethrows) {
431 // inThrowsClause = true;
433 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface) && previousToken != Scanner.TokenNameDOT) {
434 inClassOrInterfaceHeader = true;
437 * ## APPEND newlines and indentations to the output string
439 // Do not add a new line between ELSE and IF, if the option
440 // elseIfOnSameLine is true.
441 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
442 // if (pendingNewlineAfterParen
443 // && previousCompilableToken == TokenNameelse
444 // && token == TokenNameif
445 // && options.compactElseIfMode) {
446 // pendingNewlineAfterParen = false;
447 // pendingNewLines = 0;
448 // indentationLevel += pop(TokenNameelse);
449 // // because else if is now one single statement,
450 // // the indentation level after it is increased by one and not by 2
451 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
453 // Add a newline & indent to the formatted source string if
454 // a for/if-else/while statement was scanned and there is no block
456 pendingNewlineAfterParen = pendingNewlineAfterParen
457 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
458 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
459 pendingNewlineAfterParen = false;
460 // Do to add a newline & indent sequence if the current token is an
461 // open brace or a period or if the current token is a semi-colon and
463 // previous token is a close paren.
464 // add a new line if a parenthesis belonging to a for() statement
465 // has been closed and the current token is not an opening brace
466 if (token != TokenNameLBRACE && !isComment(token)
467 // to avoid adding new line between else and a comment
468 && token != TokenNameDOT && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
470 currentLineIndentationLevel = indentationLevel;
472 pendingSpace = false;
474 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
476 if (constructionsCount > 0 && constructions[constructionsCount - 1] != BLOCK
477 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
478 currentLineIndentationLevel = indentationLevel - 1;
480 currentLineIndentationLevel = indentationLevel;
483 pendingSpace = false;
487 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode && constructionsCount > 0
488 && constructions[constructionsCount - 1] == TokenNamedo) {
490 currentLineIndentationLevel = indentationLevel - 1;
492 pendingSpace = false;
495 if (token == TokenNameLBRACE && inThrowsClause) {
496 inThrowsClause = false;
497 if (options.newLineBeforeOpeningBraceMode) {
499 currentLineIndentationLevel = indentationLevel;
501 pendingSpace = false;
505 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
506 inClassOrInterfaceHeader = false;
507 if (options.newLineBeforeOpeningBraceMode) {
509 currentLineIndentationLevel = indentationLevel;
511 pendingSpace = false;
514 // don't linebreak empty array declarations
515 if (token == TokenNameRPAREN && arrayDeclarationCount > 0) {
516 if (previousCompilableToken == TokenNameLPAREN) {
520 // Add pending new lines to the formatted source string.
521 // Note: pending new lines are not added if the current token
522 // is a single line comment or whitespace.
523 // if the comment is between parenthesis, there is no blank line
525 // (if it's a one-line comment, a blank line is added after it).
527 (pendingNewLines > 0 && (!isComment(token)))
528 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token)))
529 || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE)
530 || (newLinesInWhitespace > 0 && previousCompilableToken == TokenNameDOT)
532 && token != Scanner.TokenNameWHITESPACE) {
533 // Do not add newline & indent between an adjoining close brace and
534 // close paren. Anonymous inner classes may use this form.
535 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
536 // OPTION (NewLineInCompoundStatement): do not add newline & indent
537 // between close brace and else, (do) while, catch, and finally if
538 // newlineInCompoundStatement is true.
539 boolean nlicsOption = previousToken == TokenNameRBRACE
540 && !options.newlineInControlStatementMode
541 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo) || token == TokenNamecatch || token == TokenNamefinally);
542 // Do not add a newline & indent between a close brace and
544 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
545 // Do not add a new line & indent between a multiline comment and a
547 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
548 // Do not add a newline & indent between a close brace and a colon
549 // (in array assignments, for example).
550 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
551 // Add a newline and indent, if appropriate.
553 || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
554 // if clearAllBlankLinesMode=false, leaves the blank lines
555 // inserted by the user
556 // if clearAllBlankLinesMode=true, removes all of then
557 // and insert only blank lines required by the formatting.
558 if (!options.clearAllBlankLinesMode) {
559 // (isComment(token))
560 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
561 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
563 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
564 containsOpenCloseBraces = true;
565 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
566 if (isComment(previousToken)) {
567 newLine(pendingNewLines);
570 * if (!(constructionsCount > 1 && constructions[constructionsCount-1] == NONINDENT_BLOCK &&
571 * (constructions[constructionsCount-2] == TokenNamefor
573 if (options.newLineInEmptyBlockMode) {
574 if (inArrayAssignment) {
575 newLine(1); // array assigment with an empty block
577 newLine(pendingNewLines);
583 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
585 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
586 newLine(pendingNewLines);
589 if (((previousCompilableToken == TokenNameSEMICOLON) || (previousCompilableToken == TokenNameLBRACE)
590 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
591 && (token == TokenNameRBRACE)) {
592 indentationOffset = -1;
593 indentationLevel += popExclusiveUntilBlock();
595 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
597 currentLineIndentationLevel++;
599 currentLineIndentationLevel = indentationLevel + indentationOffset;
601 pendingSpace = false;
602 indentationOffset = 0;
605 newLinesInWhitespace = 0;
607 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
611 boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
613 // case TokenNameDOLLAR :
614 // dollarBraceCount++;
617 // case TokenNamefinally :
618 expectingOpenBrace = true;
619 pendingNewlineAfterParen = true;
620 indentationLevel += pushControlStatement(token);
623 case TokenNamedefault:
624 if (tokenBeforeColonCount == tokenBeforeColon.length) {
626 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
628 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
629 indentationLevel += pushControlStatement(TokenNamecase);
631 case TokenNameQUESTION:
632 if (tokenBeforeColonCount == tokenBeforeColon.length) {
634 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
636 tokenBeforeColon[tokenBeforeColonCount++] = token;
638 case TokenNameswitch:
640 case TokenNameforeach:
643 if (openParenthesisCount == openParenthesis.length) {
644 System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
646 openParenthesis[openParenthesisCount++] = 0;
647 expectingOpenBrace = true;
648 indentationLevel += pushControlStatement(token);
651 pendingNewlineAfterParen = true;
653 // several CATCH statements can be contiguous.
654 // a CATCH is encountered pop until first CATCH (if a CATCH
655 // follows a TRY it works the same way,
656 // as CATCH and TRY are the same token in the stack).
657 expectingOpenBrace = true;
658 indentationLevel += pushControlStatement(TokenNamecatch);
661 expectingOpenBrace = true;
662 indentationLevel += pushControlStatement(token);
667 case TokenNameLPAREN:
668 // if (previousToken == TokenNamesynchronized) {
669 // indentationLevel += pushControlStatement(previousToken);
671 // Put a space between the previous and current token if the
672 // previous token was not a keyword, open paren, logical
673 // compliment (eg: !), semi-colon, open brace, close brace,
675 if (previousCompilableToken != TokenNameLBRACKET && previousToken != TokenNameIdentifier && previousToken != 0
676 && previousToken != TokenNameNOT && previousToken != TokenNameLPAREN && previousToken != TokenNameTWIDDLE
677 && previousToken != TokenNameSEMICOLON && previousToken != TokenNameLBRACE && previousToken != TokenNameRBRACE
678 && previousToken != TokenNamesuper) {
679 // && previousToken != TokenNamethis) {
682 // If in a for/if/while statement, increase the parenthesis count
683 // for the current openParenthesisCount
684 // else increase the count for stand alone parenthesis.
685 if (openParenthesisCount > 0)
686 openParenthesis[openParenthesisCount - 1]++;
688 openParenthesis[0]++;
689 pendingSpace = false;
690 // recognize array declaration for nice output
691 if (previousCompilableToken == TokenNamearray) {
692 arrayDeclarationCount++;
693 arrayDeclarationParenthesis[arrayDeclarationCount]=openParenthesis[openParenthesisCount];
699 case TokenNameRPAREN:
700 // check for closing array declaration
701 if (arrayDeclarationCount>0) {
702 if (arrayDeclarationParenthesis[arrayDeclarationCount]==openParenthesis[openParenthesisCount]) {
703 if (previousCompilableToken != TokenNameLPAREN) {
707 currentLineIndentationLevel = indentationLevel;
709 arrayDeclarationCount--;
712 // Decrease the parenthesis count
713 // if there is no more unclosed parenthesis,
714 // a new line and indent may be append (depending on the next
716 if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
717 openParenthesis[openParenthesisCount - 1]--;
718 if (openParenthesis[openParenthesisCount - 1] <= 0) {
719 pendingNewlineAfterParen = true;
720 inAssignment = false;
721 openParenthesisCount--;
724 openParenthesis[0]--;
726 pendingSpace = false;
728 case TokenNameLBRACE:
729 if (previousCompilableToken == TokenNameDOLLAR) {
732 if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
733 // if (previousCompilableToken == TokenNameRBRACKET) {
734 inArrayAssignment = true;
735 inAssignment = false;
737 if (inArrayAssignment) {
738 indentationLevel += pushBlock();
740 // Add new line and increase indentation level after open brace.
742 indentationLevel += pushBlock();
743 inAssignment = false;
747 case TokenNameRBRACE:
748 if (dollarBraceCount > 0) {
752 if (previousCompilableToken == TokenNameRPAREN) {
753 pendingSpace = false;
755 if (inArrayAssignment) {
756 inArrayAssignment = false;
758 indentationLevel += popInclusiveUntilBlock();
761 indentationLevel += popInclusiveUntilBlock();
762 if (previousCompilableToken == TokenNameRPAREN) {
763 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
765 currentLineBuffer.append(options.lineSeparatorSequence);
766 increaseLineDelta(options.lineSeparatorSequence.length);
768 if (constructionsCount > 0) {
769 switch (constructions[constructionsCount - 1]) {
771 case TokenNameforeach:
772 //indentationLevel += popExclusiveUntilBlock();
774 case TokenNameswitch:
779 case TokenNamefinally:
782 // case TokenNamesynchronized :
783 clearNonBlockIndents = true;
790 case TokenNameLBRACKET:
792 pendingSpace = false;
794 case TokenNameRBRACKET:
795 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
796 // if there is no left bracket to close, the right bracket is
798 pendingSpace = false;
801 pendingSpace = false;
802 if (arrayDeclarationCount>0) {
808 pendingSpace = false;
810 case TokenNameSEMICOLON:
811 // Do not generate line terminators in the definition of
812 // the for statement.
813 // if not in this case, jump a line and reduce indentation after
815 // if the block it closes belongs to a conditional statement (if,
817 if (openParenthesisCount <= 1) {
819 if (expectingOpenBrace) {
820 clearNonBlockIndents = true;
821 expectingOpenBrace = false;
824 inAssignment = false;
825 pendingSpace = false;
827 case TokenNamePLUS_PLUS:
828 case TokenNameMINUS_MINUS:
829 // Do not put a space between a post-increment/decrement
830 // and the identifier being modified.
831 if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET || previousToken == TokenNameVariable) {
832 pendingSpace = false;
836 // previously ADDITION
838 // Handle the unary operators plus and minus via a flag
839 if (!isLiteralToken(previousToken) && previousToken != TokenNameIdentifier && previousToken != TokenNameRPAREN
840 && previousToken != TokenNameRBRACKET) {
841 unarySignModifier = 1;
845 // In a switch/case statement, add a newline & indent
846 // when a colon is encountered.
847 if (tokenBeforeColonCount > 0) {
848 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
851 tokenBeforeColonCount--;
857 case Scanner.TokenNameCOMMENT_LINE:
860 currentLineIndentationLevel++;
862 break; // a line is always inserted after a one-line comment
863 case Scanner.TokenNameCOMMENT_PHPDOC:
864 case Scanner.TokenNameCOMMENT_BLOCK:
865 currentCommentOffset = getCurrentCommentOffset();
868 case Scanner.TokenNameWHITESPACE:
869 if (!phpTagAndWhitespace) {
870 // Count the number of line terminators in the whitespace so
871 // line spacing can be preserved near comments.
872 char[] source = scanner.source;
873 newLinesInWhitespace = 0;
874 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
875 if (source[i] == '\r') {
877 if (source[++i] == '\n') {
878 newLinesInWhitespace++;
880 newLinesInWhitespace++;
883 newLinesInWhitespace++;
885 } else if (source[i] == '\n') {
886 newLinesInWhitespace++;
889 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
892 // case TokenNameHTML :
893 // // Add the next token to the formatted source string.
894 // // outputCurrentToken(token);
895 // int startPosition = scanner.startPosition;
897 // for (int i = startPosition, max = scanner.currentPosition; i <
899 // char currentCharacter = scanner.source[i];
900 // updateMappedPositions(i);
901 // currentLineBuffer.append(currentCharacter);
905 if ((token == TokenNameIdentifier) || isLiteralToken(token) || token == TokenNamesuper) {
906 // || token == TokenNamethis) {
907 // Do not put a space between a unary operator
908 // (eg: ++, --, +, -) and the identifier being modified.
909 if (previousToken == TokenNamePLUS_PLUS || previousToken == TokenNameMINUS_MINUS
910 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
911 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
912 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
913 pendingSpace = false;
915 unarySignModifier = 0;
919 // Do not output whitespace tokens.
920 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
922 * Add pending space to the formatted source string. Do not output a space under the following circumstances: 1) this is
923 * the first pass 2) previous token is an open paren 3) previous token is a period 4) previous token is the logical
924 * compliment (eg: !) 5) previous token is the bitwise compliment (eg: ~) 6) previous token is the open bracket (eg: [) 7)
925 * in an assignment statement, if the previous token is an open brace or the current token is a close brace 8) previous
926 * token is a single line comment 9) current token is a '->'
928 if (token == TokenNameMINUS_GREATER && options.compactDereferencingMode)
929 pendingSpace = false;
931 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
932 if (pendingSpace && insertSpaceAfter(previousToken)
933 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
934 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
935 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
938 // Add the next token to the formatted source string.
939 outputCurrentToken(token);
940 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
942 currentLineBuffer.append(options.lineSeparatorSequence);
943 increaseLineDelta(options.lineSeparatorSequence.length);
947 // Whitespace tokens do not need to be remembered.
948 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
949 previousToken = token;
950 if (token != Scanner.TokenNameCOMMENT_BLOCK && token != Scanner.TokenNameCOMMENT_LINE
951 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
952 previousCompilableToken = token;
956 output(copyRemainingSource());
958 // dump the last token of the source in the formatted output.
959 } catch (InvalidInputException e) {
960 output(copyRemainingSource());
962 // dump the last token of the source in the formatted output.
967 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
969 * @return the formatted ouput.
971 public String formatSourceString(String sourceString) {
972 char[] sourceChars = sourceString.toCharArray();
973 formattedSource = new StringBuffer(sourceChars.length);
974 scanner.setSource(sourceChars);
976 return formattedSource.toString();
980 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
983 * the string to format
984 * @param indentationLevel
985 * the initial indentation level
986 * @return the formatted ouput.
988 public String format(String string, int indentationLevel) {
989 return format(string, indentationLevel, (int[]) null);
993 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The positions array
994 * is modified to contain the mapped positions.
997 * the string to format
998 * @param indentationLevel
999 * the initial indentation level
1001 * the array of positions to map
1002 * @return the formatted ouput.
1004 public String format(String string, int indentationLevel, int[] positions) {
1005 return this.format(string, indentationLevel, positions, null);
1008 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
1009 if (lineSeparator != null) {
1010 this.options.setLineSeparator(lineSeparator);
1012 if (positions != null) {
1013 this.setPositionsToMap(positions);
1014 this.setInitialIndentationLevel(indentationLevel);
1015 String formattedString = this.formatSourceString(string);
1016 int[] mappedPositions = this.getMappedPositions();
1017 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
1018 return formattedString;
1020 this.setInitialIndentationLevel(indentationLevel);
1021 return this.formatSourceString(string);
1026 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The initial
1027 * indentation level is 0.
1030 * the string to format
1031 * @return the formatted ouput.
1033 public String format(String string) {
1034 return this.format(string, 0, (int[]) null);
1038 * Formats a given source string, starting indenting it at a particular depth and using the given options
1040 * @deprecated backport 1.0 internal functionality
1042 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1043 CodeFormatter formatter = new CodeFormatter(options);
1044 formatter.setInitialIndentationLevel(initialIndentationLevel);
1045 return formatter.formatSourceString(sourceString);
1049 * Returns the number of characters and tab char between the beginning of the line and the beginning of the comment.
1051 private int getCurrentCommentOffset() {
1052 int linePtr = scanner.linePtr;
1053 // if there is no beginning of line, return 0.
1057 int beginningOfLine = scanner.lineEnds[linePtr];
1058 int currentStartPosition = scanner.startPosition;
1059 char[] source = scanner.source;
1060 // find the position of the beginning of the line containing the comment
1061 while (beginningOfLine > currentStartPosition) {
1063 beginningOfLine = scanner.lineEnds[--linePtr];
1065 beginningOfLine = 0;
1069 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1070 char currentCharacter = source[i];
1071 switch (currentCharacter) {
1073 offset += options.tabSize;
1089 * Returns an array of descriptions for the configurable options. The descriptions may be changed and passed back to a different
1092 * @deprecated backport 1.0 internal functionality
1094 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1095 String componentName = CodeFormatter.class.getName();
1096 FormatterOptions options = new FormatterOptions();
1097 return new ConfigurableOption[] {
1098 new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1100 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1),
1102 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1),
1104 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1105 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1106 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1),
1108 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),
1110 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1),
1112 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1),
1114 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1119 * Returns the array of mapped positions. Returns null is no positions have been set.
1122 * @deprecated There is no need to retrieve the mapped positions anymore.
1124 public int[] getMappedPositions() {
1125 if (null!=mappedPositions) {
1126 for (int i=0;i<mappedPositions.length;i++) {
1127 if (mappedPositions[i]>=formattedSource.length()) {
1128 mappedPositions[i]=formattedSource.length()-1;
1132 return mappedPositions;
1136 * Returns the priority of the token given as argument <br>
1137 * The most prioritary the token is, the smallest the return value is.
1139 * @return the priority of <code>token</code>
1141 * the token of which the priority is requested
1143 private static int getTokenPriority(int token) {
1145 case TokenNameextends:
1146 // case TokenNameimplements :
1147 // case TokenNamethrows :
1149 case TokenNameSEMICOLON:
1152 case TokenNameCOMMA:
1155 case TokenNameEQUAL:
1158 case TokenNameAND_AND:
1160 case TokenNameOR_OR:
1163 case TokenNameQUESTION:
1165 case TokenNameCOLON:
1167 return 50; // it's better cutting on ?: than on ;
1168 case TokenNameEQUAL_EQUAL:
1170 case TokenNameEQUAL_EQUAL_EQUAL:
1172 case TokenNameNOT_EQUAL:
1174 case TokenNameNOT_EQUAL_EQUAL:
1179 case TokenNameLESS_EQUAL:
1181 case TokenNameGREATER:
1183 case TokenNameGREATER_EQUAL:
1185 // case TokenNameinstanceof : // instanceof
1189 case TokenNameMINUS:
1192 case TokenNameMULTIPLY:
1194 case TokenNameDIVIDE:
1196 case TokenNameREMAINDER:
1199 case TokenNameLEFT_SHIFT:
1201 case TokenNameRIGHT_SHIFT:
1203 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1212 case TokenNameMULTIPLY_EQUAL:
1214 case TokenNameDIVIDE_EQUAL:
1216 case TokenNameREMAINDER_EQUAL:
1218 case TokenNamePLUS_EQUAL:
1220 case TokenNameMINUS_EQUAL:
1222 case TokenNameLEFT_SHIFT_EQUAL:
1224 case TokenNameRIGHT_SHIFT_EQUAL:
1226 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1227 case TokenNameAND_EQUAL:
1229 case TokenNameXOR_EQUAL:
1231 case TokenNameOR_EQUAL:
1233 case TokenNameDOT_EQUAL:
1240 return Integer.MAX_VALUE;
1245 * Handles the exception raised when an invalid token is encountered. Returns true if the exception has been handled, false
1248 private boolean handleInvalidToken(Exception e) {
1249 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT) || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1250 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1256 private final void increaseGlobalDelta(int offset) {
1257 globalDelta += offset;
1260 private final void increaseLineDelta(int offset) {
1261 lineDelta += offset;
1264 private final void increaseSplitDelta(int offset) {
1265 splitDelta += offset;
1269 * Returns true if a space has to be inserted after <code>operator</code> false otherwise.
1271 private boolean insertSpaceAfter(int token) {
1273 case TokenNameLPAREN:
1275 case TokenNameTWIDDLE:
1278 case TokenNameWHITESPACE:
1279 case TokenNameLBRACKET:
1280 case TokenNameDOLLAR:
1281 case Scanner.TokenNameCOMMENT_LINE:
1289 * Returns true if a space has to be inserted before <code>operator</code> false otherwise. <br>
1290 * Cannot be static as it uses the code formatter options (to know if the compact assignment mode is on).
1292 private boolean insertSpaceBefore(int token) {
1294 case TokenNameEQUAL:
1295 return (!options.compactAssignmentMode);
1301 private static boolean isComment(int token) {
1302 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE
1303 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1307 private static boolean isLiteralToken(int token) {
1308 boolean result = token == TokenNameIntegerLiteral
1309 // || token == TokenNameLongLiteral
1310 // || token == TokenNameFloatingPointLiteral
1311 || token == TokenNameDoubleLiteral
1312 // || token == TokenNameCharacterLiteral
1313 || token == TokenNameStringDoubleQuote;
1318 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>, it is split and the result is dumped in
1319 * <code>formattedSource</code>
1321 * @param newLineCount
1322 * the number of new lines to append
1324 private void newLine(int newLineCount) {
1325 // format current line
1327 beginningOfLineIndex = formattedSource.length();
1328 String currentLine = currentLineBuffer.toString();
1329 if (containsOpenCloseBraces) {
1330 containsOpenCloseBraces = false;
1331 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1332 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1334 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1336 // dump line break(s)
1337 for (int i = 0; i < newLineCount; i++) {
1338 formattedSource.append(options.lineSeparatorSequence);
1339 increaseSplitDelta(options.lineSeparatorSequence.length);
1341 // reset formatter for next line
1342 int currentLength = currentLine.length();
1343 currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1344 increaseGlobalDelta(splitDelta);
1345 increaseGlobalDelta(lineDelta);
1347 currentLineIndentationLevel = initialIndentationLevel;
1350 private String operatorString(int operator) {
1352 case TokenNameextends:
1353 return "extends"; //$NON-NLS-1$
1354 // case TokenNameimplements :
1355 // return "implements"; //$NON-NLS-1$
1357 // case TokenNamethrows :
1358 // return "throws"; //$NON-NLS-1$
1359 case TokenNameSEMICOLON:
1361 return ";"; //$NON-NLS-1$
1362 case TokenNameCOMMA:
1364 return ","; //$NON-NLS-1$
1365 case TokenNameEQUAL:
1367 return "="; //$NON-NLS-1$
1368 case TokenNameAND_AND:
1370 return "&&"; //$NON-NLS-1$
1371 case TokenNameOR_OR:
1373 return "||"; //$NON-NLS-1$
1374 case TokenNameQUESTION:
1376 return "?"; //$NON-NLS-1$
1377 case TokenNameCOLON:
1379 return ":"; //$NON-NLS-1$
1380 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1382 return "::"; //$NON-NLS-1$
1383 case TokenNameEQUAL_EQUAL:
1384 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1385 return "=="; //$NON-NLS-1$
1386 case TokenNameEQUAL_EQUAL_EQUAL:
1387 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1388 return "==="; //$NON-NLS-1$
1389 case TokenNameEQUAL_GREATER:
1391 return "=>"; //$NON-NLS-1$
1392 case TokenNameNOT_EQUAL:
1393 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1394 return "!="; //$NON-NLS-1$
1395 case TokenNameNOT_EQUAL_EQUAL:
1396 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1397 return "!=="; //$NON-NLS-1$
1400 return "<"; //$NON-NLS-1$
1401 case TokenNameLESS_EQUAL:
1403 return "<="; //$NON-NLS-1$
1404 case TokenNameGREATER:
1406 return ">"; //$NON-NLS-1$
1407 case TokenNameGREATER_EQUAL:
1409 return ">="; //$NON-NLS-1$
1410 // case TokenNameinstanceof : // instanceof
1411 // return "instanceof"; //$NON-NLS-1$
1413 // + (15.17, 15.17.2)
1414 return "+"; //$NON-NLS-1$
1415 case TokenNameMINUS:
1417 return "-"; //$NON-NLS-1$
1418 case TokenNameMULTIPLY:
1420 return "*"; //$NON-NLS-1$
1421 case TokenNameDIVIDE:
1423 return "/"; //$NON-NLS-1$
1424 case TokenNameREMAINDER:
1426 return "%"; //$NON-NLS-1$
1427 case TokenNameLEFT_SHIFT:
1429 return "<<"; //$NON-NLS-1$
1430 case TokenNameRIGHT_SHIFT:
1432 return ">>"; //$NON-NLS-1$
1433 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1434 // return ">>>"; //$NON-NLS-1$
1436 // & (15.21, 15.21.1, 15.21.2)
1437 return "&"; //$NON-NLS-1$
1439 // | (15.21, 15.21.1, 15.21.2)
1440 return "|"; //$NON-NLS-1$
1442 // ^ (15.21, 15.21.1, 15.21.2)
1443 return "^"; //$NON-NLS-1$
1444 case TokenNameMULTIPLY_EQUAL:
1446 return "*="; //$NON-NLS-1$
1447 case TokenNameDIVIDE_EQUAL:
1449 return "/="; //$NON-NLS-1$
1450 case TokenNameREMAINDER_EQUAL:
1452 return "%="; //$NON-NLS-1$
1453 case TokenNamePLUS_EQUAL:
1455 return "+="; //$NON-NLS-1$
1456 case TokenNameMINUS_EQUAL:
1458 return "-="; //$NON-NLS-1$
1459 case TokenNameMINUS_GREATER:
1461 return "->"; //$NON-NLS-1$
1462 case TokenNameLEFT_SHIFT_EQUAL:
1464 return "<<="; //$NON-NLS-1$
1465 case TokenNameRIGHT_SHIFT_EQUAL:
1467 return ">>="; //$NON-NLS-1$
1468 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1469 // return ">>>="; //$NON-NLS-1$
1470 case TokenNameAND_EQUAL:
1472 return "&="; //$NON-NLS-1$
1473 case TokenNameXOR_EQUAL:
1475 return "^="; //$NON-NLS-1$
1476 case TokenNameOR_EQUAL:
1478 return "|="; //$NON-NLS-1$
1479 case TokenNameDOT_EQUAL:
1481 return ".="; //$NON-NLS-1$
1484 return "."; //$NON-NLS-1$
1486 return ""; //$NON-NLS-1$
1491 * Appends <code>stringToOutput</code> to the formatted output. <br>
1492 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1494 private void output(String stringToOutput) {
1495 char currentCharacter;
1496 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1497 currentCharacter = stringToOutput.charAt(i);
1498 if (currentCharacter != '\t') {
1499 currentLineBuffer.append(currentCharacter);
1504 private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
1505 newLine(newLineCount);
1506 formattedSource.append(scanner.source, scanner.startPosition, scanner.currentPosition - scanner.startPosition);
1510 * Appends <code>token</code> to the formatted output. <br>
1511 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1513 private void outputCurrentToken(int token) {
1514 char[] source = scanner.source;
1515 int startPosition = scanner.startPosition;
1517 case Scanner.TokenNameCOMMENT_PHPDOC:
1518 case Scanner.TokenNameCOMMENT_BLOCK:
1519 case Scanner.TokenNameCOMMENT_LINE:
1520 boolean endOfLine = false;
1521 int currentCommentOffset = getCurrentCommentOffset();
1522 int beginningOfLineSpaces = 0;
1524 currentCommentOffset = getCurrentCommentOffset();
1525 beginningOfLineSpaces = 0;
1526 boolean pendingCarriageReturn = false;
1527 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1528 char currentCharacter = source[i];
1529 updateMappedPositions(i);
1530 switch (currentCharacter) {
1532 pendingCarriageReturn = true;
1536 if (pendingCarriageReturn) {
1537 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1539 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1541 pendingCarriageReturn = false;
1542 currentLineBuffer.append(options.lineSeparatorSequence);
1543 beginningOfLineSpaces = 0;
1547 if (pendingCarriageReturn) {
1548 pendingCarriageReturn = false;
1549 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1550 currentLineBuffer.append(options.lineSeparatorSequence);
1551 beginningOfLineSpaces = 0;
1555 // we remove a maximum of currentCommentOffset characters (tabs
1556 // are converted to space numbers).
1557 beginningOfLineSpaces += options.tabSize;
1558 if (beginningOfLineSpaces > currentCommentOffset) {
1559 currentLineBuffer.append(currentCharacter);
1561 increaseGlobalDelta(-1);
1564 currentLineBuffer.append(currentCharacter);
1568 if (pendingCarriageReturn) {
1569 pendingCarriageReturn = false;
1570 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1571 currentLineBuffer.append(options.lineSeparatorSequence);
1572 beginningOfLineSpaces = 0;
1576 // we remove a maximum of currentCommentOffset characters (tabs
1577 // are converted to space numbers).
1578 beginningOfLineSpaces++;
1579 if (beginningOfLineSpaces > currentCommentOffset) {
1580 currentLineBuffer.append(currentCharacter);
1582 increaseGlobalDelta(-1);
1585 currentLineBuffer.append(currentCharacter);
1589 if (pendingCarriageReturn) {
1590 pendingCarriageReturn = false;
1591 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1592 currentLineBuffer.append(options.lineSeparatorSequence);
1593 beginningOfLineSpaces = 0;
1596 beginningOfLineSpaces = 0;
1597 currentLineBuffer.append(currentCharacter);
1602 updateMappedPositions(scanner.currentPosition - 1);
1603 multipleLineCommentCounter++;
1606 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1607 char currentCharacter = source[i];
1608 updateMappedPositions(i);
1609 currentLineBuffer.append(currentCharacter);
1615 * Outputs <code>currentString</code>:<br>
1617 * <li>If its length is < maxLineLength, output
1618 * <li>Otherwise it is split.
1621 * @param currentString
1623 * @param preIndented
1624 * whether the string to output was pre-indented
1626 * number of indentation to put in front of <code>currentString</code>
1628 * value of the operator belonging to <code>currentString</code>.
1630 private void outputLine(String currentString, boolean preIndented, int depth, int operator, int substringIndex,
1631 int[] startSubstringIndexes, int offsetInGlobalLine) {
1632 boolean emptyFirstSubString = false;
1633 String operatorString = operatorString(operator);
1634 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1635 boolean placeOperatorAhead = !placeOperatorBehind;
1636 // dump prefix operator?
1637 if (placeOperatorAhead) {
1642 if (operator != 0) {
1643 if (insertSpaceBefore(operator)) {
1644 formattedSource.append(' ');
1645 increaseSplitDelta(1);
1647 formattedSource.append(operatorString);
1648 increaseSplitDelta(operatorString.length());
1649 if (insertSpaceAfter(operator) && operator != TokenNameimplements && operator != TokenNameextends) {
1650 // && operator != TokenNamethrows) {
1651 formattedSource.append(' ');
1652 increaseSplitDelta(1);
1656 SplitLine splitLine = null;
1657 if (options.maxLineLength == 0 || getLength(currentString, depth) < options.maxLineLength
1658 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1659 // depending on the type of operator, outputs new line before of after
1661 // indent before postfix operator
1662 // indent also when the line cannot be split
1663 if (operator == TokenNameextends || operator == TokenNameimplements) {
1664 // || operator == TokenNamethrows) {
1665 formattedSource.append(' ');
1666 increaseSplitDelta(1);
1668 if (placeOperatorBehind) {
1673 int max = currentString.length();
1674 if (multipleLineCommentCounter != 0) {
1676 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1677 String line = reader.readLine();
1678 while (line != null) {
1679 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + line.length()
1680 + options.lineSeparatorSequence.length);
1681 formattedSource.append(line);
1682 beginningOfLineIndex = beginningOfLineIndex + line.length();
1683 if ((line = reader.readLine()) != null) {
1684 formattedSource.append(options.lineSeparatorSequence);
1685 beginningOfLineIndex += options.lineSeparatorSequence.length;
1686 dumpTab(currentLineIndentationLevel);
1690 } catch (IOException e) {
1691 e.printStackTrace();
1694 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1695 for (int i = 0; i < max; i++) {
1696 char currentChar = currentString.charAt(i);
1697 switch (currentChar) {
1702 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1703 // split with a comment inside a condition
1704 // a substring cannot end with a lineSeparatorSequence,
1705 // except if it has been added by format() after a one-line
1707 formattedSource.append(options.lineSeparatorSequence);
1708 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1713 formattedSource.append(currentChar);
1717 // update positions inside the mappedPositions table
1718 if (substringIndex != -1) {
1719 if (multipleLineCommentCounter == 0) {
1720 int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1721 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1723 // compute the splitDelta resulting with the operator and blank removal
1724 if (substringIndex + 1 != startSubstringIndexes.length) {
1725 increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1728 // dump postfix operator?
1729 if (placeOperatorBehind) {
1730 if (insertSpaceBefore(operator)) {
1731 formattedSource.append(' ');
1732 if (operator != 0) {
1733 increaseSplitDelta(1);
1736 formattedSource.append(operatorString);
1737 if (operator != 0) {
1738 increaseSplitDelta(operatorString.length());
1743 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1744 // extends has to stand alone on a line when currentString has been split.
1745 if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1746 // || operator == TokenNameimplements
1747 // || operator == TokenNamethrows)) {
1748 formattedSource.append(options.lineSeparatorSequence);
1749 increaseSplitDelta(options.lineSeparatorSequence.length);
1752 if (operator == TokenNameextends) {
1753 // || operator == TokenNameimplements
1754 // || operator == TokenNamethrows) {
1755 formattedSource.append(' ');
1756 increaseSplitDelta(1);
1759 // perform actual splitting
1760 String result[] = splitLine.substrings;
1761 int[] splitOperators = splitLine.operators;
1762 if (result[0].length() == 0) {
1763 // when the substring 0 is null, the substring 1 is correctly indented.
1765 emptyFirstSubString = true;
1767 // the operator going in front of the result[0] string is the operator
1769 for (int i = 0, max = result.length; i < max; i++) {
1770 // the new depth is the current one if this is the first substring,
1771 // the current one + 1 otherwise.
1772 // if the substring is a comment, use the current indentation Level
1773 // instead of the depth
1774 // (-1 because the ouputline increases depth).
1775 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1777 String currentResult = result[i];
1778 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1779 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1780 || currentResult.startsWith("//")) //$NON-NLS-1$
1781 ? indentationLevel - 1 : depth;
1782 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1783 i == 0 ? newDepth : newDepth + 1, splitOperators[i], i, splitLine.startSubstringsIndexes, currentString
1784 .indexOf(currentResult));
1786 formattedSource.append(options.lineSeparatorSequence);
1787 increaseSplitDelta(options.lineSeparatorSequence.length);
1791 if (result.length == splitOperators.length - 1) {
1792 int lastOperator = splitOperators[result.length];
1793 String lastOperatorString = operatorString(lastOperator);
1794 formattedSource.append(options.lineSeparatorSequence);
1795 increaseSplitDelta(options.lineSeparatorSequence.length);
1796 if (breakLineBeforeOperator(lastOperator)) {
1798 if (lastOperator != 0) {
1799 if (insertSpaceBefore(lastOperator)) {
1800 formattedSource.append(' ');
1801 increaseSplitDelta(1);
1803 formattedSource.append(lastOperatorString);
1804 increaseSplitDelta(lastOperatorString.length());
1805 if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements && lastOperator != TokenNameextends) {
1806 // && lastOperator != TokenNamethrows) {
1807 formattedSource.append(' ');
1808 increaseSplitDelta(1);
1813 if (placeOperatorBehind) {
1814 if (insertSpaceBefore(operator)) {
1815 formattedSource.append(' ');
1816 increaseSplitDelta(1);
1818 formattedSource.append(operatorString);
1819 //increaseSplitDelta(operatorString.length());
1824 * Pops the top statement of the stack if it is <code>token</code>
1826 private int pop(int token) {
1828 if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1830 constructionsCount--;
1836 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1838 private int popBlock() {
1840 if ((constructionsCount > 0)
1841 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1842 if (constructions[constructionsCount - 1] == BLOCK)
1844 constructionsCount--;
1850 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1851 * Does not remove <code>token</code> from the stack.
1854 * the token to be left as the top of the stack
1856 private int popExclusiveUntil(int token) {
1858 int startCount = constructionsCount;
1859 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1860 if (constructions[i] != NONINDENT_BLOCK)
1862 constructionsCount--;
1868 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1869 * Does not remove it from the stack.
1871 private int popExclusiveUntilBlock() {
1872 int startCount = constructionsCount;
1874 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1875 constructionsCount--;
1882 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
1883 * <code>CASE</code>.<br>
1884 * Does not remove it from the stack.
1886 private int popExclusiveUntilBlockOrCase() {
1887 int startCount = constructionsCount;
1889 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK
1890 && constructions[i] != TokenNamecase; i--) {
1891 constructionsCount--;
1898 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1899 * Removes <code>token</code> from the stack too.
1902 * the token to remove from the stack
1904 private int popInclusiveUntil(int token) {
1905 int startCount = constructionsCount;
1907 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1908 if (constructions[i] != NONINDENT_BLOCK)
1910 constructionsCount--;
1912 if (constructionsCount > 0) {
1913 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1915 constructionsCount--;
1921 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1922 * Does not remove it from the stack.
1924 private int popInclusiveUntilBlock() {
1925 int startCount = constructionsCount;
1927 for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1929 constructionsCount--;
1931 if (constructionsCount > 0) {
1932 if (constructions[constructionsCount - 1] == BLOCK)
1934 constructionsCount--;
1940 * Pushes a block in the stack. <br>
1941 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>, pushes
1942 * <code>NONINDENT_BLOCK</code> otherwise. Creates a new bigger array if the current one is full.
1944 private int pushBlock() {
1946 if (constructionsCount == constructions.length)
1947 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1948 if ((constructionsCount == 0) || (constructions[constructionsCount - 1] == BLOCK)
1949 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK) || (constructions[constructionsCount - 1] == TokenNamecase)) {
1951 constructions[constructionsCount++] = BLOCK;
1953 constructions[constructionsCount++] = NONINDENT_BLOCK;
1959 * Pushes <code>token</code>.<br>
1960 * Creates a new bigger array if the current one is full.
1962 private int pushControlStatement(int token) {
1963 if (constructionsCount == constructions.length)
1964 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1965 constructions[constructionsCount++] = token;
1969 private static boolean separateFirstArgumentOn(int currentToken) {
1970 //return (currentToken == TokenNameCOMMA || currentToken ==
1971 // TokenNameSEMICOLON);
1972 return currentToken != TokenNameif && currentToken != TokenNameLPAREN && currentToken != TokenNameNOT
1973 && currentToken != TokenNamewhile && currentToken != TokenNamefor && currentToken != TokenNameforeach
1974 && currentToken != TokenNameswitch;
1978 * Set the positions to map. The mapped positions should be retrieved using the getMappedPositions() method.
1982 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1984 * @see #getMappedPositions()
1986 public void setPositionsToMap(int[] positions) {
1987 positionsToMap = positions;
1990 mappedPositions = new int[positions.length];
1994 * Appends a space character to the current line buffer.
1996 private void space() {
1997 currentLineBuffer.append(' ');
1998 increaseLineDelta(1);
2002 * Splits <code>stringToSplit</code> on the top level token <br>
2003 * If there are several identical token at the same level, the string is cut into many pieces.
2005 * @return an object containing the operator and all the substrings or null if the string cannot be split
2007 public SplitLine split(String stringToSplit) {
2008 return split(stringToSplit, 0);
2012 * Splits <code>stringToSplit</code> on the top level token <br>
2013 * If there are several identical token at the same level, the string is cut into many pieces.
2015 * @return an object containing the operator and all the substrings or null if the string cannot be split
2017 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2019 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2021 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2024 // split doesn't work correct for PHP
2027 // int currentToken = 0;
2028 // int splitTokenType = 0;
2029 // int splitTokenDepth = Integer.MAX_VALUE;
2030 // int splitTokenPriority = Integer.MAX_VALUE;
2031 // int[] substringsStartPositions = new int[10];
2032 // // contains the start position of substrings
2033 // int[] substringsEndPositions = new int[10];
2034 // // contains the start position of substrings
2035 // int substringsCount = 1; // index in the substringsStartPosition array
2036 // int[] splitOperators = new int[10];
2037 // // contains the start position of substrings
2038 // int splitOperatorsCount = 0; // index in the substringsStartPosition array
2039 // int[] openParenthesisPosition = new int[10];
2040 // int openParenthesisPositionCount = 0;
2041 // int position = 0;
2042 // int lastOpenParenthesisPosition = -1;
2043 // // used to remember the position of the 1st open parenthesis
2044 // // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2045 // // setup the scanner with a new source
2046 // int lastCommentStartPosition = -1;
2047 // // to remember the start position of the last comment
2048 // int firstTokenOnLine = -1;
2049 // // to remember the first token of the line
2050 // int previousToken = -1;
2051 // // to remember the previous token.
2052 // splitScanner.setSource(stringToSplit.toCharArray());
2054 // // start the loop
2056 // // takes the next token
2058 // if (currentToken != Scanner.TokenNameWHITESPACE)
2059 // previousToken = currentToken;
2060 // currentToken = splitScanner.getNextToken();
2061 // if (Scanner.DEBUG) {
2062 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2063 // int currentStartPosition = splitScanner
2064 // .getCurrentTokenStartPosition();
2065 // System.out.print(currentStartPosition + "," + currentEndPosition
2067 // System.out.println(scanner.toStringAction(currentToken));
2069 // } catch (InvalidInputException e) {
2070 // if (!handleInvalidToken(e))
2072 // currentToken = 0;
2073 // // this value is not modify when an exception is raised.
2075 // if (currentToken == TokenNameEOF)
2077 // if (firstTokenOnLine == -1) {
2078 // firstTokenOnLine = currentToken;
2080 // switch (currentToken) {
2081 // case TokenNameRBRACE :
2082 // case TokenNameRPAREN :
2083 // if (openParenthesisPositionCount > 0) {
2084 // if (openParenthesisPositionCount == 1
2085 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2086 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2087 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2088 // || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2089 // splitTokenType = 0;
2090 // splitTokenDepth = openParenthesisPositionCount;
2091 // splitTokenPriority = Integer.MAX_VALUE;
2092 // substringsStartPositions[0] = 0;
2093 // // better token means the whole line until now is the first
2095 // substringsCount = 1; // resets the count of substrings
2096 // substringsEndPositions[0] = openParenthesisPosition[0];
2097 // // substring ends on operator start
2098 // position = openParenthesisPosition[0];
2099 // // the string mustn't be cut before the closing parenthesis but
2100 // // after the opening one.
2101 // splitOperatorsCount = 1; // resets the count of split operators
2102 // splitOperators[0] = 0;
2104 // openParenthesisPositionCount--;
2107 // case TokenNameLBRACE :
2108 // case TokenNameLPAREN :
2109 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2112 // openParenthesisPosition,
2114 // (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2115 // 0, openParenthesisPositionCount);
2117 // openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2118 // if (currentToken == TokenNameLPAREN
2119 // && previousToken == TokenNameRPAREN) {
2120 // openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2123 // case TokenNameSEMICOLON :
2125 // case TokenNameCOMMA :
2127 // case TokenNameEQUAL :
2129 // if (openParenthesisPositionCount < splitTokenDepth
2130 // || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2131 // // the current token is better than the one we currently have
2132 // // (in level or in priority if same level)
2133 // // reset the substringsCount
2134 // splitTokenDepth = openParenthesisPositionCount;
2135 // splitTokenType = currentToken;
2136 // splitTokenPriority = getTokenPriority(currentToken);
2137 // substringsStartPositions[0] = 0;
2138 // // better token means the whole line until now is the first
2140 // if (separateFirstArgumentOn(firstTokenOnLine)
2141 // && openParenthesisPositionCount > 0) {
2142 // substringsCount = 2; // resets the count of substrings
2143 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2144 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2145 // substringsEndPositions[1] = splitScanner.startPosition;
2146 // splitOperatorsCount = 2; // resets the count of split operators
2147 // splitOperators[0] = 0;
2148 // splitOperators[1] = currentToken;
2149 // position = splitScanner.currentPosition;
2150 // // next substring will start from operator end
2152 // substringsCount = 1; // resets the count of substrings
2153 // substringsEndPositions[0] = splitScanner.startPosition;
2154 // // substring ends on operator start
2155 // position = splitScanner.currentPosition;
2156 // // next substring will start from operator end
2157 // splitOperatorsCount = 1; // resets the count of split operators
2158 // splitOperators[0] = currentToken;
2161 // if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2162 // && splitTokenType != TokenNameEQUAL
2163 // && currentToken != TokenNameEQUAL) {
2164 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2166 // // take only the 1st = into account.
2167 // // if another token with the same priority is found,
2168 // // push the start position of the substring and
2169 // // push the token into the stack.
2170 // // create a new array object if the current one is full.
2171 // if (substringsCount == substringsStartPositions.length) {
2174 // substringsStartPositions,
2176 // (substringsStartPositions = new int[substringsCount * 2]),
2177 // 0, substringsCount);
2178 // System.arraycopy(substringsEndPositions, 0,
2179 // (substringsEndPositions = new int[substringsCount * 2]),
2180 // 0, substringsCount);
2182 // if (splitOperatorsCount == splitOperators.length) {
2183 // System.arraycopy(splitOperators, 0,
2184 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2185 // splitOperatorsCount);
2187 // substringsStartPositions[substringsCount] = position;
2188 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2189 // // substring ends on operator start
2190 // position = splitScanner.currentPosition;
2191 // // next substring will start from operator end
2192 // splitOperators[splitOperatorsCount++] = currentToken;
2196 // case TokenNameCOLON :
2198 // // see 1FK7C5R, we only split on a colon, when it is associated
2199 // // with a question-mark.
2200 // // indeed it might appear also behind a case statement, and we do
2201 // // not to break at this point.
2202 // if ((splitOperatorsCount == 0)
2203 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2206 // case TokenNameextends :
2207 // case TokenNameimplements :
2208 // //case TokenNamethrows :
2209 // case TokenNameDOT :
2211 // case TokenNameMULTIPLY :
2213 // case TokenNameDIVIDE :
2215 // case TokenNameREMAINDER :
2217 // case TokenNamePLUS :
2218 // // + (15.17, 15.17.2)
2219 // case TokenNameMINUS :
2221 // case TokenNameLEFT_SHIFT :
2223 // case TokenNameRIGHT_SHIFT :
2225 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2226 // case TokenNameLESS :
2228 // case TokenNameLESS_EQUAL :
2230 // case TokenNameGREATER :
2232 // case TokenNameGREATER_EQUAL :
2234 // // case TokenNameinstanceof : // instanceof
2235 // case TokenNameEQUAL_EQUAL :
2236 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2237 // case TokenNameEQUAL_EQUAL_EQUAL :
2238 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2239 // case TokenNameNOT_EQUAL :
2240 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2241 // case TokenNameNOT_EQUAL_EQUAL :
2242 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2243 // case TokenNameAND :
2244 // // & (15.21, 15.21.1, 15.21.2)
2245 // case TokenNameOR :
2246 // // | (15.21, 15.21.1, 15.21.2)
2247 // case TokenNameXOR :
2248 // // ^ (15.21, 15.21.1, 15.21.2)
2249 // case TokenNameAND_AND :
2251 // case TokenNameOR_OR :
2253 // case TokenNameQUESTION :
2255 // case TokenNameMULTIPLY_EQUAL :
2257 // case TokenNameDIVIDE_EQUAL :
2259 // case TokenNameREMAINDER_EQUAL :
2261 // case TokenNamePLUS_EQUAL :
2263 // case TokenNameMINUS_EQUAL :
2265 // case TokenNameLEFT_SHIFT_EQUAL :
2267 // case TokenNameRIGHT_SHIFT_EQUAL :
2269 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2270 // case TokenNameAND_EQUAL :
2272 // case TokenNameXOR_EQUAL :
2274 // case TokenNameOR_EQUAL :
2276 // if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority
2277 // > getTokenPriority(currentToken)))
2278 // && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2279 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2280 // // the current token is better than the one we currently have
2281 // // (in level or in priority if same level)
2282 // // reset the substringsCount
2283 // splitTokenDepth = openParenthesisPositionCount;
2284 // splitTokenType = currentToken;
2285 // splitTokenPriority = getTokenPriority(currentToken);
2286 // substringsStartPositions[0] = 0;
2287 // // better token means the whole line until now is the first
2289 // if (separateFirstArgumentOn(firstTokenOnLine)
2290 // && openParenthesisPositionCount > 0) {
2291 // substringsCount = 2; // resets the count of substrings
2292 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2293 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2294 // substringsEndPositions[1] = splitScanner.startPosition;
2295 // splitOperatorsCount = 3; // resets the count of split operators
2296 // splitOperators[0] = 0;
2297 // splitOperators[1] = 0;
2298 // splitOperators[2] = currentToken;
2299 // position = splitScanner.currentPosition;
2300 // // next substring will start from operator end
2302 // substringsCount = 1; // resets the count of substrings
2303 // substringsEndPositions[0] = splitScanner.startPosition;
2304 // // substring ends on operator start
2305 // position = splitScanner.currentPosition;
2306 // // next substring will start from operator end
2307 // splitOperatorsCount = 2; // resets the count of split operators
2308 // splitOperators[0] = 0;
2309 // // nothing for first operand since operator will be inserted in
2310 // // front of the second operand
2311 // splitOperators[1] = currentToken;
2314 // if (openParenthesisPositionCount == splitTokenDepth
2315 // && splitTokenPriority == getTokenPriority(currentToken)) {
2316 // // if another token with the same priority is found,
2317 // // push the start position of the substring and
2318 // // push the token into the stack.
2319 // // create a new array object if the current one is full.
2320 // if (substringsCount == substringsStartPositions.length) {
2323 // substringsStartPositions,
2325 // (substringsStartPositions = new int[substringsCount * 2]),
2326 // 0, substringsCount);
2327 // System.arraycopy(substringsEndPositions, 0,
2328 // (substringsEndPositions = new int[substringsCount * 2]),
2329 // 0, substringsCount);
2331 // if (splitOperatorsCount == splitOperators.length) {
2332 // System.arraycopy(splitOperators, 0,
2333 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2334 // splitOperatorsCount);
2336 // substringsStartPositions[substringsCount] = position;
2337 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2338 // // substring ends on operator start
2339 // position = splitScanner.currentPosition;
2340 // // next substring will start from operator end
2341 // splitOperators[splitOperatorsCount++] = currentToken;
2347 // if (isComment(currentToken)) {
2348 // lastCommentStartPosition = splitScanner.startPosition;
2350 // lastCommentStartPosition = -1;
2353 // } catch (InvalidInputException e) {
2356 // // if the string cannot be split, return null.
2357 // if (splitOperatorsCount == 0)
2359 // // ## SPECIAL CASES BEGIN
2360 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2361 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2362 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2363 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) ||
2364 // (separateFirstArgumentOn(firstTokenOnLine)
2365 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2366 // && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2367 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2368 // // not be broken on two lines
2369 // // only one split on a top level .
2370 // // or more than one split on . and substring before open parenthesis fits
2372 // // or split inside parenthesis and first token is not a for/while/if
2373 // SplitLine sl = split(
2374 // stringToSplit.substring(lastOpenParenthesisPosition),
2375 // lastOpenParenthesisPosition);
2376 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2377 // // trim() is used to remove the extra blanks at the end of the
2378 // // substring. See PR 1FGYPI1
2379 // return new SplitLine(new int[]{0, 0}, new String[]{
2380 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2381 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2382 // offsetInGlobalLine,
2383 // lastOpenParenthesisPosition + offsetInGlobalLine});
2385 // // right substring can be split and is split on comma
2386 // // copy substrings and operators
2387 // // except if the 1st string is empty.
2388 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2389 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2390 // String[] result = new String[subStringsLength];
2391 // int[] startIndexes = new int[subStringsLength];
2392 // int operatorsLength = sl.operators.length + 1 - startIndex;
2393 // int[] operators = new int[operatorsLength];
2394 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2395 // operators[0] = 0;
2396 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2397 // 1, subStringsLength - 1);
2398 // for (int i = subStringsLength - 1; i >= 0; i--) {
2399 // startIndexes[i] += offsetInGlobalLine;
2401 // System.arraycopy(sl.substrings, startIndex, result, 1,
2402 // subStringsLength - 1);
2403 // System.arraycopy(sl.operators, startIndex, operators, 1,
2404 // operatorsLength - 1);
2405 // return new SplitLine(operators, result, startIndexes);
2408 // // if the last token is a comment and the substring before the comment fits
2410 // // split before the comment and return the result.
2411 // if (lastCommentStartPosition > -1
2412 // && lastCommentStartPosition < options.maxLineLength
2413 // && splitTokenPriority > 50) {
2414 // int end = lastCommentStartPosition;
2415 // int start = lastCommentStartPosition;
2416 // if (stringToSplit.charAt(end - 1) == ' ') {
2419 // if (start != end && stringToSplit.charAt(start) == ' ') {
2422 // return new SplitLine(new int[]{0, 0}, new String[]{
2423 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2424 // new int[]{0, start});
2426 // if (position != stringToSplit.length()) {
2427 // if (substringsCount == substringsStartPositions.length) {
2428 // System.arraycopy(substringsStartPositions, 0,
2429 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2430 // substringsCount);
2431 // System.arraycopy(substringsEndPositions, 0,
2432 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2433 // substringsCount);
2435 // // avoid empty extra substring, e.g. line terminated with a semi-colon
2436 // substringsStartPositions[substringsCount] = position;
2437 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2439 // if (splitOperatorsCount == splitOperators.length) {
2440 // System.arraycopy(splitOperators, 0,
2441 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2442 // splitOperatorsCount);
2444 // splitOperators[splitOperatorsCount] = 0;
2445 // // the last element of the stack is the position of the end of
2447 // // +1 because the substring method excludes the last character
2448 // String[] result = new String[substringsCount];
2449 // for (int i = 0; i < substringsCount; i++) {
2450 // int start = substringsStartPositions[i];
2451 // int end = substringsEndPositions[i];
2452 // if (stringToSplit.charAt(start) == ' ') {
2454 // substringsStartPositions[i]++;
2456 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2459 // result[i] = stringToSplit.substring(start, end);
2460 // substringsStartPositions[i] += offsetInGlobalLine;
2462 // if (splitOperatorsCount > substringsCount) {
2463 // System.arraycopy(substringsStartPositions, 0,
2464 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2465 // substringsCount);
2466 // System.arraycopy(substringsEndPositions, 0,
2467 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2468 // substringsCount);
2469 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2470 // substringsStartPositions[i] = position;
2471 // substringsEndPositions[i] = position;
2473 // System.arraycopy(splitOperators, 0,
2474 // (splitOperators = new int[splitOperatorsCount]), 0,
2475 // splitOperatorsCount);
2477 // System.arraycopy(substringsStartPositions, 0,
2478 // (substringsStartPositions = new int[substringsCount]), 0,
2479 // substringsCount);
2480 // System.arraycopy(substringsEndPositions, 0,
2481 // (substringsEndPositions = new int[substringsCount]), 0,
2482 // substringsCount);
2483 // System.arraycopy(splitOperators, 0,
2484 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2486 // SplitLine splitLine = new SplitLine(splitOperators, result,
2487 // substringsStartPositions);
2488 // return splitLine;
2491 private void updateMappedPositions(int startPosition) {
2492 if (positionsToMap == null) {
2495 char[] source = scanner.source;
2496 int sourceLength = source.length;
2497 while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2498 int posToMap = positionsToMap[indexToMap];
2499 if (posToMap < 0 || posToMap >= sourceLength) {
2500 // protection against out of bounds position
2501 if (posToMap == sourceLength) {
2502 mappedPositions[indexToMap] = formattedSource.length();
2504 indexToMap = positionsToMap.length; // no more mapping
2507 if (CharOperation.isWhitespace(source[posToMap])) {
2508 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2510 if (posToMap == sourceLength - 1) {
2511 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2513 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2520 private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2521 if (mappedPositions == null || mappedPositions.length == indexInMap)
2523 while (indexInMap < mappedPositions.length && startPosition <= mappedPositions[indexInMap]
2524 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2525 mappedPositions[indexInMap] += splitDelta;
2530 private int getLength(String s, int tabDepth) {
2532 for (int i = 0; i < tabDepth; i++) {
2533 length += options.tabSize;
2535 for (int i = 0, max = s.length(); i < max; i++) {
2536 char currentChar = s.charAt(i);
2537 switch (currentChar) {
2539 length += options.tabSize;
2549 * Sets the initial indentation level
2551 * @param indentationLevel
2552 * new indentation level
2556 public void setInitialIndentationLevel(int newIndentationLevel) {
2557 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;