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.corext.codemanipulation.StubUtility;
27 import net.sourceforge.phpdt.internal.corext.util.Strings;
28 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
29 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
30 import net.sourceforge.phpdt.internal.ui.preferences.CodeFormatterPreferencePage;
32 import org.eclipse.jface.text.IDocument;
33 import org.eclipse.jface.text.formatter.IContentFormatterExtension;
34 import org.eclipse.jface.text.formatter.IFormattingContext;
37 * <h2>How to format a piece of code ?</h2>
39 * <li>Create an instance of <code>CodeFormatter</code>
40 * <li>Use the method <code>void format(aString)</code> on this instance to format <code>aString</code>. It will return the
44 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
45 // IContentFormatterExtension {
46 public FormatterOptions options;
49 * Represents a block in the <code>constructions</code> stack.
51 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
54 * Represents a block following a control statement in the <code>constructions</code> stack.
56 public static final int NONINDENT_BLOCK = -100;
59 * Contains the formatted output.
61 StringBuffer formattedSource;
64 * Contains the current line. <br>
65 * Will be dumped at the next "newline"
67 StringBuffer currentLineBuffer;
70 * Used during the formatting to get each token.
75 * Contains the tokens responsible for the current indentation level and the blocks not closed yet.
77 private int[] constructions;
80 * Index in the <code>constructions</code> array.
82 private int constructionsCount;
85 * Level of indentation of the current token (number of tab char put in front of it).
87 private int indentationLevel;
90 * Regular level of indentation of all the lines
92 private int initialIndentationLevel;
95 * Used to split a line.
100 * To remember the offset between the beginning of the line and the beginning of the comment.
102 int currentCommentOffset;
104 int currentLineIndentationLevel;
106 int maxLineSize = 30;
108 private boolean containsOpenCloseBraces;
110 private int indentationLevelForOpenCloseBraces;
113 * Collections of positions to map
115 private int[] positionsToMap;
118 * Collections of mapped positions
120 private int[] mappedPositions;
122 private int indexToMap;
124 private int indexInMap;
126 private int globalDelta;
128 private int lineDelta;
130 private int splitDelta;
132 private int beginningOfLineIndex;
134 private int multipleLineCommentCounter;
137 * Creates a new instance of Code Formatter using the given settings.
139 * @deprecated backport 1.0 internal functionality
141 public CodeFormatter(ConfigurableOption[] settings) {
142 this(convertConfigurableOptions(settings));
146 * Creates a new instance of Code Formatter using the FormattingOptions object given as argument
148 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
150 public CodeFormatter() {
155 * Creates a new instance of Code Formatter using the given settings.
157 public CodeFormatter(Map settings) {
158 // initialize internal state
159 constructionsCount = 0;
160 constructions = new int[10];
161 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
162 currentCommentOffset = -1;
163 // initialize primary and secondary scanners
164 scanner = new Scanner(true /* comment */
165 , true /* whitespace */
168 , true, /* tokenizeStrings */
169 null, null); // regular scanner for forming lines
170 scanner.recordLineSeparator = true;
171 scanner.ignorePHPOneLiner = true;
172 // to remind of the position of the beginning of the line.
173 splitScanner = new Scanner(true /* comment */
174 , true /* whitespace */
177 , true, /* tokenizeStrings */
179 splitScanner.ignorePHPOneLiner = true;
180 // secondary scanner to split long lines formed by primary scanning
181 // initialize current line buffer
182 currentLineBuffer = new StringBuffer();
183 this.options = new FormatterOptions(settings);
187 * Returns true if a lineSeparator has to be inserted before <code>operator</code> false otherwise.
189 private static boolean breakLineBeforeOperator(int operator) {
192 case TokenNameSEMICOLON:
201 * @deprecated backport 1.0 internal functionality
203 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
204 Hashtable options = new Hashtable(10);
205 for (int i = 0; i < settings.length; i++) {
206 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
207 String optionName = settings[i].getOptionName();
208 int valueIndex = settings[i].getCurrentValueIndex();
209 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
210 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
211 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
213 .put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
214 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
215 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
217 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
218 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
219 options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
220 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
221 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
222 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
223 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
225 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
226 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
227 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
235 * Returns the end of the source code.
237 private final String copyRemainingSource() {
238 char str[] = scanner.source;
239 int startPosition = scanner.startPosition;
240 int length = str.length - startPosition;
241 StringBuffer bufr = new StringBuffer(length);
242 if (startPosition < str.length) {
243 bufr.append(str, startPosition, length);
245 return (bufr.toString());
249 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
251 private void dumpTab(int tabCount) {
252 if (options.indentWithTab) {
253 for (int j = 0; j < tabCount; j++) {
254 formattedSource.append('\t');
255 increaseSplitDelta(1);
258 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
259 formattedSource.append(' ');
260 increaseSplitDelta(1);
266 * Dumps <code>currentLineBuffer</code> into the formatted string.
268 private void flushBuffer() {
269 String currentString = currentLineBuffer.toString();
271 beginningOfLineIndex = formattedSource.length();
272 if (containsOpenCloseBraces) {
273 containsOpenCloseBraces = false;
274 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
275 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
277 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
279 int scannerSourceLength = scanner.source.length;
280 if (scannerSourceLength > 2) {
281 if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
282 formattedSource.append(options.lineSeparatorSequence);
283 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
284 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
285 formattedSource.append(options.lineSeparatorSequence);
286 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
287 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
288 formattedSource.append(options.lineSeparatorSequence);
289 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
292 updateMappedPositions(scanner.startPosition);
296 * Formats the input string.
298 private void format() {
300 int previousToken = 0;
301 int previousCompilableToken = 0;
302 int indentationOffset = 0;
303 int newLinesInWhitespace = 0;
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;
318 // openBracketCount is used to count the number of open brackets not closed
320 int openBracketCount = 0;
321 int unarySignModifier = 0;
322 // openParenthesis[0] is used to count the parenthesis not belonging to a
324 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
325 int openParenthesisCount = 1;
326 int[] openParenthesis = new int[10];
327 // 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];
332 constructionsCount = 0; // initializes the constructions count.
333 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
335 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
336 boolean specialElse = false;
337 // OPTION (IndentationLevel): initial indentation level may be non-zero.
338 currentLineIndentationLevel += constructionsCount;
339 // An InvalidInputException exception might cause the termination of this
343 // Get the next token. Catch invalid input and output it
344 // with minimal formatting, also catch end of input and
347 token = scanner.getNextToken();
349 int currentEndPosition = scanner.getCurrentTokenEndPosition();
350 int currentStartPosition = scanner.getCurrentTokenStartPosition();
351 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
352 System.out.println(scanner.toStringAction(token));
354 // Patch for line comment
355 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
356 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
357 int length = scanner.currentPosition;
358 loop: for (int index = length - 1; index >= 0; index--) {
359 switch (scanner.source[index]) {
362 scanner.currentPosition--;
369 } catch (InvalidInputException e) {
370 if (!handleInvalidToken(e)) {
375 if (token == Scanner.TokenNameEOF) {
377 } else if (token == Scanner.TokenNameHEREDOC) {
378 // no indentation for heredocs and HTML !
379 outputCurrentTokenWithoutIndent(Scanner.TokenNameHEREDOC, 0);
381 } else if (token == Scanner.TokenNameINLINE_HTML) {
382 // no indentation for heredocs and HTML !
383 int newLineCount = 1;
384 if (scanner.startPosition==0) {
387 outputCurrentTokenWithoutIndent(Scanner.TokenNameINLINE_HTML, newLineCount);
388 int srcLen = scanner.source.length;
389 if (scanner.currentPosition < srcLen-1) {
395 * ## MODIFYING the indentation level before generating new lines and indentation in the output string
397 // Removes all the indentations made by statements not followed by a
399 // except if the current token is ELSE, CATCH or if we are in a
401 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
404 if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
408 indentationLevel += popInclusiveUntil(TokenNameif);
410 // case TokenNamecatch :
411 // indentationLevel += popInclusiveUntil(TokenNamecatch);
413 // case TokenNamefinally :
414 // indentationLevel += popInclusiveUntil(TokenNamecatch);
417 if (nlicsToken == TokenNamedo) {
418 indentationLevel += pop(TokenNamedo);
422 indentationLevel += popExclusiveUntilBlockOrCase();
423 // clear until a CASE, DEFAULT or BLOCK is encountered.
424 // Thus, the indentationLevel is correctly cleared either
425 // in a switch/case statement or in any other situation.
427 clearNonBlockIndents = false;
429 // returns to the indentation level created by the SWITCH keyword
430 // if the current token is a CASE or a DEFAULT
431 if (token == TokenNamecase || token == TokenNamedefault) {
432 indentationLevel += pop(TokenNamecase);
434 // if (token == Scanner.TokenNamethrows) {
435 // inThrowsClause = true;
437 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface) && previousToken != Scanner.TokenNameDOT) {
438 inClassOrInterfaceHeader = true;
441 * ## APPEND newlines and indentations to the output string
443 // Do not add a new line between ELSE and IF, if the option
444 // elseIfOnSameLine is true.
445 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
446 // if (pendingNewlineAfterParen
447 // && previousCompilableToken == TokenNameelse
448 // && token == TokenNameif
449 // && options.compactElseIfMode) {
450 // pendingNewlineAfterParen = false;
451 // pendingNewLines = 0;
452 // indentationLevel += pop(TokenNameelse);
453 // // because else if is now one single statement,
454 // // the indentation level after it is increased by one and not by 2
455 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
457 // Add a newline & indent to the formatted source string if
458 // a for/if-else/while statement was scanned and there is no block
460 pendingNewlineAfterParen = pendingNewlineAfterParen
461 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
462 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
463 pendingNewlineAfterParen = false;
464 // Do to add a newline & indent sequence if the current token is an
465 // open brace or a period or if the current token is a semi-colon and
467 // previous token is a close paren.
468 // add a new line if a parenthesis belonging to a for() statement
469 // has been closed and the current token is not an opening brace
470 if (token != TokenNameLBRACE && !isComment(token)
471 // to avoid adding new line between else and a comment
472 && token != TokenNameDOT && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
474 currentLineIndentationLevel = indentationLevel;
476 pendingSpace = false;
478 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
480 if (constructionsCount > 0 && constructions[constructionsCount - 1] != BLOCK
481 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
482 currentLineIndentationLevel = indentationLevel - 1;
484 currentLineIndentationLevel = indentationLevel;
487 pendingSpace = false;
491 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode && constructionsCount > 0
492 && constructions[constructionsCount - 1] == TokenNamedo) {
494 currentLineIndentationLevel = indentationLevel - 1;
496 pendingSpace = false;
499 if (token == TokenNameLBRACE && inThrowsClause) {
500 inThrowsClause = false;
501 if (options.newLineBeforeOpeningBraceMode) {
503 currentLineIndentationLevel = indentationLevel;
505 pendingSpace = false;
509 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
510 inClassOrInterfaceHeader = false;
511 if (options.newLineBeforeOpeningBraceMode) {
513 currentLineIndentationLevel = indentationLevel;
515 pendingSpace = false;
518 // Add pending new lines to the formatted source string.
519 // Note: pending new lines are not added if the current token
520 // is a single line comment or whitespace.
521 // if the comment is between parenthesis, there is no blank line
523 // (if it's a one-line comment, a blank line is added after it).
524 if (((pendingNewLines > 0 && (!isComment(token)))
525 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
526 && token != Scanner.TokenNameWHITESPACE) {
527 // Do not add newline & indent between an adjoining close brace and
528 // close paren. Anonymous inner classes may use this form.
529 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
530 // OPTION (NewLineInCompoundStatement): do not add newline & indent
531 // between close brace and else, (do) while, catch, and finally if
532 // newlineInCompoundStatement is true.
533 boolean nlicsOption = previousToken == TokenNameRBRACE
534 && !options.newlineInControlStatementMode
535 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo) || token == TokenNamecatch || token == TokenNamefinally);
536 // Do not add a newline & indent between a close brace and
538 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
539 // Do not add a new line & indent between a multiline comment and a
541 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && token == TokenNameLBRACE;
542 // Do not add a newline & indent between a close brace and a colon
543 // (in array assignments, for example).
544 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameCOMMA;
545 // Add a newline and indent, if appropriate.
547 || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
548 // if clearAllBlankLinesMode=false, leaves the blank lines
549 // inserted by the user
550 // if clearAllBlankLinesMode=true, removes all of then
551 // and insert only blank lines required by the formatting.
552 if (!options.clearAllBlankLinesMode) {
553 // (isComment(token))
554 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
555 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
557 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
558 containsOpenCloseBraces = true;
559 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
560 if (isComment(previousToken)) {
561 newLine(pendingNewLines);
564 * if (!(constructionsCount > 1 && constructions[constructionsCount-1] == NONINDENT_BLOCK &&
565 * (constructions[constructionsCount-2] == TokenNamefor
567 if (options.newLineInEmptyBlockMode) {
568 if (inArrayAssignment) {
569 newLine(1); // array assigment with an empty block
571 newLine(pendingNewLines);
577 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
579 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
580 newLine(pendingNewLines);
583 if (((previousCompilableToken == TokenNameSEMICOLON) || (previousCompilableToken == TokenNameLBRACE)
584 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
585 && (token == TokenNameRBRACE)) {
586 indentationOffset = -1;
587 indentationLevel += popExclusiveUntilBlock();
589 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
591 currentLineIndentationLevel++;
593 currentLineIndentationLevel = indentationLevel + indentationOffset;
595 pendingSpace = false;
596 indentationOffset = 0;
599 newLinesInWhitespace = 0;
601 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
605 boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
607 // case TokenNameDOLLAR :
608 // dollarBraceCount++;
611 // case TokenNamefinally :
612 expectingOpenBrace = true;
613 pendingNewlineAfterParen = true;
614 indentationLevel += pushControlStatement(token);
617 case TokenNamedefault:
618 if (tokenBeforeColonCount == tokenBeforeColon.length) {
620 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
622 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
623 indentationLevel += pushControlStatement(TokenNamecase);
625 case TokenNameQUESTION:
626 if (tokenBeforeColonCount == tokenBeforeColon.length) {
628 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
630 tokenBeforeColon[tokenBeforeColonCount++] = token;
632 case TokenNameswitch:
634 case TokenNameforeach:
637 if (openParenthesisCount == openParenthesis.length) {
638 System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
640 openParenthesis[openParenthesisCount++] = 0;
641 expectingOpenBrace = true;
642 indentationLevel += pushControlStatement(token);
645 pendingNewlineAfterParen = true;
647 // several CATCH statements can be contiguous.
648 // a CATCH is encountered pop until first CATCH (if a CATCH
649 // follows a TRY it works the same way,
650 // as CATCH and TRY are the same token in the stack).
651 expectingOpenBrace = true;
652 indentationLevel += pushControlStatement(TokenNamecatch);
655 expectingOpenBrace = true;
656 indentationLevel += pushControlStatement(token);
661 case TokenNameLPAREN:
662 // if (previousToken == TokenNamesynchronized) {
663 // indentationLevel += pushControlStatement(previousToken);
665 // Put a space between the previous and current token if the
666 // previous token was not a keyword, open paren, logical
667 // compliment (eg: !), semi-colon, open brace, close brace,
669 if (previousCompilableToken != TokenNameLBRACKET && previousToken != TokenNameIdentifier && previousToken != 0
670 && previousToken != TokenNameNOT && previousToken != TokenNameLPAREN && previousToken != TokenNameTWIDDLE
671 && previousToken != TokenNameSEMICOLON && previousToken != TokenNameLBRACE && previousToken != TokenNameRBRACE
672 && previousToken != TokenNamesuper) {
673 // && previousToken != TokenNamethis) {
676 // If in a for/if/while statement, increase the parenthesis count
677 // for the current openParenthesisCount
678 // else increase the count for stand alone parenthesis.
679 if (openParenthesisCount > 0)
680 openParenthesis[openParenthesisCount - 1]++;
682 openParenthesis[0]++;
683 pendingSpace = false;
686 case TokenNameRPAREN:
687 // Decrease the parenthesis count
688 // if there is no more unclosed parenthesis,
689 // a new line and indent may be append (depending on the next
691 if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
692 openParenthesis[openParenthesisCount - 1]--;
693 if (openParenthesis[openParenthesisCount - 1] <= 0) {
694 pendingNewlineAfterParen = true;
695 inAssignment = false;
696 openParenthesisCount--;
699 openParenthesis[0]--;
701 pendingSpace = false;
703 case TokenNameLBRACE:
704 if (previousCompilableToken == TokenNameDOLLAR) {
707 if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
708 // if (previousCompilableToken == TokenNameRBRACKET) {
709 inArrayAssignment = true;
710 inAssignment = false;
712 if (inArrayAssignment) {
713 indentationLevel += pushBlock();
715 // Add new line and increase indentation level after open brace.
717 indentationLevel += pushBlock();
721 case TokenNameRBRACE:
722 if (dollarBraceCount > 0) {
726 if (previousCompilableToken == TokenNameRPAREN) {
727 pendingSpace = false;
729 if (inArrayAssignment) {
730 inArrayAssignment = false;
732 indentationLevel += popInclusiveUntilBlock();
735 indentationLevel += popInclusiveUntilBlock();
736 if (previousCompilableToken == TokenNameRPAREN) {
737 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
739 currentLineBuffer.append(options.lineSeparatorSequence);
740 increaseLineDelta(options.lineSeparatorSequence.length);
742 if (constructionsCount > 0) {
743 switch (constructions[constructionsCount - 1]) {
745 case TokenNameforeach:
746 //indentationLevel += popExclusiveUntilBlock();
748 case TokenNameswitch:
753 case TokenNamefinally:
756 // case TokenNamesynchronized :
757 clearNonBlockIndents = true;
764 case TokenNameLBRACKET:
766 pendingSpace = false;
768 case TokenNameRBRACKET:
769 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
770 // if there is no left bracket to close, the right bracket is
772 pendingSpace = false;
776 pendingSpace = false;
778 case TokenNameSEMICOLON:
779 // Do not generate line terminators in the definition of
780 // the for statement.
781 // if not in this case, jump a line and reduce indentation after
783 // if the block it closes belongs to a conditional statement (if,
785 if (openParenthesisCount <= 1) {
787 if (expectingOpenBrace) {
788 clearNonBlockIndents = true;
789 expectingOpenBrace = false;
792 inAssignment = false;
793 pendingSpace = false;
795 case TokenNamePLUS_PLUS:
796 case TokenNameMINUS_MINUS:
797 // Do not put a space between a post-increment/decrement
798 // and the identifier being modified.
799 if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET) {
800 pendingSpace = false;
804 // previously ADDITION
806 // Handle the unary operators plus and minus via a flag
807 if (!isLiteralToken(previousToken) && previousToken != TokenNameIdentifier && previousToken != TokenNameRPAREN
808 && previousToken != TokenNameRBRACKET) {
809 unarySignModifier = 1;
813 // In a switch/case statement, add a newline & indent
814 // when a colon is encountered.
815 if (tokenBeforeColonCount > 0) {
816 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
819 tokenBeforeColonCount--;
825 case Scanner.TokenNameCOMMENT_LINE:
828 currentLineIndentationLevel++;
830 break; // a line is always inserted after a one-line comment
831 case Scanner.TokenNameCOMMENT_PHPDOC:
832 case Scanner.TokenNameCOMMENT_BLOCK:
833 currentCommentOffset = getCurrentCommentOffset();
836 case Scanner.TokenNameWHITESPACE:
837 if (!phpTagAndWhitespace) {
838 // Count the number of line terminators in the whitespace so
839 // line spacing can be preserved near comments.
840 char[] source = scanner.source;
841 newLinesInWhitespace = 0;
842 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
843 if (source[i] == '\r') {
845 if (source[++i] == '\n') {
846 newLinesInWhitespace++;
848 newLinesInWhitespace++;
851 newLinesInWhitespace++;
853 } else if (source[i] == '\n') {
854 newLinesInWhitespace++;
857 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
860 // case TokenNameHTML :
861 // // Add the next token to the formatted source string.
862 // // outputCurrentToken(token);
863 // int startPosition = scanner.startPosition;
865 // for (int i = startPosition, max = scanner.currentPosition; i <
867 // char currentCharacter = scanner.source[i];
868 // updateMappedPositions(i);
869 // currentLineBuffer.append(currentCharacter);
873 if ((token == TokenNameIdentifier) || isLiteralToken(token) || token == TokenNamesuper) {
874 // || token == TokenNamethis) {
875 // Do not put a space between a unary operator
876 // (eg: ++, --, +, -) and the identifier being modified.
877 if (previousToken == TokenNamePLUS_PLUS || previousToken == TokenNameMINUS_MINUS
878 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
879 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
880 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
881 pendingSpace = false;
883 unarySignModifier = 0;
887 // Do not output whitespace tokens.
888 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
890 * Add pending space to the formatted source string. Do not output a space under the following circumstances: 1) this is
891 * the first pass 2) previous token is an open paren 3) previous token is a period 4) previous token is the logical
892 * compliment (eg: !) 5) previous token is the bitwise compliment (eg: ~) 6) previous token is the open bracket (eg: [) 7)
893 * in an assignment statement, if the previous token is an open brace or the current token is a close brace 8) previous
894 * token is a single line comment 9) current token is a '->'
896 if (token == TokenNameMINUS_GREATER && options.compactDereferencingMode)
897 pendingSpace = false;
899 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
900 if (pendingSpace && insertSpaceAfter(previousToken)
901 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
902 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
903 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
906 // Add the next token to the formatted source string.
907 outputCurrentToken(token);
908 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
910 currentLineBuffer.append(options.lineSeparatorSequence);
911 increaseLineDelta(options.lineSeparatorSequence.length);
915 // Whitespace tokens do not need to be remembered.
916 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
917 previousToken = token;
918 if (token != Scanner.TokenNameCOMMENT_BLOCK && token != Scanner.TokenNameCOMMENT_LINE
919 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
920 previousCompilableToken = token;
924 output(copyRemainingSource());
926 // dump the last token of the source in the formatted output.
927 } catch (InvalidInputException e) {
928 output(copyRemainingSource());
930 // dump the last token of the source in the formatted output.
935 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
937 * @return the formatted ouput.
939 public String formatSourceString(String sourceString) {
940 char[] sourceChars = sourceString.toCharArray();
941 formattedSource = new StringBuffer(sourceChars.length);
942 scanner.setSource(sourceChars);
944 return formattedSource.toString();
948 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
951 * the string to format
952 * @param indentationLevel
953 * the initial indentation level
954 * @return the formatted ouput.
956 public String format(String string, int indentationLevel) {
957 return format(string, indentationLevel, (int[]) null);
961 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The positions array
962 * is modified to contain the mapped positions.
965 * the string to format
966 * @param indentationLevel
967 * the initial indentation level
969 * the array of positions to map
970 * @return the formatted ouput.
972 public String format(String string, int indentationLevel, int[] positions) {
973 return this.format(string, indentationLevel, positions, null);
976 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
977 if (lineSeparator != null) {
978 this.options.setLineSeparator(lineSeparator);
980 if (positions != null) {
981 this.setPositionsToMap(positions);
982 this.setInitialIndentationLevel(indentationLevel);
983 String formattedString = this.formatSourceString(string);
984 int[] mappedPositions = this.getMappedPositions();
985 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
986 return formattedString;
988 this.setInitialIndentationLevel(indentationLevel);
989 return this.formatSourceString(string);
994 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The initial
995 * indentation level is 0.
998 * the string to format
999 * @return the formatted ouput.
1001 public String format(String string) {
1002 return this.format(string, 0, (int[]) null);
1006 * Formats a given source string, starting indenting it at a particular depth and using the given options
1008 * @deprecated backport 1.0 internal functionality
1010 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
1011 CodeFormatter formatter = new CodeFormatter(options);
1012 formatter.setInitialIndentationLevel(initialIndentationLevel);
1013 return formatter.formatSourceString(sourceString);
1017 * Returns the number of characters and tab char between the beginning of the line and the beginning of the comment.
1019 private int getCurrentCommentOffset() {
1020 int linePtr = scanner.linePtr;
1021 // if there is no beginning of line, return 0.
1025 int beginningOfLine = scanner.lineEnds[linePtr];
1026 int currentStartPosition = scanner.startPosition;
1027 char[] source = scanner.source;
1028 // find the position of the beginning of the line containing the comment
1029 while (beginningOfLine > currentStartPosition) {
1031 beginningOfLine = scanner.lineEnds[--linePtr];
1033 beginningOfLine = 0;
1037 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1038 char currentCharacter = source[i];
1039 switch (currentCharacter) {
1041 offset += options.tabSize;
1057 * Returns an array of descriptions for the configurable options. The descriptions may be changed and passed back to a different
1060 * @deprecated backport 1.0 internal functionality
1062 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1063 String componentName = CodeFormatter.class.getName();
1064 FormatterOptions options = new FormatterOptions();
1065 return new ConfigurableOption[] {
1066 new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1068 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1),
1070 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1),
1072 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1073 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1074 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1),
1076 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),
1078 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1),
1080 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1),
1082 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1087 * Returns the array of mapped positions. Returns null is no positions have been set.
1090 * @deprecated There is no need to retrieve the mapped positions anymore.
1092 public int[] getMappedPositions() {
1093 return mappedPositions;
1097 * Returns the priority of the token given as argument <br>
1098 * The most prioritary the token is, the smallest the return value is.
1100 * @return the priority of <code>token</code>
1102 * the token of which the priority is requested
1104 private static int getTokenPriority(int token) {
1106 case TokenNameextends:
1107 // case TokenNameimplements :
1108 // case TokenNamethrows :
1110 case TokenNameSEMICOLON:
1113 case TokenNameCOMMA:
1116 case TokenNameEQUAL:
1119 case TokenNameAND_AND:
1121 case TokenNameOR_OR:
1124 case TokenNameQUESTION:
1126 case TokenNameCOLON:
1128 return 50; // it's better cutting on ?: than on ;
1129 case TokenNameEQUAL_EQUAL:
1131 case TokenNameEQUAL_EQUAL_EQUAL:
1133 case TokenNameNOT_EQUAL:
1135 case TokenNameNOT_EQUAL_EQUAL:
1140 case TokenNameLESS_EQUAL:
1142 case TokenNameGREATER:
1144 case TokenNameGREATER_EQUAL:
1146 // case TokenNameinstanceof : // instanceof
1150 case TokenNameMINUS:
1153 case TokenNameMULTIPLY:
1155 case TokenNameDIVIDE:
1157 case TokenNameREMAINDER:
1160 case TokenNameLEFT_SHIFT:
1162 case TokenNameRIGHT_SHIFT:
1164 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1173 case TokenNameMULTIPLY_EQUAL:
1175 case TokenNameDIVIDE_EQUAL:
1177 case TokenNameREMAINDER_EQUAL:
1179 case TokenNamePLUS_EQUAL:
1181 case TokenNameMINUS_EQUAL:
1183 case TokenNameLEFT_SHIFT_EQUAL:
1185 case TokenNameRIGHT_SHIFT_EQUAL:
1187 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1188 case TokenNameAND_EQUAL:
1190 case TokenNameXOR_EQUAL:
1192 case TokenNameOR_EQUAL:
1194 case TokenNameDOT_EQUAL:
1201 return Integer.MAX_VALUE;
1206 * Handles the exception raised when an invalid token is encountered. Returns true if the exception has been handled, false
1209 private boolean handleInvalidToken(Exception e) {
1210 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT) || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1211 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1217 private final void increaseGlobalDelta(int offset) {
1218 globalDelta += offset;
1221 private final void increaseLineDelta(int offset) {
1222 lineDelta += offset;
1225 private final void increaseSplitDelta(int offset) {
1226 splitDelta += offset;
1230 * Returns true if a space has to be inserted after <code>operator</code> false otherwise.
1232 private boolean insertSpaceAfter(int token) {
1234 case TokenNameLPAREN:
1236 case TokenNameTWIDDLE:
1240 case TokenNameWHITESPACE:
1241 case TokenNameLBRACKET:
1242 case TokenNameDOLLAR:
1243 case Scanner.TokenNameCOMMENT_LINE:
1251 * Returns true if a space has to be inserted before <code>operator</code> false otherwise. <br>
1252 * Cannot be static as it uses the code formatter options (to know if the compact assignment mode is on).
1254 private boolean insertSpaceBefore(int token) {
1256 case TokenNameEQUAL:
1257 return (!options.compactAssignmentMode);
1263 private static boolean isComment(int token) {
1264 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE
1265 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1269 private static boolean isLiteralToken(int token) {
1270 boolean result = token == TokenNameIntegerLiteral
1271 // || token == TokenNameLongLiteral
1272 // || token == TokenNameFloatingPointLiteral
1273 || token == TokenNameDoubleLiteral
1274 // || token == TokenNameCharacterLiteral
1275 || token == TokenNameStringDoubleQuote;
1280 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>, it is split and the result is dumped in
1281 * <code>formattedSource</code>
1283 * @param newLineCount
1284 * the number of new lines to append
1286 private void newLine(int newLineCount) {
1287 // format current line
1289 beginningOfLineIndex = formattedSource.length();
1290 String currentLine = currentLineBuffer.toString();
1291 if (containsOpenCloseBraces) {
1292 containsOpenCloseBraces = false;
1293 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1294 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1296 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1298 // dump line break(s)
1299 for (int i = 0; i < newLineCount; i++) {
1300 formattedSource.append(options.lineSeparatorSequence);
1301 increaseSplitDelta(options.lineSeparatorSequence.length);
1303 // reset formatter for next line
1304 int currentLength = currentLine.length();
1305 currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1306 increaseGlobalDelta(splitDelta);
1307 increaseGlobalDelta(lineDelta);
1309 currentLineIndentationLevel = initialIndentationLevel;
1312 private String operatorString(int operator) {
1314 case TokenNameextends:
1315 return "extends"; //$NON-NLS-1$
1316 // case TokenNameimplements :
1317 // return "implements"; //$NON-NLS-1$
1319 // case TokenNamethrows :
1320 // return "throws"; //$NON-NLS-1$
1321 case TokenNameSEMICOLON:
1323 return ";"; //$NON-NLS-1$
1324 case TokenNameCOMMA:
1326 return ","; //$NON-NLS-1$
1327 case TokenNameEQUAL:
1329 return "="; //$NON-NLS-1$
1330 case TokenNameAND_AND:
1332 return "&&"; //$NON-NLS-1$
1333 case TokenNameOR_OR:
1335 return "||"; //$NON-NLS-1$
1336 case TokenNameQUESTION:
1338 return "?"; //$NON-NLS-1$
1339 case TokenNameCOLON:
1341 return ":"; //$NON-NLS-1$
1342 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1344 return "::"; //$NON-NLS-1$
1345 case TokenNameEQUAL_EQUAL:
1346 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1347 return "=="; //$NON-NLS-1$
1348 case TokenNameEQUAL_EQUAL_EQUAL:
1349 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1350 return "==="; //$NON-NLS-1$
1351 case TokenNameEQUAL_GREATER:
1353 return "=>"; //$NON-NLS-1$
1354 case TokenNameNOT_EQUAL:
1355 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1356 return "!="; //$NON-NLS-1$
1357 case TokenNameNOT_EQUAL_EQUAL:
1358 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1359 return "!=="; //$NON-NLS-1$
1362 return "<"; //$NON-NLS-1$
1363 case TokenNameLESS_EQUAL:
1365 return "<="; //$NON-NLS-1$
1366 case TokenNameGREATER:
1368 return ">"; //$NON-NLS-1$
1369 case TokenNameGREATER_EQUAL:
1371 return ">="; //$NON-NLS-1$
1372 // case TokenNameinstanceof : // instanceof
1373 // return "instanceof"; //$NON-NLS-1$
1375 // + (15.17, 15.17.2)
1376 return "+"; //$NON-NLS-1$
1377 case TokenNameMINUS:
1379 return "-"; //$NON-NLS-1$
1380 case TokenNameMULTIPLY:
1382 return "*"; //$NON-NLS-1$
1383 case TokenNameDIVIDE:
1385 return "/"; //$NON-NLS-1$
1386 case TokenNameREMAINDER:
1388 return "%"; //$NON-NLS-1$
1389 case TokenNameLEFT_SHIFT:
1391 return "<<"; //$NON-NLS-1$
1392 case TokenNameRIGHT_SHIFT:
1394 return ">>"; //$NON-NLS-1$
1395 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1396 // return ">>>"; //$NON-NLS-1$
1398 // & (15.21, 15.21.1, 15.21.2)
1399 return "&"; //$NON-NLS-1$
1401 // | (15.21, 15.21.1, 15.21.2)
1402 return "|"; //$NON-NLS-1$
1404 // ^ (15.21, 15.21.1, 15.21.2)
1405 return "^"; //$NON-NLS-1$
1406 case TokenNameMULTIPLY_EQUAL:
1408 return "*="; //$NON-NLS-1$
1409 case TokenNameDIVIDE_EQUAL:
1411 return "/="; //$NON-NLS-1$
1412 case TokenNameREMAINDER_EQUAL:
1414 return "%="; //$NON-NLS-1$
1415 case TokenNamePLUS_EQUAL:
1417 return "+="; //$NON-NLS-1$
1418 case TokenNameMINUS_EQUAL:
1420 return "-="; //$NON-NLS-1$
1421 case TokenNameMINUS_GREATER:
1423 return "->"; //$NON-NLS-1$
1424 case TokenNameLEFT_SHIFT_EQUAL:
1426 return "<<="; //$NON-NLS-1$
1427 case TokenNameRIGHT_SHIFT_EQUAL:
1429 return ">>="; //$NON-NLS-1$
1430 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1431 // return ">>>="; //$NON-NLS-1$
1432 case TokenNameAND_EQUAL:
1434 return "&="; //$NON-NLS-1$
1435 case TokenNameXOR_EQUAL:
1437 return "^="; //$NON-NLS-1$
1438 case TokenNameOR_EQUAL:
1440 return "|="; //$NON-NLS-1$
1441 case TokenNameDOT_EQUAL:
1443 return ".="; //$NON-NLS-1$
1446 return "."; //$NON-NLS-1$
1448 return ""; //$NON-NLS-1$
1453 * Appends <code>stringToOutput</code> to the formatted output. <br>
1454 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1456 private void output(String stringToOutput) {
1457 char currentCharacter;
1458 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1459 currentCharacter = stringToOutput.charAt(i);
1460 if (currentCharacter != '\t') {
1461 currentLineBuffer.append(currentCharacter);
1466 private void outputCurrentTokenWithoutIndent(int token, int newLineCount) {
1467 newLine(newLineCount);
1468 formattedSource.append(scanner.source, scanner.startPosition, scanner.currentPosition - scanner.startPosition);
1472 * Appends <code>token</code> to the formatted output. <br>
1473 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1475 private void outputCurrentToken(int token) {
1476 char[] source = scanner.source;
1477 int startPosition = scanner.startPosition;
1479 case Scanner.TokenNameCOMMENT_PHPDOC:
1480 case Scanner.TokenNameCOMMENT_BLOCK:
1481 case Scanner.TokenNameCOMMENT_LINE:
1482 boolean endOfLine = false;
1483 int currentCommentOffset = getCurrentCommentOffset();
1484 int beginningOfLineSpaces = 0;
1486 currentCommentOffset = getCurrentCommentOffset();
1487 beginningOfLineSpaces = 0;
1488 boolean pendingCarriageReturn = false;
1489 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1490 char currentCharacter = source[i];
1491 updateMappedPositions(i);
1492 switch (currentCharacter) {
1494 pendingCarriageReturn = true;
1498 if (pendingCarriageReturn) {
1499 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1501 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1503 pendingCarriageReturn = false;
1504 currentLineBuffer.append(options.lineSeparatorSequence);
1505 beginningOfLineSpaces = 0;
1509 if (pendingCarriageReturn) {
1510 pendingCarriageReturn = false;
1511 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1512 currentLineBuffer.append(options.lineSeparatorSequence);
1513 beginningOfLineSpaces = 0;
1517 // we remove a maximum of currentCommentOffset characters (tabs
1518 // are converted to space numbers).
1519 beginningOfLineSpaces += options.tabSize;
1520 if (beginningOfLineSpaces > currentCommentOffset) {
1521 currentLineBuffer.append(currentCharacter);
1523 increaseGlobalDelta(-1);
1526 currentLineBuffer.append(currentCharacter);
1530 if (pendingCarriageReturn) {
1531 pendingCarriageReturn = false;
1532 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1533 currentLineBuffer.append(options.lineSeparatorSequence);
1534 beginningOfLineSpaces = 0;
1538 // we remove a maximum of currentCommentOffset characters (tabs
1539 // are converted to space numbers).
1540 beginningOfLineSpaces++;
1541 if (beginningOfLineSpaces > currentCommentOffset) {
1542 currentLineBuffer.append(currentCharacter);
1544 increaseGlobalDelta(-1);
1547 currentLineBuffer.append(currentCharacter);
1551 if (pendingCarriageReturn) {
1552 pendingCarriageReturn = false;
1553 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1554 currentLineBuffer.append(options.lineSeparatorSequence);
1555 beginningOfLineSpaces = 0;
1558 beginningOfLineSpaces = 0;
1559 currentLineBuffer.append(currentCharacter);
1564 updateMappedPositions(scanner.currentPosition - 1);
1565 multipleLineCommentCounter++;
1568 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1569 char currentCharacter = source[i];
1570 updateMappedPositions(i);
1571 currentLineBuffer.append(currentCharacter);
1577 * Outputs <code>currentString</code>:<br>
1579 * <li>If its length is < maxLineLength, output
1580 * <li>Otherwise it is split.
1583 * @param currentString
1585 * @param preIndented
1586 * whether the string to output was pre-indented
1588 * number of indentation to put in front of <code>currentString</code>
1590 * value of the operator belonging to <code>currentString</code>.
1592 private void outputLine(String currentString, boolean preIndented, int depth, int operator, int substringIndex,
1593 int[] startSubstringIndexes, int offsetInGlobalLine) {
1594 boolean emptyFirstSubString = false;
1595 String operatorString = operatorString(operator);
1596 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1597 boolean placeOperatorAhead = !placeOperatorBehind;
1598 // dump prefix operator?
1599 if (placeOperatorAhead) {
1604 if (operator != 0) {
1605 if (insertSpaceBefore(operator)) {
1606 formattedSource.append(' ');
1607 increaseSplitDelta(1);
1609 formattedSource.append(operatorString);
1610 increaseSplitDelta(operatorString.length());
1611 if (insertSpaceAfter(operator) && operator != TokenNameimplements && operator != TokenNameextends) {
1612 // && operator != TokenNamethrows) {
1613 formattedSource.append(' ');
1614 increaseSplitDelta(1);
1618 SplitLine splitLine = null;
1619 if (options.maxLineLength == 0 || getLength(currentString, depth) < options.maxLineLength
1620 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1621 // depending on the type of operator, outputs new line before of after
1623 // indent before postfix operator
1624 // indent also when the line cannot be split
1625 if (operator == TokenNameextends || operator == TokenNameimplements) {
1626 // || operator == TokenNamethrows) {
1627 formattedSource.append(' ');
1628 increaseSplitDelta(1);
1630 if (placeOperatorBehind) {
1635 int max = currentString.length();
1636 if (multipleLineCommentCounter != 0) {
1638 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1639 String line = reader.readLine();
1640 while (line != null) {
1641 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + line.length()
1642 + options.lineSeparatorSequence.length);
1643 formattedSource.append(line);
1644 beginningOfLineIndex = beginningOfLineIndex + line.length();
1645 if ((line = reader.readLine()) != null) {
1646 formattedSource.append(options.lineSeparatorSequence);
1647 beginningOfLineIndex += options.lineSeparatorSequence.length;
1648 dumpTab(currentLineIndentationLevel);
1652 } catch (IOException e) {
1653 e.printStackTrace();
1656 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1657 for (int i = 0; i < max; i++) {
1658 char currentChar = currentString.charAt(i);
1659 switch (currentChar) {
1664 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1665 // split with a comment inside a condition
1666 // a substring cannot end with a lineSeparatorSequence,
1667 // except if it has been added by format() after a one-line
1669 formattedSource.append(options.lineSeparatorSequence);
1670 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1675 formattedSource.append(currentChar);
1679 // update positions inside the mappedPositions table
1680 if (substringIndex != -1) {
1681 if (multipleLineCommentCounter == 0) {
1682 int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1683 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1685 // compute the splitDelta resulting with the operator and blank removal
1686 if (substringIndex + 1 != startSubstringIndexes.length) {
1687 increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1690 // dump postfix operator?
1691 if (placeOperatorBehind) {
1692 if (insertSpaceBefore(operator)) {
1693 formattedSource.append(' ');
1694 if (operator != 0) {
1695 increaseSplitDelta(1);
1698 formattedSource.append(operatorString);
1699 if (operator != 0) {
1700 increaseSplitDelta(operatorString.length());
1705 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1706 // extends has to stand alone on a line when currentString has been split.
1707 if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1708 // || operator == TokenNameimplements
1709 // || operator == TokenNamethrows)) {
1710 formattedSource.append(options.lineSeparatorSequence);
1711 increaseSplitDelta(options.lineSeparatorSequence.length);
1714 if (operator == TokenNameextends) {
1715 // || operator == TokenNameimplements
1716 // || operator == TokenNamethrows) {
1717 formattedSource.append(' ');
1718 increaseSplitDelta(1);
1721 // perform actual splitting
1722 String result[] = splitLine.substrings;
1723 int[] splitOperators = splitLine.operators;
1724 if (result[0].length() == 0) {
1725 // when the substring 0 is null, the substring 1 is correctly indented.
1727 emptyFirstSubString = true;
1729 // the operator going in front of the result[0] string is the operator
1731 for (int i = 0, max = result.length; i < max; i++) {
1732 // the new depth is the current one if this is the first substring,
1733 // the current one + 1 otherwise.
1734 // if the substring is a comment, use the current indentation Level
1735 // instead of the depth
1736 // (-1 because the ouputline increases depth).
1737 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1739 String currentResult = result[i];
1740 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1741 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1742 || currentResult.startsWith("//")) //$NON-NLS-1$
1743 ? indentationLevel - 1 : depth;
1744 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1745 i == 0 ? newDepth : newDepth + 1, splitOperators[i], i, splitLine.startSubstringsIndexes, currentString
1746 .indexOf(currentResult));
1748 formattedSource.append(options.lineSeparatorSequence);
1749 increaseSplitDelta(options.lineSeparatorSequence.length);
1753 if (result.length == splitOperators.length - 1) {
1754 int lastOperator = splitOperators[result.length];
1755 String lastOperatorString = operatorString(lastOperator);
1756 formattedSource.append(options.lineSeparatorSequence);
1757 increaseSplitDelta(options.lineSeparatorSequence.length);
1758 if (breakLineBeforeOperator(lastOperator)) {
1760 if (lastOperator != 0) {
1761 if (insertSpaceBefore(lastOperator)) {
1762 formattedSource.append(' ');
1763 increaseSplitDelta(1);
1765 formattedSource.append(lastOperatorString);
1766 increaseSplitDelta(lastOperatorString.length());
1767 if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements && lastOperator != TokenNameextends) {
1768 // && lastOperator != TokenNamethrows) {
1769 formattedSource.append(' ');
1770 increaseSplitDelta(1);
1775 if (placeOperatorBehind) {
1776 if (insertSpaceBefore(operator)) {
1777 formattedSource.append(' ');
1778 increaseSplitDelta(1);
1780 formattedSource.append(operatorString);
1781 //increaseSplitDelta(operatorString.length());
1786 * Pops the top statement of the stack if it is <code>token</code>
1788 private int pop(int token) {
1790 if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1792 constructionsCount--;
1798 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1800 private int popBlock() {
1802 if ((constructionsCount > 0)
1803 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1804 if (constructions[constructionsCount - 1] == BLOCK)
1806 constructionsCount--;
1812 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1813 * Does not remove <code>token</code> from the stack.
1816 * the token to be left as the top of the stack
1818 private int popExclusiveUntil(int token) {
1820 int startCount = constructionsCount;
1821 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1822 if (constructions[i] != NONINDENT_BLOCK)
1824 constructionsCount--;
1830 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1831 * Does not remove it from the stack.
1833 private int popExclusiveUntilBlock() {
1834 int startCount = constructionsCount;
1836 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1837 constructionsCount--;
1844 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
1845 * <code>CASE</code>.<br>
1846 * Does not remove it from the stack.
1848 private int popExclusiveUntilBlockOrCase() {
1849 int startCount = constructionsCount;
1851 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK
1852 && constructions[i] != TokenNamecase; i--) {
1853 constructionsCount--;
1860 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1861 * Removes <code>token</code> from the stack too.
1864 * the token to remove from the stack
1866 private int popInclusiveUntil(int token) {
1867 int startCount = constructionsCount;
1869 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1870 if (constructions[i] != NONINDENT_BLOCK)
1872 constructionsCount--;
1874 if (constructionsCount > 0) {
1875 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1877 constructionsCount--;
1883 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1884 * Does not remove it from the stack.
1886 private int popInclusiveUntilBlock() {
1887 int startCount = constructionsCount;
1889 for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1891 constructionsCount--;
1893 if (constructionsCount > 0) {
1894 if (constructions[constructionsCount - 1] == BLOCK)
1896 constructionsCount--;
1902 * Pushes a block in the stack. <br>
1903 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>, pushes
1904 * <code>NONINDENT_BLOCK</code> otherwise. Creates a new bigger array if the current one is full.
1906 private int pushBlock() {
1908 if (constructionsCount == constructions.length)
1909 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1910 if ((constructionsCount == 0) || (constructions[constructionsCount - 1] == BLOCK)
1911 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK) || (constructions[constructionsCount - 1] == TokenNamecase)) {
1913 constructions[constructionsCount++] = BLOCK;
1915 constructions[constructionsCount++] = NONINDENT_BLOCK;
1921 * Pushes <code>token</code>.<br>
1922 * Creates a new bigger array if the current one is full.
1924 private int pushControlStatement(int token) {
1925 if (constructionsCount == constructions.length)
1926 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1927 constructions[constructionsCount++] = token;
1931 private static boolean separateFirstArgumentOn(int currentToken) {
1932 //return (currentToken == TokenNameCOMMA || currentToken ==
1933 // TokenNameSEMICOLON);
1934 return currentToken != TokenNameif && currentToken != TokenNameLPAREN && currentToken != TokenNameNOT
1935 && currentToken != TokenNamewhile && currentToken != TokenNamefor && currentToken != TokenNameforeach
1936 && currentToken != TokenNameswitch;
1940 * Set the positions to map. The mapped positions should be retrieved using the getMappedPositions() method.
1944 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1946 * @see #getMappedPositions()
1948 public void setPositionsToMap(int[] positions) {
1949 positionsToMap = positions;
1952 mappedPositions = new int[positions.length];
1956 * Appends a space character to the current line buffer.
1958 private void space() {
1959 currentLineBuffer.append(' ');
1960 increaseLineDelta(1);
1964 * Splits <code>stringToSplit</code> on the top level token <br>
1965 * If there are several identical token at the same level, the string is cut into many pieces.
1967 * @return an object containing the operator and all the substrings or null if the string cannot be split
1969 public SplitLine split(String stringToSplit) {
1970 return split(stringToSplit, 0);
1974 * Splits <code>stringToSplit</code> on the top level token <br>
1975 * If there are several identical token at the same level, the string is cut into many pieces.
1977 * @return an object containing the operator and all the substrings or null if the string cannot be split
1979 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1981 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1983 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1986 // split doesn't work correct for PHP
1989 // int currentToken = 0;
1990 // int splitTokenType = 0;
1991 // int splitTokenDepth = Integer.MAX_VALUE;
1992 // int splitTokenPriority = Integer.MAX_VALUE;
1993 // int[] substringsStartPositions = new int[10];
1994 // // contains the start position of substrings
1995 // int[] substringsEndPositions = new int[10];
1996 // // contains the start position of substrings
1997 // int substringsCount = 1; // index in the substringsStartPosition array
1998 // int[] splitOperators = new int[10];
1999 // // contains the start position of substrings
2000 // int splitOperatorsCount = 0; // index in the substringsStartPosition array
2001 // int[] openParenthesisPosition = new int[10];
2002 // int openParenthesisPositionCount = 0;
2003 // int position = 0;
2004 // int lastOpenParenthesisPosition = -1;
2005 // // used to remember the position of the 1st open parenthesis
2006 // // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2007 // // setup the scanner with a new source
2008 // int lastCommentStartPosition = -1;
2009 // // to remember the start position of the last comment
2010 // int firstTokenOnLine = -1;
2011 // // to remember the first token of the line
2012 // int previousToken = -1;
2013 // // to remember the previous token.
2014 // splitScanner.setSource(stringToSplit.toCharArray());
2016 // // start the loop
2018 // // takes the next token
2020 // if (currentToken != Scanner.TokenNameWHITESPACE)
2021 // previousToken = currentToken;
2022 // currentToken = splitScanner.getNextToken();
2023 // if (Scanner.DEBUG) {
2024 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2025 // int currentStartPosition = splitScanner
2026 // .getCurrentTokenStartPosition();
2027 // System.out.print(currentStartPosition + "," + currentEndPosition
2029 // System.out.println(scanner.toStringAction(currentToken));
2031 // } catch (InvalidInputException e) {
2032 // if (!handleInvalidToken(e))
2034 // currentToken = 0;
2035 // // this value is not modify when an exception is raised.
2037 // if (currentToken == TokenNameEOF)
2039 // if (firstTokenOnLine == -1) {
2040 // firstTokenOnLine = currentToken;
2042 // switch (currentToken) {
2043 // case TokenNameRBRACE :
2044 // case TokenNameRPAREN :
2045 // if (openParenthesisPositionCount > 0) {
2046 // if (openParenthesisPositionCount == 1
2047 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2048 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2049 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2050 // || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2051 // splitTokenType = 0;
2052 // splitTokenDepth = openParenthesisPositionCount;
2053 // splitTokenPriority = Integer.MAX_VALUE;
2054 // substringsStartPositions[0] = 0;
2055 // // better token means the whole line until now is the first
2057 // substringsCount = 1; // resets the count of substrings
2058 // substringsEndPositions[0] = openParenthesisPosition[0];
2059 // // substring ends on operator start
2060 // position = openParenthesisPosition[0];
2061 // // the string mustn't be cut before the closing parenthesis but
2062 // // after the opening one.
2063 // splitOperatorsCount = 1; // resets the count of split operators
2064 // splitOperators[0] = 0;
2066 // openParenthesisPositionCount--;
2069 // case TokenNameLBRACE :
2070 // case TokenNameLPAREN :
2071 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2074 // openParenthesisPosition,
2076 // (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2077 // 0, openParenthesisPositionCount);
2079 // openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2080 // if (currentToken == TokenNameLPAREN
2081 // && previousToken == TokenNameRPAREN) {
2082 // openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2085 // case TokenNameSEMICOLON :
2087 // case TokenNameCOMMA :
2089 // case TokenNameEQUAL :
2091 // if (openParenthesisPositionCount < splitTokenDepth
2092 // || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2093 // // the current token is better than the one we currently have
2094 // // (in level or in priority if same level)
2095 // // reset the substringsCount
2096 // splitTokenDepth = openParenthesisPositionCount;
2097 // splitTokenType = currentToken;
2098 // splitTokenPriority = getTokenPriority(currentToken);
2099 // substringsStartPositions[0] = 0;
2100 // // better token means the whole line until now is the first
2102 // if (separateFirstArgumentOn(firstTokenOnLine)
2103 // && openParenthesisPositionCount > 0) {
2104 // substringsCount = 2; // resets the count of substrings
2105 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2106 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2107 // substringsEndPositions[1] = splitScanner.startPosition;
2108 // splitOperatorsCount = 2; // resets the count of split operators
2109 // splitOperators[0] = 0;
2110 // splitOperators[1] = currentToken;
2111 // position = splitScanner.currentPosition;
2112 // // next substring will start from operator end
2114 // substringsCount = 1; // resets the count of substrings
2115 // substringsEndPositions[0] = splitScanner.startPosition;
2116 // // substring ends on operator start
2117 // position = splitScanner.currentPosition;
2118 // // next substring will start from operator end
2119 // splitOperatorsCount = 1; // resets the count of split operators
2120 // splitOperators[0] = currentToken;
2123 // if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2124 // && splitTokenType != TokenNameEQUAL
2125 // && currentToken != TokenNameEQUAL) {
2126 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2128 // // take only the 1st = into account.
2129 // // if another token with the same priority is found,
2130 // // push the start position of the substring and
2131 // // push the token into the stack.
2132 // // create a new array object if the current one is full.
2133 // if (substringsCount == substringsStartPositions.length) {
2136 // substringsStartPositions,
2138 // (substringsStartPositions = new int[substringsCount * 2]),
2139 // 0, substringsCount);
2140 // System.arraycopy(substringsEndPositions, 0,
2141 // (substringsEndPositions = new int[substringsCount * 2]),
2142 // 0, substringsCount);
2144 // if (splitOperatorsCount == splitOperators.length) {
2145 // System.arraycopy(splitOperators, 0,
2146 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2147 // splitOperatorsCount);
2149 // substringsStartPositions[substringsCount] = position;
2150 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2151 // // substring ends on operator start
2152 // position = splitScanner.currentPosition;
2153 // // next substring will start from operator end
2154 // splitOperators[splitOperatorsCount++] = currentToken;
2158 // case TokenNameCOLON :
2160 // // see 1FK7C5R, we only split on a colon, when it is associated
2161 // // with a question-mark.
2162 // // indeed it might appear also behind a case statement, and we do
2163 // // not to break at this point.
2164 // if ((splitOperatorsCount == 0)
2165 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2168 // case TokenNameextends :
2169 // case TokenNameimplements :
2170 // //case TokenNamethrows :
2171 // case TokenNameDOT :
2173 // case TokenNameMULTIPLY :
2175 // case TokenNameDIVIDE :
2177 // case TokenNameREMAINDER :
2179 // case TokenNamePLUS :
2180 // // + (15.17, 15.17.2)
2181 // case TokenNameMINUS :
2183 // case TokenNameLEFT_SHIFT :
2185 // case TokenNameRIGHT_SHIFT :
2187 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2188 // case TokenNameLESS :
2190 // case TokenNameLESS_EQUAL :
2192 // case TokenNameGREATER :
2194 // case TokenNameGREATER_EQUAL :
2196 // // case TokenNameinstanceof : // instanceof
2197 // case TokenNameEQUAL_EQUAL :
2198 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2199 // case TokenNameEQUAL_EQUAL_EQUAL :
2200 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2201 // case TokenNameNOT_EQUAL :
2202 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2203 // case TokenNameNOT_EQUAL_EQUAL :
2204 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2205 // case TokenNameAND :
2206 // // & (15.21, 15.21.1, 15.21.2)
2207 // case TokenNameOR :
2208 // // | (15.21, 15.21.1, 15.21.2)
2209 // case TokenNameXOR :
2210 // // ^ (15.21, 15.21.1, 15.21.2)
2211 // case TokenNameAND_AND :
2213 // case TokenNameOR_OR :
2215 // case TokenNameQUESTION :
2217 // case TokenNameMULTIPLY_EQUAL :
2219 // case TokenNameDIVIDE_EQUAL :
2221 // case TokenNameREMAINDER_EQUAL :
2223 // case TokenNamePLUS_EQUAL :
2225 // case TokenNameMINUS_EQUAL :
2227 // case TokenNameLEFT_SHIFT_EQUAL :
2229 // case TokenNameRIGHT_SHIFT_EQUAL :
2231 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2232 // case TokenNameAND_EQUAL :
2234 // case TokenNameXOR_EQUAL :
2236 // case TokenNameOR_EQUAL :
2238 // if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority
2239 // > getTokenPriority(currentToken)))
2240 // && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2241 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2242 // // the current token is better than the one we currently have
2243 // // (in level or in priority if same level)
2244 // // reset the substringsCount
2245 // splitTokenDepth = openParenthesisPositionCount;
2246 // splitTokenType = currentToken;
2247 // splitTokenPriority = getTokenPriority(currentToken);
2248 // substringsStartPositions[0] = 0;
2249 // // better token means the whole line until now is the first
2251 // if (separateFirstArgumentOn(firstTokenOnLine)
2252 // && openParenthesisPositionCount > 0) {
2253 // substringsCount = 2; // resets the count of substrings
2254 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2255 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2256 // substringsEndPositions[1] = splitScanner.startPosition;
2257 // splitOperatorsCount = 3; // resets the count of split operators
2258 // splitOperators[0] = 0;
2259 // splitOperators[1] = 0;
2260 // splitOperators[2] = currentToken;
2261 // position = splitScanner.currentPosition;
2262 // // next substring will start from operator end
2264 // substringsCount = 1; // resets the count of substrings
2265 // substringsEndPositions[0] = splitScanner.startPosition;
2266 // // substring ends on operator start
2267 // position = splitScanner.currentPosition;
2268 // // next substring will start from operator end
2269 // splitOperatorsCount = 2; // resets the count of split operators
2270 // splitOperators[0] = 0;
2271 // // nothing for first operand since operator will be inserted in
2272 // // front of the second operand
2273 // splitOperators[1] = currentToken;
2276 // if (openParenthesisPositionCount == splitTokenDepth
2277 // && splitTokenPriority == getTokenPriority(currentToken)) {
2278 // // if another token with the same priority is found,
2279 // // push the start position of the substring and
2280 // // push the token into the stack.
2281 // // create a new array object if the current one is full.
2282 // if (substringsCount == substringsStartPositions.length) {
2285 // substringsStartPositions,
2287 // (substringsStartPositions = new int[substringsCount * 2]),
2288 // 0, substringsCount);
2289 // System.arraycopy(substringsEndPositions, 0,
2290 // (substringsEndPositions = new int[substringsCount * 2]),
2291 // 0, substringsCount);
2293 // if (splitOperatorsCount == splitOperators.length) {
2294 // System.arraycopy(splitOperators, 0,
2295 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2296 // splitOperatorsCount);
2298 // substringsStartPositions[substringsCount] = position;
2299 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2300 // // substring ends on operator start
2301 // position = splitScanner.currentPosition;
2302 // // next substring will start from operator end
2303 // splitOperators[splitOperatorsCount++] = currentToken;
2309 // if (isComment(currentToken)) {
2310 // lastCommentStartPosition = splitScanner.startPosition;
2312 // lastCommentStartPosition = -1;
2315 // } catch (InvalidInputException e) {
2318 // // if the string cannot be split, return null.
2319 // if (splitOperatorsCount == 0)
2321 // // ## SPECIAL CASES BEGIN
2322 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2323 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2324 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2325 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) ||
2326 // (separateFirstArgumentOn(firstTokenOnLine)
2327 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2328 // && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2329 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2330 // // not be broken on two lines
2331 // // only one split on a top level .
2332 // // or more than one split on . and substring before open parenthesis fits
2334 // // or split inside parenthesis and first token is not a for/while/if
2335 // SplitLine sl = split(
2336 // stringToSplit.substring(lastOpenParenthesisPosition),
2337 // lastOpenParenthesisPosition);
2338 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2339 // // trim() is used to remove the extra blanks at the end of the
2340 // // substring. See PR 1FGYPI1
2341 // return new SplitLine(new int[]{0, 0}, new String[]{
2342 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2343 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2344 // offsetInGlobalLine,
2345 // lastOpenParenthesisPosition + offsetInGlobalLine});
2347 // // right substring can be split and is split on comma
2348 // // copy substrings and operators
2349 // // except if the 1st string is empty.
2350 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2351 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2352 // String[] result = new String[subStringsLength];
2353 // int[] startIndexes = new int[subStringsLength];
2354 // int operatorsLength = sl.operators.length + 1 - startIndex;
2355 // int[] operators = new int[operatorsLength];
2356 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2357 // operators[0] = 0;
2358 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2359 // 1, subStringsLength - 1);
2360 // for (int i = subStringsLength - 1; i >= 0; i--) {
2361 // startIndexes[i] += offsetInGlobalLine;
2363 // System.arraycopy(sl.substrings, startIndex, result, 1,
2364 // subStringsLength - 1);
2365 // System.arraycopy(sl.operators, startIndex, operators, 1,
2366 // operatorsLength - 1);
2367 // return new SplitLine(operators, result, startIndexes);
2370 // // if the last token is a comment and the substring before the comment fits
2372 // // split before the comment and return the result.
2373 // if (lastCommentStartPosition > -1
2374 // && lastCommentStartPosition < options.maxLineLength
2375 // && splitTokenPriority > 50) {
2376 // int end = lastCommentStartPosition;
2377 // int start = lastCommentStartPosition;
2378 // if (stringToSplit.charAt(end - 1) == ' ') {
2381 // if (start != end && stringToSplit.charAt(start) == ' ') {
2384 // return new SplitLine(new int[]{0, 0}, new String[]{
2385 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2386 // new int[]{0, start});
2388 // if (position != stringToSplit.length()) {
2389 // if (substringsCount == substringsStartPositions.length) {
2390 // System.arraycopy(substringsStartPositions, 0,
2391 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2392 // substringsCount);
2393 // System.arraycopy(substringsEndPositions, 0,
2394 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2395 // substringsCount);
2397 // // avoid empty extra substring, e.g. line terminated with a semi-colon
2398 // substringsStartPositions[substringsCount] = position;
2399 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2401 // if (splitOperatorsCount == splitOperators.length) {
2402 // System.arraycopy(splitOperators, 0,
2403 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2404 // splitOperatorsCount);
2406 // splitOperators[splitOperatorsCount] = 0;
2407 // // the last element of the stack is the position of the end of
2409 // // +1 because the substring method excludes the last character
2410 // String[] result = new String[substringsCount];
2411 // for (int i = 0; i < substringsCount; i++) {
2412 // int start = substringsStartPositions[i];
2413 // int end = substringsEndPositions[i];
2414 // if (stringToSplit.charAt(start) == ' ') {
2416 // substringsStartPositions[i]++;
2418 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2421 // result[i] = stringToSplit.substring(start, end);
2422 // substringsStartPositions[i] += offsetInGlobalLine;
2424 // if (splitOperatorsCount > substringsCount) {
2425 // System.arraycopy(substringsStartPositions, 0,
2426 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2427 // substringsCount);
2428 // System.arraycopy(substringsEndPositions, 0,
2429 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2430 // substringsCount);
2431 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2432 // substringsStartPositions[i] = position;
2433 // substringsEndPositions[i] = position;
2435 // System.arraycopy(splitOperators, 0,
2436 // (splitOperators = new int[splitOperatorsCount]), 0,
2437 // splitOperatorsCount);
2439 // System.arraycopy(substringsStartPositions, 0,
2440 // (substringsStartPositions = new int[substringsCount]), 0,
2441 // substringsCount);
2442 // System.arraycopy(substringsEndPositions, 0,
2443 // (substringsEndPositions = new int[substringsCount]), 0,
2444 // substringsCount);
2445 // System.arraycopy(splitOperators, 0,
2446 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2448 // SplitLine splitLine = new SplitLine(splitOperators, result,
2449 // substringsStartPositions);
2450 // return splitLine;
2453 private void updateMappedPositions(int startPosition) {
2454 if (positionsToMap == null) {
2457 char[] source = scanner.source;
2458 int sourceLength = source.length;
2459 while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2460 int posToMap = positionsToMap[indexToMap];
2461 if (posToMap < 0 || posToMap >= sourceLength) {
2462 // protection against out of bounds position
2463 if (posToMap == sourceLength) {
2464 mappedPositions[indexToMap] = formattedSource.length();
2466 indexToMap = positionsToMap.length; // no more mapping
2469 if (CharOperation.isWhitespace(source[posToMap])) {
2470 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2472 if (posToMap == sourceLength - 1) {
2473 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2475 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2482 private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2483 if (mappedPositions == null || mappedPositions.length == indexInMap)
2485 while (indexInMap < mappedPositions.length && startPosition <= mappedPositions[indexInMap]
2486 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2487 mappedPositions[indexInMap] += splitDelta;
2492 private int getLength(String s, int tabDepth) {
2494 for (int i = 0; i < tabDepth; i++) {
2495 length += options.tabSize;
2497 for (int i = 0, max = s.length(); i < max; i++) {
2498 char currentChar = s.charAt(i);
2499 switch (currentChar) {
2501 length += options.tabSize;
2511 * Sets the initial indentation level
2513 * @param indentationLevel
2514 * new indentation level
2518 public void setInitialIndentationLevel(int newIndentationLevel) {
2519 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;