1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.formatter;
12 import java.io.BufferedReader;
13 import java.io.IOException;
14 import java.io.StringReader;
15 import java.util.Hashtable;
16 import java.util.Locale;
19 import net.sourceforge.phpdt.core.ICodeFormatter;
20 import net.sourceforge.phpdt.core.compiler.CharOperation;
21 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
22 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
23 import net.sourceforge.phpdt.internal.compiler.ConfigurableOption;
24 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
25 import net.sourceforge.phpdt.internal.corext.codemanipulation.StubUtility;
26 import net.sourceforge.phpdt.internal.corext.util.Strings;
27 import net.sourceforge.phpdt.internal.formatter.impl.FormatterOptions;
28 import net.sourceforge.phpdt.internal.formatter.impl.SplitLine;
29 import net.sourceforge.phpdt.internal.ui.preferences.CodeFormatterPreferencePage;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.formatter.IContentFormatterExtension;
33 import org.eclipse.jface.text.formatter.IFormattingContext;
35 * <h2>How to format a piece of code ?</h2>
37 * <li>Create an instance of <code>CodeFormatter</code>
38 * <li>Use the method <code>void format(aString)</code> on this instance to
39 * format <code>aString</code>. It will return the formatted string.
42 public class CodeFormatter implements ITerminalSymbols, ICodeFormatter {
43 // IContentFormatterExtension {
44 public FormatterOptions options;
46 * Represents a block in the <code>constructions</code> stack.
48 public static final int BLOCK = ITerminalSymbols.TokenNameLBRACE;
50 * Represents a block following a control statement in the <code>constructions</code>
53 public static final int NONINDENT_BLOCK = -100;
55 * Contains the formatted output.
57 StringBuffer formattedSource;
59 * Contains the current line. <br>
60 * Will be dumped at the next "newline"
62 StringBuffer currentLineBuffer;
64 * Used during the formatting to get each token.
68 * Contains the tokens responsible for the current indentation level and the
69 * blocks not closed yet.
71 private int[] constructions;
73 * Index in the <code>constructions</code> array.
75 private int constructionsCount;
77 * Level of indentation of the current token (number of tab char put in front
80 private int indentationLevel;
82 * Regular level of indentation of all the lines
84 private int initialIndentationLevel;
86 * Used to split a line.
90 * To remember the offset between the beginning of the line and the beginning
93 int currentCommentOffset;
94 int currentLineIndentationLevel;
96 private boolean containsOpenCloseBraces;
97 private int indentationLevelForOpenCloseBraces;
99 * Collections of positions to map
101 private int[] positionsToMap;
103 * Collections of mapped positions
105 private int[] mappedPositions;
106 private int indexToMap;
107 private int indexInMap;
108 private int globalDelta;
109 private int lineDelta;
110 private int splitDelta;
111 private int beginningOfLineIndex;
112 private int multipleLineCommentCounter;
114 * Creates a new instance of Code Formatter using the given settings.
116 * @deprecated backport 1.0 internal functionality
118 public CodeFormatter(ConfigurableOption[] settings) {
119 this(convertConfigurableOptions(settings));
122 * Creates a new instance of Code Formatter using the FormattingOptions
123 * object given as argument
125 * @deprecated Use CodeFormatter(ConfigurableOption[]) instead
127 public CodeFormatter() {
131 * Creates a new instance of Code Formatter using the given settings.
133 public CodeFormatter(Map settings) {
134 // initialize internal state
135 constructionsCount = 0;
136 constructions = new int[10];
137 currentLineIndentationLevel = indentationLevel = initialIndentationLevel;
138 currentCommentOffset = -1;
139 // initialize primary and secondary scanners
140 scanner = new Scanner(true /* comment */
141 , true /* whitespace */
144 , true, /* tokenizeStrings */
145 null, null); // regular scanner for forming lines
146 scanner.recordLineSeparator = true;
147 // to remind of the position of the beginning of the line.
148 splitScanner = new Scanner(true /* comment */
149 , true /* whitespace */
152 , true, /* tokenizeStrings */
154 // secondary scanner to split long lines formed by primary scanning
155 // initialize current line buffer
156 currentLineBuffer = new StringBuffer();
157 this.options = new FormatterOptions(settings);
160 * Returns true if a lineSeparator has to be inserted before <code>operator</code>
163 private static boolean breakLineBeforeOperator(int operator) {
165 case TokenNameCOMMA :
166 case TokenNameSEMICOLON :
167 case TokenNameEQUAL :
174 * @deprecated backport 1.0 internal functionality
176 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
177 Hashtable options = new Hashtable(10);
178 for (int i = 0; i < settings.length; i++) {
179 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
180 String optionName = settings[i].getOptionName();
181 int valueIndex = settings[i].getCurrentValueIndex();
182 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
184 "net.sourceforge.phpdt.core.formatter.newline.openingBrace",
185 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
186 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
188 "net.sourceforge.phpdt.core.formatter.newline.controlStatement",
189 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
190 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
191 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll",
192 valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
193 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
194 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf",
195 valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
196 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
198 "net.sourceforge.phpdt.core.formatter.newline.emptyBlock",
199 valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
200 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
201 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String
202 .valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
203 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
204 options.put("net.sourceforge.phpdt.core.formatter.style.assignment",
205 valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
206 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
207 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char",
208 valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
209 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
210 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size",
211 String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
218 * Returns the end of the source code.
220 private final String copyRemainingSource() {
221 char str[] = scanner.source;
222 int startPosition = scanner.startPosition;
223 int length = str.length - startPosition;
224 StringBuffer bufr = new StringBuffer(length);
225 if (startPosition < str.length) {
226 bufr.append(str, startPosition, length);
228 return (bufr.toString());
231 * Inserts <code>tabCount</code> tab character or their equivalent number
234 private void dumpTab(int tabCount) {
235 if (options.indentWithTab) {
236 for (int j = 0; j < tabCount; j++) {
237 formattedSource.append('\t');
238 increaseSplitDelta(1);
241 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
242 formattedSource.append(' ');
243 increaseSplitDelta(1);
248 * Dumps <code>currentLineBuffer</code> into the formatted string.
250 private void flushBuffer() {
251 String currentString = currentLineBuffer.toString();
253 beginningOfLineIndex = formattedSource.length();
254 if (containsOpenCloseBraces) {
255 containsOpenCloseBraces = false;
256 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0,
258 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
260 outputLine(currentString, false, currentLineIndentationLevel, 0, -1,
263 int scannerSourceLength = scanner.source.length;
264 if (scannerSourceLength > 2) {
265 if (scanner.source[scannerSourceLength - 1] == '\n'
266 && scanner.source[scannerSourceLength - 2] == '\r') {
267 formattedSource.append(options.lineSeparatorSequence);
268 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
269 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
270 formattedSource.append(options.lineSeparatorSequence);
271 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
272 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
273 formattedSource.append(options.lineSeparatorSequence);
274 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
277 updateMappedPositions(scanner.startPosition);
280 * Formats the input string.
282 private void format() {
284 int previousToken = 0;
285 int previousCompilableToken = 0;
286 int indentationOffset = 0;
287 int newLinesInWhitespace = 0;
288 // number of new lines in the previous whitespace token
289 // (used to leave blank lines before comments)
290 int pendingNewLines = 0;
291 boolean expectingOpenBrace = false;
292 boolean clearNonBlockIndents = false;
293 // true if all indentations till the 1st { (usefull after } or ;)
294 boolean pendingSpace = true;
295 boolean pendingNewlineAfterParen = false;
296 // true when a cr is to be put after a ) (in conditional statements)
297 boolean inAssignment = false;
298 boolean inArrayAssignment = false;
299 boolean inThrowsClause = false;
300 boolean inClassOrInterfaceHeader = false;
301 int dollarBraceCount = 0;
302 // openBracketCount is used to count the number of open brackets not closed
304 int openBracketCount = 0;
305 int unarySignModifier = 0;
306 // openParenthesis[0] is used to count the parenthesis not belonging to a
308 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
309 int openParenthesisCount = 1;
310 int[] openParenthesis = new int[10];
311 // tokenBeforeColon is used to know what token goes along with the current
313 // it can be case or ?
314 int tokenBeforeColonCount = 0;
315 int[] tokenBeforeColon = new int[10];
316 constructionsCount = 0; // initializes the constructions count.
317 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
319 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
320 boolean specialElse = false;
321 // OPTION (IndentationLevel): initial indentation level may be non-zero.
322 currentLineIndentationLevel += constructionsCount;
323 // An InvalidInputException exception might cause the termination of this
327 // Get the next token. Catch invalid input and output it
328 // with minimal formatting, also catch end of input and
331 token = scanner.getNextToken();
333 int currentEndPosition = scanner.getCurrentTokenEndPosition();
334 int currentStartPosition = scanner.getCurrentTokenStartPosition();
335 System.out.print(currentStartPosition + "," + currentEndPosition
337 System.out.println(scanner.toStringAction(token));
339 // Patch for line comment
340 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
341 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
342 int length = scanner.currentPosition;
343 loop : for (int index = length - 1; index >= 0; index--) {
344 switch (scanner.source[index]) {
347 scanner.currentPosition--;
354 } catch (InvalidInputException e) {
355 if (!handleInvalidToken(e)) {
360 if (token == Scanner.TokenNameEOF)
363 * ## MODIFYING the indentation level before generating new lines and
364 * indentation in the output string
366 // Removes all the indentations made by statements not followed by a
368 // except if the current token is ELSE, CATCH or if we are in a
370 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
373 if (constructionsCount > 0
374 && constructions[constructionsCount - 1] == TokenNameelse) {
378 indentationLevel += popInclusiveUntil(TokenNameif);
380 // case TokenNamecatch :
381 // indentationLevel += popInclusiveUntil(TokenNamecatch);
383 // case TokenNamefinally :
384 // indentationLevel += popInclusiveUntil(TokenNamecatch);
386 case TokenNamewhile :
387 if (nlicsToken == TokenNamedo) {
388 indentationLevel += pop(TokenNamedo);
392 indentationLevel += popExclusiveUntilBlockOrCase();
393 // clear until a CASE, DEFAULT or BLOCK is encountered.
394 // Thus, the indentationLevel is correctly cleared either
395 // in a switch/case statement or in any other situation.
397 clearNonBlockIndents = false;
399 // returns to the indentation level created by the SWITCH keyword
400 // if the current token is a CASE or a DEFAULT
401 if (token == TokenNamecase || token == TokenNamedefault) {
402 indentationLevel += pop(TokenNamecase);
404 // if (token == Scanner.TokenNamethrows) {
405 // inThrowsClause = true;
407 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface)
408 && previousToken != Scanner.TokenNameDOT) {
409 inClassOrInterfaceHeader = true;
412 * ## APPEND newlines and indentations to the output string
414 // Do not add a new line between ELSE and IF, if the option
415 // elseIfOnSameLine is true.
416 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
417 // if (pendingNewlineAfterParen
418 // && previousCompilableToken == TokenNameelse
419 // && token == TokenNameif
420 // && options.compactElseIfMode) {
421 // pendingNewlineAfterParen = false;
422 // pendingNewLines = 0;
423 // indentationLevel += pop(TokenNameelse);
424 // // because else if is now one single statement,
425 // // the indentation level after it is increased by one and not by 2
426 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
428 // Add a newline & indent to the formatted source string if
429 // a for/if-else/while statement was scanned and there is no block
431 pendingNewlineAfterParen = pendingNewlineAfterParen
432 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
433 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
434 pendingNewlineAfterParen = false;
435 // Do to add a newline & indent sequence if the current token is an
436 // open brace or a period or if the current token is a semi-colon and
438 // previous token is a close paren.
439 // add a new line if a parenthesis belonging to a for() statement
440 // has been closed and the current token is not an opening brace
441 if (token != TokenNameLBRACE
443 // to avoid adding new line between else and a comment
444 && token != TokenNameDOT
445 && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
447 currentLineIndentationLevel = indentationLevel;
449 pendingSpace = false;
451 if (token == TokenNameLBRACE
452 && options.newLineBeforeOpeningBraceMode) {
454 if (constructionsCount > 0
455 && constructions[constructionsCount - 1] != BLOCK
456 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
457 currentLineIndentationLevel = indentationLevel - 1;
459 currentLineIndentationLevel = indentationLevel;
462 pendingSpace = false;
466 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode
467 && constructionsCount > 0
468 && constructions[constructionsCount - 1] == TokenNamedo) {
470 currentLineIndentationLevel = indentationLevel - 1;
472 pendingSpace = false;
475 if (token == TokenNameLBRACE && inThrowsClause) {
476 inThrowsClause = false;
477 if (options.newLineBeforeOpeningBraceMode) {
479 currentLineIndentationLevel = indentationLevel;
481 pendingSpace = false;
485 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
486 inClassOrInterfaceHeader = false;
487 if (options.newLineBeforeOpeningBraceMode) {
489 currentLineIndentationLevel = indentationLevel;
491 pendingSpace = false;
494 // Add pending new lines to the formatted source string.
495 // Note: pending new lines are not added if the current token
496 // is a single line comment or whitespace.
497 // if the comment is between parenthesis, there is no blank line
499 // (if it's a one-line comment, a blank line is added after it).
500 if (((pendingNewLines > 0 && (!isComment(token)))
501 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
502 && token != Scanner.TokenNameWHITESPACE) {
503 // Do not add newline & indent between an adjoining close brace and
504 // close paren. Anonymous inner classes may use this form.
505 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE
506 && token == TokenNameRPAREN;
507 // OPTION (NewLineInCompoundStatement): do not add newline & indent
508 // between close brace and else, (do) while, catch, and finally if
509 // newlineInCompoundStatement is true.
510 boolean nlicsOption = previousToken == TokenNameRBRACE
511 && !options.newlineInControlStatementMode
512 && (token == TokenNameelse
513 || (token == TokenNamewhile && nlicsToken == TokenNamedo)
514 || token == TokenNamecatch || token == TokenNamefinally);
515 // Do not add a newline & indent between a close brace and
517 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE
518 && token == TokenNameSEMICOLON;
519 // Do not add a new line & indent between a multiline comment and a
521 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK
522 && token == TokenNameLBRACE;
523 // Do not add a newline & indent between a close brace and a colon
524 // (in array assignments, for example).
525 boolean commaAndCloseBrace = previousToken == TokenNameRBRACE
526 && token == TokenNameCOMMA;
527 // Add a newline and indent, if appropriate.
529 || (!commentAndOpenBrace && !closeBraceAndCloseParen
530 && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
531 // if clearAllBlankLinesMode=false, leaves the blank lines
532 // inserted by the user
533 // if clearAllBlankLinesMode=true, removes all of then
534 // and insert only blank lines required by the formatting.
535 if (!options.clearAllBlankLinesMode) {
536 // (isComment(token))
537 pendingNewLines = (pendingNewLines < newLinesInWhitespace)
538 ? newLinesInWhitespace
540 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
542 if (previousCompilableToken == TokenNameLBRACE
543 && token == TokenNameRBRACE) {
544 containsOpenCloseBraces = true;
545 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
546 if (isComment(previousToken)) {
547 newLine(pendingNewLines);
550 * if (!(constructionsCount > 1 &&
551 * constructions[constructionsCount-1] == NONINDENT_BLOCK &&
552 * (constructions[constructionsCount-2] == TokenNamefor
554 if (options.newLineInEmptyBlockMode) {
555 if (inArrayAssignment) {
556 newLine(1); // array assigment with an empty block
558 newLine(pendingNewLines);
564 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
566 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
567 newLine(pendingNewLines);
570 if (((previousCompilableToken == TokenNameSEMICOLON)
571 || (previousCompilableToken == TokenNameLBRACE)
572 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
573 && (token == TokenNameRBRACE)) {
574 indentationOffset = -1;
575 indentationLevel += popExclusiveUntilBlock();
577 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
579 currentLineIndentationLevel++;
581 currentLineIndentationLevel = indentationLevel
584 pendingSpace = false;
585 indentationOffset = 0;
588 newLinesInWhitespace = 0;
590 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
594 boolean phpTagAndWhitespace =
595 previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
597 // case TokenNameDOLLAR :
598 // dollarBraceCount++;
601 // case TokenNamefinally :
602 expectingOpenBrace = true;
603 pendingNewlineAfterParen = true;
604 indentationLevel += pushControlStatement(token);
607 case TokenNamedefault :
608 if (tokenBeforeColonCount == tokenBeforeColon.length) {
609 System.arraycopy(tokenBeforeColon, 0,
610 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
611 tokenBeforeColonCount);
613 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
614 indentationLevel += pushControlStatement(TokenNamecase);
616 case TokenNameQUESTION :
617 if (tokenBeforeColonCount == tokenBeforeColon.length) {
618 System.arraycopy(tokenBeforeColon, 0,
619 (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0,
620 tokenBeforeColonCount);
622 tokenBeforeColon[tokenBeforeColonCount++] = token;
624 case TokenNameswitch :
627 case TokenNamewhile :
628 if (openParenthesisCount == openParenthesis.length) {
629 System.arraycopy(openParenthesis, 0,
630 (openParenthesis = new int[openParenthesisCount * 2]), 0,
631 openParenthesisCount);
633 openParenthesis[openParenthesisCount++] = 0;
634 expectingOpenBrace = true;
635 indentationLevel += pushControlStatement(token);
638 pendingNewlineAfterParen = true;
639 case TokenNamecatch :
640 // several CATCH statements can be contiguous.
641 // a CATCH is encountered pop until first CATCH (if a CATCH
642 // follows a TRY it works the same way,
643 // as CATCH and TRY are the same token in the stack).
644 expectingOpenBrace = true;
645 indentationLevel += pushControlStatement(TokenNamecatch);
648 expectingOpenBrace = true;
649 indentationLevel += pushControlStatement(token);
654 case TokenNameLPAREN :
655 // if (previousToken == TokenNamesynchronized) {
656 // indentationLevel += pushControlStatement(previousToken);
658 // Put a space between the previous and current token if the
659 // previous token was not a keyword, open paren, logical
660 // compliment (eg: !), semi-colon, open brace, close brace,
662 if (previousCompilableToken != TokenNameLBRACKET
663 && previousToken != TokenNameIdentifier && previousToken != 0
664 && previousToken != TokenNameNOT
665 && previousToken != TokenNameLPAREN
666 && previousToken != TokenNameTWIDDLE
667 && previousToken != TokenNameSEMICOLON
668 && previousToken != TokenNameLBRACE
669 && previousToken != TokenNameRBRACE
670 && previousToken != TokenNamesuper) {
671 // && previousToken != TokenNamethis) {
674 // If in a for/if/while statement, increase the parenthesis count
675 // for the current openParenthesisCount
676 // else increase the count for stand alone parenthesis.
677 if (openParenthesisCount > 0)
678 openParenthesis[openParenthesisCount - 1]++;
680 openParenthesis[0]++;
681 pendingSpace = false;
684 case TokenNameRPAREN :
685 // Decrease the parenthesis count
686 // if there is no more unclosed parenthesis,
687 // a new line and indent may be append (depending on the next
689 if ((openParenthesisCount > 1)
690 && (openParenthesis[openParenthesisCount - 1] > 0)) {
691 openParenthesis[openParenthesisCount - 1]--;
692 if (openParenthesis[openParenthesisCount - 1] <= 0) {
693 pendingNewlineAfterParen = true;
694 inAssignment = false;
695 openParenthesisCount--;
698 openParenthesis[0]--;
700 pendingSpace = false;
702 case TokenNameLBRACE :
703 if (previousCompilableToken == TokenNameDOLLAR) {
706 if ((previousCompilableToken == TokenNameRBRACKET)
707 || (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 //indentationLevel += popExclusiveUntilBlock();
747 case TokenNameswitch :
751 case TokenNamecatch :
752 case TokenNamefinally :
753 case TokenNamewhile :
755 // case TokenNamesynchronized :
756 clearNonBlockIndents = true;
763 case TokenNameLBRACKET :
765 pendingSpace = false;
767 case TokenNameRBRACKET :
768 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
769 // if there is no left bracket to close, the right bracket is
771 pendingSpace = false;
773 case TokenNameCOMMA :
775 pendingSpace = false;
777 case TokenNameSEMICOLON :
778 // Do not generate line terminators in the definition of
779 // the for statement.
780 // if not in this case, jump a line and reduce indentation after
782 // if the block it closes belongs to a conditional statement (if,
784 if (openParenthesisCount <= 1) {
786 if (expectingOpenBrace) {
787 clearNonBlockIndents = true;
788 expectingOpenBrace = false;
791 inAssignment = false;
792 pendingSpace = false;
794 case TokenNamePLUS_PLUS :
795 case TokenNameMINUS_MINUS :
796 // Do not put a space between a post-increment/decrement
797 // and the identifier being modified.
798 if (previousToken == TokenNameIdentifier
799 || previousToken == TokenNameRBRACKET) {
800 pendingSpace = false;
804 // previously ADDITION
805 case TokenNameMINUS :
806 // Handle the unary operators plus and minus via a flag
807 if (!isLiteralToken(previousToken)
808 && previousToken != TokenNameIdentifier
809 && previousToken != TokenNameRPAREN
810 && previousToken != TokenNameRBRACKET) {
811 unarySignModifier = 1;
814 case TokenNameCOLON :
815 // In a switch/case statement, add a newline & indent
816 // when a colon is encountered.
817 if (tokenBeforeColonCount > 0) {
818 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
821 tokenBeforeColonCount--;
824 case TokenNameEQUAL :
827 case Scanner.TokenNameCOMMENT_LINE :
830 currentLineIndentationLevel++;
832 break; // a line is always inserted after a one-line comment
833 case Scanner.TokenNameCOMMENT_PHPDOC :
834 case Scanner.TokenNameCOMMENT_BLOCK :
835 currentCommentOffset = getCurrentCommentOffset();
838 case Scanner.TokenNameWHITESPACE :
839 if (!phpTagAndWhitespace) {
840 // Count the number of line terminators in the whitespace so
841 // line spacing can be preserved near comments.
842 char[] source = scanner.source;
843 newLinesInWhitespace = 0;
844 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
845 if (source[i] == '\r') {
847 if (source[++i] == '\n') {
848 newLinesInWhitespace++;
850 newLinesInWhitespace++;
853 newLinesInWhitespace++;
855 } else if (source[i] == '\n') {
856 newLinesInWhitespace++;
859 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
862 // case TokenNameHTML :
863 // // Add the next token to the formatted source string.
864 // // outputCurrentToken(token);
865 // int startPosition = scanner.startPosition;
867 // for (int i = startPosition, max = scanner.currentPosition; i <
869 // char currentCharacter = scanner.source[i];
870 // updateMappedPositions(i);
871 // currentLineBuffer.append(currentCharacter);
875 if ((token == TokenNameIdentifier) || isLiteralToken(token)
876 || token == TokenNamesuper) {
877 // || token == TokenNamethis) {
878 // Do not put a space between a unary operator
879 // (eg: ++, --, +, -) and the identifier being modified.
880 if (previousToken == TokenNamePLUS_PLUS
881 || previousToken == TokenNameMINUS_MINUS
882 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
883 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
884 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
885 pendingSpace = false;
887 unarySignModifier = 0;
891 // Do not output whitespace tokens.
892 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
894 * Add pending space to the formatted source string. Do not output a
895 * space under the following circumstances: 1) this is the first pass 2)
896 * previous token is an open paren 3) previous token is a period 4)
897 * previous token is the logical compliment (eg: !) 5) previous token
898 * is the bitwise compliment (eg: ~) 6) previous token is the open
899 * bracket (eg: [) 7) in an assignment statement, if the previous
900 * token is an open brace or the current token is a close brace 8)
901 * previous token is a single line comment 9) current token is a '->'
903 if (token == TokenNameMINUS_GREATER
904 && options.compactDereferencingMode)
905 pendingSpace = false;
907 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE
908 && token == TokenNameRBRACE;
910 && insertSpaceAfter(previousToken)
911 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
912 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
913 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL))
914 && !openAndCloseBrace)
917 // Add the next token to the formatted source string.
918 outputCurrentToken(token);
919 if (token == Scanner.TokenNameCOMMENT_LINE
920 && openParenthesisCount > 1) {
922 currentLineBuffer.append(options.lineSeparatorSequence);
923 increaseLineDelta(options.lineSeparatorSequence.length);
927 // Whitespace tokens do not need to be remembered.
928 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
929 previousToken = token;
930 if (token != Scanner.TokenNameCOMMENT_BLOCK
931 && token != Scanner.TokenNameCOMMENT_LINE
932 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
933 previousCompilableToken = token;
937 output(copyRemainingSource());
939 // dump the last token of the source in the formatted output.
940 } catch (InvalidInputException e) {
941 output(copyRemainingSource());
943 // dump the last token of the source in the formatted output.
947 * Formats the char array <code>sourceString</code>, and returns a string
948 * containing the formatted version.
950 * @return the formatted ouput.
952 public String formatSourceString(String sourceString) {
953 char[] sourceChars = sourceString.toCharArray();
954 formattedSource = new StringBuffer(sourceChars.length);
955 scanner.setSource(sourceChars);
957 return formattedSource.toString();
960 * Formats the char array <code>sourceString</code>, and returns a string
961 * containing the formatted version.
964 * the string to format
965 * @param indentationLevel
966 * the initial indentation level
967 * @return the formatted ouput.
969 public String format(String string, int indentationLevel) {
970 return format(string, indentationLevel, (int[]) null);
973 * Formats the char array <code>sourceString</code>, and returns a string
974 * containing the formatted version. The positions array is modified to
975 * contain the mapped positions.
978 * the string to format
979 * @param indentationLevel
980 * the initial indentation level
982 * the array of positions to map
983 * @return the formatted ouput.
985 public String format(String string, int indentationLevel, int[] positions) {
986 return this.format(string, indentationLevel, positions, null);
988 public String format(String string, int indentationLevel, int[] positions,
989 String lineSeparator) {
990 if (lineSeparator != null) {
991 this.options.setLineSeparator(lineSeparator);
993 if (positions != null) {
994 this.setPositionsToMap(positions);
995 this.setInitialIndentationLevel(indentationLevel);
996 String formattedString = this.formatSourceString(string);
997 int[] mappedPositions = this.getMappedPositions();
998 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
999 return formattedString;
1001 this.setInitialIndentationLevel(indentationLevel);
1002 return this.formatSourceString(string);
1006 * Formats the char array <code>sourceString</code>, and returns a string
1007 * containing the formatted version. The initial indentation level is 0.
1010 * the string to format
1011 * @return the formatted ouput.
1013 public String format(String string) {
1014 return this.format(string, 0, (int[]) null);
1017 * Formats a given source string, starting indenting it at a particular depth
1018 * and using the given options
1020 * @deprecated backport 1.0 internal functionality
1022 public static String format(String sourceString, int initialIndentationLevel,
1023 ConfigurableOption[] options) {
1024 CodeFormatter formatter = new CodeFormatter(options);
1025 formatter.setInitialIndentationLevel(initialIndentationLevel);
1026 return formatter.formatSourceString(sourceString);
1029 * Returns the number of characters and tab char between the beginning of the
1030 * line and the beginning of the comment.
1032 private int getCurrentCommentOffset() {
1033 int linePtr = scanner.linePtr;
1034 // if there is no beginning of line, return 0.
1038 int beginningOfLine = scanner.lineEnds[linePtr];
1039 int currentStartPosition = scanner.startPosition;
1040 char[] source = scanner.source;
1041 // find the position of the beginning of the line containing the comment
1042 while (beginningOfLine > currentStartPosition) {
1044 beginningOfLine = scanner.lineEnds[--linePtr];
1046 beginningOfLine = 0;
1050 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1051 char currentCharacter = source[i];
1052 switch (currentCharacter) {
1054 offset += options.tabSize;
1069 * Returns an array of descriptions for the configurable options. The
1070 * descriptions may be changed and passed back to a different compiler.
1072 * @deprecated backport 1.0 internal functionality
1074 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1075 String componentName = CodeFormatter.class.getName();
1076 FormatterOptions options = new FormatterOptions();
1077 return new ConfigurableOption[]{
1078 new ConfigurableOption(componentName, "newline.openingBrace", locale,
1079 options.newLineBeforeOpeningBraceMode ? 0 : 1),
1081 new ConfigurableOption(componentName, "newline.controlStatement",
1082 locale, options.newlineInControlStatementMode ? 0 : 1),
1084 new ConfigurableOption(componentName, "newline.clearAll", locale,
1085 options.clearAllBlankLinesMode ? 0 : 1),
1087 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1088 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1089 new ConfigurableOption(componentName, "newline.emptyBlock", locale,
1090 options.newLineInEmptyBlockMode ? 0 : 1),
1092 new ConfigurableOption(componentName, "line.split", locale,
1093 options.maxLineLength),
1095 new ConfigurableOption(componentName, "style.compactAssignment",
1096 locale, options.compactAssignmentMode ? 0 : 1),
1098 new ConfigurableOption(componentName, "tabulation.char", locale,
1099 options.indentWithTab ? 0 : 1),
1101 new ConfigurableOption(componentName, "tabulation.size", locale,
1102 options.tabSize) //$NON-NLS-1$
1106 * Returns the array of mapped positions. Returns null is no positions have
1110 * @deprecated There is no need to retrieve the mapped positions anymore.
1112 public int[] getMappedPositions() {
1113 return mappedPositions;
1116 * Returns the priority of the token given as argument <br>
1117 * The most prioritary the token is, the smallest the return value is.
1119 * @return the priority of <code>token</code>
1121 * the token of which the priority is requested
1123 private static int getTokenPriority(int token) {
1125 case TokenNameextends :
1126 // case TokenNameimplements :
1127 // case TokenNamethrows :
1129 case TokenNameSEMICOLON :
1132 case TokenNameCOMMA :
1135 case TokenNameEQUAL :
1138 case TokenNameAND_AND :
1140 case TokenNameOR_OR :
1143 case TokenNameQUESTION :
1145 case TokenNameCOLON :
1147 return 50; // it's better cutting on ?: than on ;
1148 case TokenNameEQUAL_EQUAL :
1150 case TokenNameEQUAL_EQUAL_EQUAL :
1152 case TokenNameNOT_EQUAL :
1154 case TokenNameNOT_EQUAL_EQUAL :
1157 case TokenNameLESS :
1159 case TokenNameLESS_EQUAL :
1161 case TokenNameGREATER :
1163 case TokenNameGREATER_EQUAL :
1165 // case TokenNameinstanceof : // instanceof
1167 case TokenNamePLUS :
1169 case TokenNameMINUS :
1172 case TokenNameMULTIPLY :
1174 case TokenNameDIVIDE :
1176 case TokenNameREMAINDER :
1179 case TokenNameLEFT_SHIFT :
1181 case TokenNameRIGHT_SHIFT :
1183 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1192 case TokenNameMULTIPLY_EQUAL :
1194 case TokenNameDIVIDE_EQUAL :
1196 case TokenNameREMAINDER_EQUAL :
1198 case TokenNamePLUS_EQUAL :
1200 case TokenNameMINUS_EQUAL :
1202 case TokenNameLEFT_SHIFT_EQUAL :
1204 case TokenNameRIGHT_SHIFT_EQUAL :
1206 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1207 case TokenNameAND_EQUAL :
1209 case TokenNameXOR_EQUAL :
1211 case TokenNameOR_EQUAL :
1218 return Integer.MAX_VALUE;
1222 * Handles the exception raised when an invalid token is encountered. Returns
1223 * true if the exception has been handled, false otherwise.
1225 private boolean handleInvalidToken(Exception e) {
1226 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT)
1227 || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1228 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1233 private final void increaseGlobalDelta(int offset) {
1234 globalDelta += offset;
1236 private final void increaseLineDelta(int offset) {
1237 lineDelta += offset;
1239 private final void increaseSplitDelta(int offset) {
1240 splitDelta += offset;
1243 * Returns true if a space has to be inserted after <code>operator</code>
1246 private boolean insertSpaceAfter(int token) {
1248 case TokenNameLPAREN :
1250 case TokenNameTWIDDLE :
1254 case TokenNameWHITESPACE :
1255 case TokenNameLBRACKET :
1256 case TokenNameDOLLAR :
1257 case Scanner.TokenNameCOMMENT_LINE :
1264 * Returns true if a space has to be inserted before <code>operator</code>
1265 * false otherwise. <br>
1266 * Cannot be static as it uses the code formatter options (to know if the
1267 * compact assignment mode is on).
1269 private boolean insertSpaceBefore(int token) {
1271 case TokenNameEQUAL :
1272 return (!options.compactAssignmentMode);
1277 private static boolean isComment(int token) {
1278 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK
1279 || token == Scanner.TokenNameCOMMENT_LINE
1280 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1283 private static boolean isLiteralToken(int token) {
1284 boolean result = token == TokenNameIntegerLiteral
1285 // || token == TokenNameLongLiteral
1286 // || token == TokenNameFloatingPointLiteral
1287 || token == TokenNameDoubleLiteral
1288 // || token == TokenNameCharacterLiteral
1289 || token == TokenNameStringDoubleQuote;
1293 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>,
1294 * it is split and the result is dumped in <code>formattedSource</code>
1296 * @param newLineCount
1297 * the number of new lines to append
1299 private void newLine(int newLineCount) {
1300 // format current line
1302 beginningOfLineIndex = formattedSource.length();
1303 String currentLine = currentLineBuffer.toString();
1304 if (containsOpenCloseBraces) {
1305 containsOpenCloseBraces = false;
1306 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1,
1308 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1310 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null,
1313 // dump line break(s)
1314 for (int i = 0; i < newLineCount; i++) {
1315 formattedSource.append(options.lineSeparatorSequence);
1316 increaseSplitDelta(options.lineSeparatorSequence.length);
1318 // reset formatter for next line
1319 int currentLength = currentLine.length();
1320 currentLineBuffer = new StringBuffer(currentLength > maxLineSize
1321 ? maxLineSize = currentLength
1323 increaseGlobalDelta(splitDelta);
1324 increaseGlobalDelta(lineDelta);
1326 currentLineIndentationLevel = initialIndentationLevel;
1328 private String operatorString(int operator) {
1330 case TokenNameextends :
1331 return "extends"; //$NON-NLS-1$
1332 // case TokenNameimplements :
1333 // return "implements"; //$NON-NLS-1$
1335 // case TokenNamethrows :
1336 // return "throws"; //$NON-NLS-1$
1337 case TokenNameSEMICOLON :
1339 return ";"; //$NON-NLS-1$
1340 case TokenNameCOMMA :
1342 return ","; //$NON-NLS-1$
1343 case TokenNameEQUAL :
1345 return "="; //$NON-NLS-1$
1346 case TokenNameAND_AND :
1348 return "&&"; //$NON-NLS-1$
1349 case TokenNameOR_OR :
1351 return "||"; //$NON-NLS-1$
1352 case TokenNameQUESTION :
1354 return "?"; //$NON-NLS-1$
1355 case TokenNameCOLON :
1357 return ":"; //$NON-NLS-1$
1358 case TokenNamePAAMAYIM_NEKUDOTAYIM :
1360 return "::"; //$NON-NLS-1$
1361 case TokenNameEQUAL_EQUAL :
1362 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1363 return "=="; //$NON-NLS-1$
1364 case TokenNameEQUAL_EQUAL_EQUAL :
1365 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1366 return "==="; //$NON-NLS-1$
1367 case TokenNameEQUAL_GREATER :
1369 return "=>"; //$NON-NLS-1$
1370 case TokenNameNOT_EQUAL :
1371 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1372 return "!="; //$NON-NLS-1$
1373 case TokenNameNOT_EQUAL_EQUAL :
1374 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1375 return "!=="; //$NON-NLS-1$
1376 case TokenNameLESS :
1378 return "<"; //$NON-NLS-1$
1379 case TokenNameLESS_EQUAL :
1381 return "<="; //$NON-NLS-1$
1382 case TokenNameGREATER :
1384 return ">"; //$NON-NLS-1$
1385 case TokenNameGREATER_EQUAL :
1387 return ">="; //$NON-NLS-1$
1388 // case TokenNameinstanceof : // instanceof
1389 // return "instanceof"; //$NON-NLS-1$
1390 case TokenNamePLUS :
1391 // + (15.17, 15.17.2)
1392 return "+"; //$NON-NLS-1$
1393 case TokenNameMINUS :
1395 return "-"; //$NON-NLS-1$
1396 case TokenNameMULTIPLY :
1398 return "*"; //$NON-NLS-1$
1399 case TokenNameDIVIDE :
1401 return "/"; //$NON-NLS-1$
1402 case TokenNameREMAINDER :
1404 return "%"; //$NON-NLS-1$
1405 case TokenNameLEFT_SHIFT :
1407 return "<<"; //$NON-NLS-1$
1408 case TokenNameRIGHT_SHIFT :
1410 return ">>"; //$NON-NLS-1$
1411 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1412 // return ">>>"; //$NON-NLS-1$
1414 // & (15.21, 15.21.1, 15.21.2)
1415 return "&"; //$NON-NLS-1$
1417 // | (15.21, 15.21.1, 15.21.2)
1418 return "|"; //$NON-NLS-1$
1420 // ^ (15.21, 15.21.1, 15.21.2)
1421 return "^"; //$NON-NLS-1$
1422 case TokenNameMULTIPLY_EQUAL :
1424 return "*="; //$NON-NLS-1$
1425 case TokenNameDIVIDE_EQUAL :
1427 return "/="; //$NON-NLS-1$
1428 case TokenNameREMAINDER_EQUAL :
1430 return "%="; //$NON-NLS-1$
1431 case TokenNamePLUS_EQUAL :
1433 return "+="; //$NON-NLS-1$
1434 case TokenNameMINUS_EQUAL :
1436 return "-="; //$NON-NLS-1$
1437 case TokenNameMINUS_GREATER :
1439 return "->"; //$NON-NLS-1$
1440 case TokenNameLEFT_SHIFT_EQUAL :
1442 return "<<="; //$NON-NLS-1$
1443 case TokenNameRIGHT_SHIFT_EQUAL :
1445 return ">>="; //$NON-NLS-1$
1446 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1447 // return ">>>="; //$NON-NLS-1$
1448 case TokenNameAND_EQUAL :
1450 return "&="; //$NON-NLS-1$
1451 case TokenNameXOR_EQUAL :
1453 return "^="; //$NON-NLS-1$
1454 case TokenNameOR_EQUAL :
1456 return "|="; //$NON-NLS-1$
1459 return "."; //$NON-NLS-1$
1461 return ""; //$NON-NLS-1$
1465 * Appends <code>stringToOutput</code> to the formatted output. <br>
1466 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1468 private void output(String stringToOutput) {
1469 char currentCharacter;
1470 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1471 currentCharacter = stringToOutput.charAt(i);
1472 if (currentCharacter != '\t') {
1473 currentLineBuffer.append(currentCharacter);
1478 * Appends <code>token</code> to the formatted output. <br>
1479 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent
1482 private void outputCurrentToken(int token) {
1483 char[] source = scanner.source;
1484 int startPosition = scanner.startPosition;
1486 case Scanner.TokenNameCOMMENT_PHPDOC :
1487 case Scanner.TokenNameCOMMENT_BLOCK :
1488 case Scanner.TokenNameCOMMENT_LINE :
1489 boolean endOfLine = false;
1490 int currentCommentOffset = getCurrentCommentOffset();
1491 int beginningOfLineSpaces = 0;
1493 currentCommentOffset = getCurrentCommentOffset();
1494 beginningOfLineSpaces = 0;
1495 boolean pendingCarriageReturn = false;
1496 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1497 char currentCharacter = source[i];
1498 updateMappedPositions(i);
1499 switch (currentCharacter) {
1501 pendingCarriageReturn = true;
1505 if (pendingCarriageReturn) {
1506 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1508 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1510 pendingCarriageReturn = false;
1511 currentLineBuffer.append(options.lineSeparatorSequence);
1512 beginningOfLineSpaces = 0;
1516 if (pendingCarriageReturn) {
1517 pendingCarriageReturn = false;
1518 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1519 currentLineBuffer.append(options.lineSeparatorSequence);
1520 beginningOfLineSpaces = 0;
1524 // we remove a maximum of currentCommentOffset characters (tabs
1525 // are converted to space numbers).
1526 beginningOfLineSpaces += options.tabSize;
1527 if (beginningOfLineSpaces > currentCommentOffset) {
1528 currentLineBuffer.append(currentCharacter);
1530 increaseGlobalDelta(-1);
1533 currentLineBuffer.append(currentCharacter);
1537 if (pendingCarriageReturn) {
1538 pendingCarriageReturn = false;
1539 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1540 currentLineBuffer.append(options.lineSeparatorSequence);
1541 beginningOfLineSpaces = 0;
1545 // we remove a maximum of currentCommentOffset characters (tabs
1546 // are converted to space numbers).
1547 beginningOfLineSpaces++;
1548 if (beginningOfLineSpaces > currentCommentOffset) {
1549 currentLineBuffer.append(currentCharacter);
1551 increaseGlobalDelta(-1);
1554 currentLineBuffer.append(currentCharacter);
1558 if (pendingCarriageReturn) {
1559 pendingCarriageReturn = false;
1560 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1561 currentLineBuffer.append(options.lineSeparatorSequence);
1562 beginningOfLineSpaces = 0;
1565 beginningOfLineSpaces = 0;
1566 currentLineBuffer.append(currentCharacter);
1571 updateMappedPositions(scanner.currentPosition - 1);
1572 multipleLineCommentCounter++;
1575 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1576 char currentCharacter = source[i];
1577 updateMappedPositions(i);
1578 currentLineBuffer.append(currentCharacter);
1583 * Outputs <code>currentString</code>:<br>
1585 * <li>If its length is < maxLineLength, output
1586 * <li>Otherwise it is split.
1589 * @param currentString
1591 * @param preIndented
1592 * whether the string to output was pre-indented
1594 * number of indentation to put in front of <code>currentString</code>
1596 * value of the operator belonging to <code>currentString</code>.
1598 private void outputLine(String currentString, boolean preIndented, int depth,
1599 int operator, int substringIndex, int[] startSubstringIndexes,
1600 int offsetInGlobalLine) {
1601 boolean emptyFirstSubString = false;
1602 String operatorString = operatorString(operator);
1603 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1604 boolean placeOperatorAhead = !placeOperatorBehind;
1605 // dump prefix operator?
1606 if (placeOperatorAhead) {
1611 if (operator != 0) {
1612 if (insertSpaceBefore(operator)) {
1613 formattedSource.append(' ');
1614 increaseSplitDelta(1);
1616 formattedSource.append(operatorString);
1617 increaseSplitDelta(operatorString.length());
1618 if (insertSpaceAfter(operator)
1619 && operator != TokenNameimplements
1620 && operator != TokenNameextends) {
1621 // && operator != TokenNamethrows) {
1622 formattedSource.append(' ');
1623 increaseSplitDelta(1);
1627 SplitLine splitLine = null;
1628 if (options.maxLineLength == 0
1629 || getLength(currentString, depth) < options.maxLineLength
1630 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1631 // depending on the type of operator, outputs new line before of after
1633 // indent before postfix operator
1634 // indent also when the line cannot be split
1635 if (operator == TokenNameextends
1636 || operator == TokenNameimplements ) {
1637 // || operator == TokenNamethrows) {
1638 formattedSource.append(' ');
1639 increaseSplitDelta(1);
1641 if (placeOperatorBehind) {
1646 int max = currentString.length();
1647 if (multipleLineCommentCounter != 0) {
1649 BufferedReader reader = new BufferedReader(new StringReader(
1651 String line = reader.readLine();
1652 while (line != null) {
1653 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1654 beginningOfLineIndex + line.length()
1655 + options.lineSeparatorSequence.length);
1656 formattedSource.append(line);
1657 beginningOfLineIndex = beginningOfLineIndex + line.length();
1658 if ((line = reader.readLine()) != null) {
1659 formattedSource.append(options.lineSeparatorSequence);
1660 beginningOfLineIndex += options.lineSeparatorSequence.length;
1661 dumpTab(currentLineIndentationLevel);
1665 } catch (IOException e) {
1666 e.printStackTrace();
1669 updateMappedPositionsWhileSplitting(beginningOfLineIndex,
1670 beginningOfLineIndex + max);
1671 for (int i = 0; i < max; i++) {
1672 char currentChar = currentString.charAt(i);
1673 switch (currentChar) {
1678 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1679 // split with a comment inside a condition
1680 // a substring cannot end with a lineSeparatorSequence,
1681 // except if it has been added by format() after a one-line
1683 formattedSource.append(options.lineSeparatorSequence);
1684 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1689 formattedSource.append(currentChar);
1693 // update positions inside the mappedPositions table
1694 if (substringIndex != -1) {
1695 if (multipleLineCommentCounter == 0) {
1696 int startPosition = beginningOfLineIndex
1697 + startSubstringIndexes[substringIndex];
1698 updateMappedPositionsWhileSplitting(startPosition, startPosition
1701 // compute the splitDelta resulting with the operator and blank removal
1702 if (substringIndex + 1 != startSubstringIndexes.length) {
1703 increaseSplitDelta(startSubstringIndexes[substringIndex] + max
1704 - startSubstringIndexes[substringIndex + 1]);
1707 // dump postfix operator?
1708 if (placeOperatorBehind) {
1709 if (insertSpaceBefore(operator)) {
1710 formattedSource.append(' ');
1711 if (operator != 0) {
1712 increaseSplitDelta(1);
1715 formattedSource.append(operatorString);
1716 if (operator != 0) {
1717 increaseSplitDelta(operatorString.length());
1722 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1723 // extends has to stand alone on a line when currentString has been split.
1724 if (options.maxLineLength != 0 && splitLine != null
1725 && (operator == TokenNameextends)) {
1726 // || operator == TokenNameimplements
1727 // || operator == TokenNamethrows)) {
1728 formattedSource.append(options.lineSeparatorSequence);
1729 increaseSplitDelta(options.lineSeparatorSequence.length);
1732 if (operator == TokenNameextends) {
1733 // || operator == TokenNameimplements
1734 // || operator == TokenNamethrows) {
1735 formattedSource.append(' ');
1736 increaseSplitDelta(1);
1739 // perform actual splitting
1740 String result[] = splitLine.substrings;
1741 int[] splitOperators = splitLine.operators;
1742 if (result[0].length() == 0) {
1743 // when the substring 0 is null, the substring 1 is correctly indented.
1745 emptyFirstSubString = true;
1747 // the operator going in front of the result[0] string is the operator
1749 for (int i = 0, max = result.length; i < max; i++) {
1750 // the new depth is the current one if this is the first substring,
1751 // the current one + 1 otherwise.
1752 // if the substring is a comment, use the current indentation Level
1753 // instead of the depth
1754 // (-1 because the ouputline increases depth).
1755 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1757 String currentResult = result[i];
1758 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1759 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1760 || currentResult.startsWith("//")) //$NON-NLS-1$
1761 ? indentationLevel - 1 : depth;
1762 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString)
1764 : false, i == 0 ? newDepth : newDepth + 1, splitOperators[i], i,
1765 splitLine.startSubstringsIndexes, currentString
1766 .indexOf(currentResult));
1768 formattedSource.append(options.lineSeparatorSequence);
1769 increaseSplitDelta(options.lineSeparatorSequence.length);
1773 if (result.length == splitOperators.length - 1) {
1774 int lastOperator = splitOperators[result.length];
1775 String lastOperatorString = operatorString(lastOperator);
1776 formattedSource.append(options.lineSeparatorSequence);
1777 increaseSplitDelta(options.lineSeparatorSequence.length);
1778 if (breakLineBeforeOperator(lastOperator)) {
1780 if (lastOperator != 0) {
1781 if (insertSpaceBefore(lastOperator)) {
1782 formattedSource.append(' ');
1783 increaseSplitDelta(1);
1785 formattedSource.append(lastOperatorString);
1786 increaseSplitDelta(lastOperatorString.length());
1787 if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements
1788 && lastOperator != TokenNameextends) {
1789 // && lastOperator != TokenNamethrows) {
1790 formattedSource.append(' ');
1791 increaseSplitDelta(1);
1796 if (placeOperatorBehind) {
1797 if (insertSpaceBefore(operator)) {
1798 formattedSource.append(' ');
1799 increaseSplitDelta(1);
1801 formattedSource.append(operatorString);
1802 //increaseSplitDelta(operatorString.length());
1806 * Pops the top statement of the stack if it is <code>token</code>
1808 private int pop(int token) {
1810 if ((constructionsCount > 0)
1811 && (constructions[constructionsCount - 1] == token)) {
1813 constructionsCount--;
1818 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a
1819 * <code>NONINDENT_BLOCK</code>.
1821 private int popBlock() {
1823 if ((constructionsCount > 0)
1824 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1825 if (constructions[constructionsCount - 1] == BLOCK)
1827 constructionsCount--;
1832 * Pops elements until the stack is empty or the top element is <code>token</code>.
1834 * Does not remove <code>token</code> from the stack.
1837 * the token to be left as the top of the stack
1839 private int popExclusiveUntil(int token) {
1841 int startCount = constructionsCount;
1842 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1843 if (constructions[i] != NONINDENT_BLOCK)
1845 constructionsCount--;
1850 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1851 * or a <code>NONINDENT_BLOCK</code>.<br>
1852 * Does not remove it from the stack.
1854 private int popExclusiveUntilBlock() {
1855 int startCount = constructionsCount;
1857 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1858 && constructions[i] != NONINDENT_BLOCK; i--) {
1859 constructionsCount--;
1865 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>,
1866 * a <code>NONINDENT_BLOCK</code> or a <code>CASE</code>.<br>
1867 * Does not remove it from the stack.
1869 private int popExclusiveUntilBlockOrCase() {
1870 int startCount = constructionsCount;
1872 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK
1873 && constructions[i] != NONINDENT_BLOCK
1874 && constructions[i] != TokenNamecase; i--) {
1875 constructionsCount--;
1881 * Pops elements until the stack is empty or the top element is <code>token</code>.
1883 * Removes <code>token</code> from the stack too.
1886 * the token to remove from the stack
1888 private int popInclusiveUntil(int token) {
1889 int startCount = constructionsCount;
1891 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1892 if (constructions[i] != NONINDENT_BLOCK)
1894 constructionsCount--;
1896 if (constructionsCount > 0) {
1897 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1899 constructionsCount--;
1904 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>
1905 * or a <code>NONINDENT_BLOCK</code>.<br>
1906 * Does not remove it from the stack.
1908 private int popInclusiveUntilBlock() {
1909 int startCount = constructionsCount;
1911 for (int i = startCount - 1; i >= 0
1912 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1914 constructionsCount--;
1916 if (constructionsCount > 0) {
1917 if (constructions[constructionsCount - 1] == BLOCK)
1919 constructionsCount--;
1924 * Pushes a block in the stack. <br>
1925 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element
1926 * is a <code>BLOCK</code>, pushes <code>NONINDENT_BLOCK</code>
1927 * otherwise. Creates a new bigger array if the current one is full.
1929 private int pushBlock() {
1931 if (constructionsCount == constructions.length)
1932 System.arraycopy(constructions, 0,
1933 (constructions = new int[constructionsCount * 2]), 0,
1934 constructionsCount);
1935 if ((constructionsCount == 0)
1936 || (constructions[constructionsCount - 1] == BLOCK)
1937 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK)
1938 || (constructions[constructionsCount - 1] == TokenNamecase)) {
1940 constructions[constructionsCount++] = BLOCK;
1942 constructions[constructionsCount++] = NONINDENT_BLOCK;
1947 * Pushes <code>token</code>.<br>
1948 * Creates a new bigger array if the current one is full.
1950 private int pushControlStatement(int token) {
1951 if (constructionsCount == constructions.length)
1952 System.arraycopy(constructions, 0,
1953 (constructions = new int[constructionsCount * 2]), 0,
1954 constructionsCount);
1955 constructions[constructionsCount++] = token;
1958 private static boolean separateFirstArgumentOn(int currentToken) {
1959 //return (currentToken == TokenNameCOMMA || currentToken ==
1960 // TokenNameSEMICOLON);
1961 return currentToken != TokenNameif && currentToken != TokenNameLPAREN
1962 && currentToken != TokenNameNOT && currentToken != TokenNamewhile
1963 && currentToken != TokenNamefor && currentToken != TokenNameswitch;
1966 * Set the positions to map. The mapped positions should be retrieved using
1967 * the getMappedPositions() method.
1971 * @deprecated Set the positions to map using the format(String, int, int[])
1974 * @see #getMappedPositions()
1976 public void setPositionsToMap(int[] positions) {
1977 positionsToMap = positions;
1980 mappedPositions = new int[positions.length];
1983 * Appends a space character to the current line buffer.
1985 private void space() {
1986 currentLineBuffer.append(' ');
1987 increaseLineDelta(1);
1990 * Splits <code>stringToSplit</code> on the top level token <br>
1991 * If there are several identical token at the same level, the string is cut
1994 * @return an object containing the operator and all the substrings or null
1995 * if the string cannot be split
1997 public SplitLine split(String stringToSplit) {
1998 return split(stringToSplit, 0);
2001 * Splits <code>stringToSplit</code> on the top level token <br>
2002 * If there are several identical token at the same level, the string is cut
2005 * @return an object containing the operator and all the substrings or null
2006 * if the string cannot be split
2008 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
2010 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and
2011 * http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
2013 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
2016 // split doesn't work correct for PHP
2019 // int currentToken = 0;
2020 // int splitTokenType = 0;
2021 // int splitTokenDepth = Integer.MAX_VALUE;
2022 // int splitTokenPriority = Integer.MAX_VALUE;
2023 // int[] substringsStartPositions = new int[10];
2024 // // contains the start position of substrings
2025 // int[] substringsEndPositions = new int[10];
2026 // // contains the start position of substrings
2027 // int substringsCount = 1; // index in the substringsStartPosition array
2028 // int[] splitOperators = new int[10];
2029 // // contains the start position of substrings
2030 // int splitOperatorsCount = 0; // index in the substringsStartPosition array
2031 // int[] openParenthesisPosition = new int[10];
2032 // int openParenthesisPositionCount = 0;
2033 // int position = 0;
2034 // int lastOpenParenthesisPosition = -1;
2035 // // used to remember the position of the 1st open parenthesis
2036 // // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
2037 // // setup the scanner with a new source
2038 // int lastCommentStartPosition = -1;
2039 // // to remember the start position of the last comment
2040 // int firstTokenOnLine = -1;
2041 // // to remember the first token of the line
2042 // int previousToken = -1;
2043 // // to remember the previous token.
2044 // splitScanner.setSource(stringToSplit.toCharArray());
2046 // // start the loop
2048 // // takes the next token
2050 // if (currentToken != Scanner.TokenNameWHITESPACE)
2051 // previousToken = currentToken;
2052 // currentToken = splitScanner.getNextToken();
2053 // if (Scanner.DEBUG) {
2054 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
2055 // int currentStartPosition = splitScanner
2056 // .getCurrentTokenStartPosition();
2057 // System.out.print(currentStartPosition + "," + currentEndPosition
2059 // System.out.println(scanner.toStringAction(currentToken));
2061 // } catch (InvalidInputException e) {
2062 // if (!handleInvalidToken(e))
2064 // currentToken = 0;
2065 // // this value is not modify when an exception is raised.
2067 // if (currentToken == TokenNameEOF)
2069 // if (firstTokenOnLine == -1) {
2070 // firstTokenOnLine = currentToken;
2072 // switch (currentToken) {
2073 // case TokenNameRBRACE :
2074 // case TokenNameRPAREN :
2075 // if (openParenthesisPositionCount > 0) {
2076 // if (openParenthesisPositionCount == 1
2077 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2078 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2079 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2080 // || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2081 // splitTokenType = 0;
2082 // splitTokenDepth = openParenthesisPositionCount;
2083 // splitTokenPriority = Integer.MAX_VALUE;
2084 // substringsStartPositions[0] = 0;
2085 // // better token means the whole line until now is the first
2087 // substringsCount = 1; // resets the count of substrings
2088 // substringsEndPositions[0] = openParenthesisPosition[0];
2089 // // substring ends on operator start
2090 // position = openParenthesisPosition[0];
2091 // // the string mustn't be cut before the closing parenthesis but
2092 // // after the opening one.
2093 // splitOperatorsCount = 1; // resets the count of split operators
2094 // splitOperators[0] = 0;
2096 // openParenthesisPositionCount--;
2099 // case TokenNameLBRACE :
2100 // case TokenNameLPAREN :
2101 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2104 // openParenthesisPosition,
2106 // (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2107 // 0, openParenthesisPositionCount);
2109 // openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2110 // if (currentToken == TokenNameLPAREN
2111 // && previousToken == TokenNameRPAREN) {
2112 // openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2115 // case TokenNameSEMICOLON :
2117 // case TokenNameCOMMA :
2119 // case TokenNameEQUAL :
2121 // if (openParenthesisPositionCount < splitTokenDepth
2122 // || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2123 // // the current token is better than the one we currently have
2124 // // (in level or in priority if same level)
2125 // // reset the substringsCount
2126 // splitTokenDepth = openParenthesisPositionCount;
2127 // splitTokenType = currentToken;
2128 // splitTokenPriority = getTokenPriority(currentToken);
2129 // substringsStartPositions[0] = 0;
2130 // // better token means the whole line until now is the first
2132 // if (separateFirstArgumentOn(firstTokenOnLine)
2133 // && openParenthesisPositionCount > 0) {
2134 // substringsCount = 2; // resets the count of substrings
2135 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2136 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2137 // substringsEndPositions[1] = splitScanner.startPosition;
2138 // splitOperatorsCount = 2; // resets the count of split operators
2139 // splitOperators[0] = 0;
2140 // splitOperators[1] = currentToken;
2141 // position = splitScanner.currentPosition;
2142 // // next substring will start from operator end
2144 // substringsCount = 1; // resets the count of substrings
2145 // substringsEndPositions[0] = splitScanner.startPosition;
2146 // // substring ends on operator start
2147 // position = splitScanner.currentPosition;
2148 // // next substring will start from operator end
2149 // splitOperatorsCount = 1; // resets the count of split operators
2150 // splitOperators[0] = currentToken;
2153 // if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2154 // && splitTokenType != TokenNameEQUAL
2155 // && currentToken != TokenNameEQUAL) {
2156 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2158 // // take only the 1st = into account.
2159 // // if another token with the same priority is found,
2160 // // push the start position of the substring and
2161 // // push the token into the stack.
2162 // // create a new array object if the current one is full.
2163 // if (substringsCount == substringsStartPositions.length) {
2166 // substringsStartPositions,
2168 // (substringsStartPositions = new int[substringsCount * 2]),
2169 // 0, substringsCount);
2170 // System.arraycopy(substringsEndPositions, 0,
2171 // (substringsEndPositions = new int[substringsCount * 2]),
2172 // 0, substringsCount);
2174 // if (splitOperatorsCount == splitOperators.length) {
2175 // System.arraycopy(splitOperators, 0,
2176 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2177 // splitOperatorsCount);
2179 // substringsStartPositions[substringsCount] = position;
2180 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2181 // // substring ends on operator start
2182 // position = splitScanner.currentPosition;
2183 // // next substring will start from operator end
2184 // splitOperators[splitOperatorsCount++] = currentToken;
2188 // case TokenNameCOLON :
2190 // // see 1FK7C5R, we only split on a colon, when it is associated
2191 // // with a question-mark.
2192 // // indeed it might appear also behind a case statement, and we do
2193 // // not to break at this point.
2194 // if ((splitOperatorsCount == 0)
2195 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2198 // case TokenNameextends :
2199 // case TokenNameimplements :
2200 // //case TokenNamethrows :
2201 // case TokenNameDOT :
2203 // case TokenNameMULTIPLY :
2205 // case TokenNameDIVIDE :
2207 // case TokenNameREMAINDER :
2209 // case TokenNamePLUS :
2210 // // + (15.17, 15.17.2)
2211 // case TokenNameMINUS :
2213 // case TokenNameLEFT_SHIFT :
2215 // case TokenNameRIGHT_SHIFT :
2217 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2218 // case TokenNameLESS :
2220 // case TokenNameLESS_EQUAL :
2222 // case TokenNameGREATER :
2224 // case TokenNameGREATER_EQUAL :
2226 // // case TokenNameinstanceof : // instanceof
2227 // case TokenNameEQUAL_EQUAL :
2228 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2229 // case TokenNameEQUAL_EQUAL_EQUAL :
2230 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2231 // case TokenNameNOT_EQUAL :
2232 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2233 // case TokenNameNOT_EQUAL_EQUAL :
2234 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2235 // case TokenNameAND :
2236 // // & (15.21, 15.21.1, 15.21.2)
2237 // case TokenNameOR :
2238 // // | (15.21, 15.21.1, 15.21.2)
2239 // case TokenNameXOR :
2240 // // ^ (15.21, 15.21.1, 15.21.2)
2241 // case TokenNameAND_AND :
2243 // case TokenNameOR_OR :
2245 // case TokenNameQUESTION :
2247 // case TokenNameMULTIPLY_EQUAL :
2249 // case TokenNameDIVIDE_EQUAL :
2251 // case TokenNameREMAINDER_EQUAL :
2253 // case TokenNamePLUS_EQUAL :
2255 // case TokenNameMINUS_EQUAL :
2257 // case TokenNameLEFT_SHIFT_EQUAL :
2259 // case TokenNameRIGHT_SHIFT_EQUAL :
2261 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2262 // case TokenNameAND_EQUAL :
2264 // case TokenNameXOR_EQUAL :
2266 // case TokenNameOR_EQUAL :
2268 // if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken)))
2269 // && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2270 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2271 // // the current token is better than the one we currently have
2272 // // (in level or in priority if same level)
2273 // // reset the substringsCount
2274 // splitTokenDepth = openParenthesisPositionCount;
2275 // splitTokenType = currentToken;
2276 // splitTokenPriority = getTokenPriority(currentToken);
2277 // substringsStartPositions[0] = 0;
2278 // // better token means the whole line until now is the first
2280 // if (separateFirstArgumentOn(firstTokenOnLine)
2281 // && openParenthesisPositionCount > 0) {
2282 // substringsCount = 2; // resets the count of substrings
2283 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2284 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2285 // substringsEndPositions[1] = splitScanner.startPosition;
2286 // splitOperatorsCount = 3; // resets the count of split operators
2287 // splitOperators[0] = 0;
2288 // splitOperators[1] = 0;
2289 // splitOperators[2] = currentToken;
2290 // position = splitScanner.currentPosition;
2291 // // next substring will start from operator end
2293 // substringsCount = 1; // resets the count of substrings
2294 // substringsEndPositions[0] = splitScanner.startPosition;
2295 // // substring ends on operator start
2296 // position = splitScanner.currentPosition;
2297 // // next substring will start from operator end
2298 // splitOperatorsCount = 2; // resets the count of split operators
2299 // splitOperators[0] = 0;
2300 // // nothing for first operand since operator will be inserted in
2301 // // front of the second operand
2302 // splitOperators[1] = currentToken;
2305 // if (openParenthesisPositionCount == splitTokenDepth
2306 // && splitTokenPriority == getTokenPriority(currentToken)) {
2307 // // if another token with the same priority is found,
2308 // // push the start position of the substring and
2309 // // push the token into the stack.
2310 // // create a new array object if the current one is full.
2311 // if (substringsCount == substringsStartPositions.length) {
2314 // substringsStartPositions,
2316 // (substringsStartPositions = new int[substringsCount * 2]),
2317 // 0, substringsCount);
2318 // System.arraycopy(substringsEndPositions, 0,
2319 // (substringsEndPositions = new int[substringsCount * 2]),
2320 // 0, substringsCount);
2322 // if (splitOperatorsCount == splitOperators.length) {
2323 // System.arraycopy(splitOperators, 0,
2324 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2325 // splitOperatorsCount);
2327 // substringsStartPositions[substringsCount] = position;
2328 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2329 // // substring ends on operator start
2330 // position = splitScanner.currentPosition;
2331 // // next substring will start from operator end
2332 // splitOperators[splitOperatorsCount++] = currentToken;
2338 // if (isComment(currentToken)) {
2339 // lastCommentStartPosition = splitScanner.startPosition;
2341 // lastCommentStartPosition = -1;
2344 // } catch (InvalidInputException e) {
2347 // // if the string cannot be split, return null.
2348 // if (splitOperatorsCount == 0)
2350 // // ## SPECIAL CASES BEGIN
2351 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2352 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2353 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2354 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) || (separateFirstArgumentOn(firstTokenOnLine)
2355 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2356 // && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2357 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2358 // // not be broken on two lines
2359 // // only one split on a top level .
2360 // // or more than one split on . and substring before open parenthesis fits
2362 // // or split inside parenthesis and first token is not a for/while/if
2363 // SplitLine sl = split(
2364 // stringToSplit.substring(lastOpenParenthesisPosition),
2365 // lastOpenParenthesisPosition);
2366 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2367 // // trim() is used to remove the extra blanks at the end of the
2368 // // substring. See PR 1FGYPI1
2369 // return new SplitLine(new int[]{0, 0}, new String[]{
2370 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2371 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2372 // offsetInGlobalLine,
2373 // lastOpenParenthesisPosition + offsetInGlobalLine});
2375 // // right substring can be split and is split on comma
2376 // // copy substrings and operators
2377 // // except if the 1st string is empty.
2378 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2379 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2380 // String[] result = new String[subStringsLength];
2381 // int[] startIndexes = new int[subStringsLength];
2382 // int operatorsLength = sl.operators.length + 1 - startIndex;
2383 // int[] operators = new int[operatorsLength];
2384 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2385 // operators[0] = 0;
2386 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2387 // 1, subStringsLength - 1);
2388 // for (int i = subStringsLength - 1; i >= 0; i--) {
2389 // startIndexes[i] += offsetInGlobalLine;
2391 // System.arraycopy(sl.substrings, startIndex, result, 1,
2392 // subStringsLength - 1);
2393 // System.arraycopy(sl.operators, startIndex, operators, 1,
2394 // operatorsLength - 1);
2395 // return new SplitLine(operators, result, startIndexes);
2398 // // if the last token is a comment and the substring before the comment fits
2400 // // split before the comment and return the result.
2401 // if (lastCommentStartPosition > -1
2402 // && lastCommentStartPosition < options.maxLineLength
2403 // && splitTokenPriority > 50) {
2404 // int end = lastCommentStartPosition;
2405 // int start = lastCommentStartPosition;
2406 // if (stringToSplit.charAt(end - 1) == ' ') {
2409 // if (start != end && stringToSplit.charAt(start) == ' ') {
2412 // return new SplitLine(new int[]{0, 0}, new String[]{
2413 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2414 // new int[]{0, start});
2416 // if (position != stringToSplit.length()) {
2417 // if (substringsCount == substringsStartPositions.length) {
2418 // System.arraycopy(substringsStartPositions, 0,
2419 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2420 // substringsCount);
2421 // System.arraycopy(substringsEndPositions, 0,
2422 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2423 // substringsCount);
2425 // // avoid empty extra substring, e.g. line terminated with a semi-colon
2426 // substringsStartPositions[substringsCount] = position;
2427 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2429 // if (splitOperatorsCount == splitOperators.length) {
2430 // System.arraycopy(splitOperators, 0,
2431 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2432 // splitOperatorsCount);
2434 // splitOperators[splitOperatorsCount] = 0;
2435 // // the last element of the stack is the position of the end of
2437 // // +1 because the substring method excludes the last character
2438 // String[] result = new String[substringsCount];
2439 // for (int i = 0; i < substringsCount; i++) {
2440 // int start = substringsStartPositions[i];
2441 // int end = substringsEndPositions[i];
2442 // if (stringToSplit.charAt(start) == ' ') {
2444 // substringsStartPositions[i]++;
2446 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2449 // result[i] = stringToSplit.substring(start, end);
2450 // substringsStartPositions[i] += offsetInGlobalLine;
2452 // if (splitOperatorsCount > substringsCount) {
2453 // System.arraycopy(substringsStartPositions, 0,
2454 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2455 // substringsCount);
2456 // System.arraycopy(substringsEndPositions, 0,
2457 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2458 // substringsCount);
2459 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2460 // substringsStartPositions[i] = position;
2461 // substringsEndPositions[i] = position;
2463 // System.arraycopy(splitOperators, 0,
2464 // (splitOperators = new int[splitOperatorsCount]), 0,
2465 // splitOperatorsCount);
2467 // System.arraycopy(substringsStartPositions, 0,
2468 // (substringsStartPositions = new int[substringsCount]), 0,
2469 // substringsCount);
2470 // System.arraycopy(substringsEndPositions, 0,
2471 // (substringsEndPositions = new int[substringsCount]), 0,
2472 // substringsCount);
2473 // System.arraycopy(splitOperators, 0,
2474 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2476 // SplitLine splitLine = new SplitLine(splitOperators, result,
2477 // substringsStartPositions);
2478 // return splitLine;
2480 private void updateMappedPositions(int startPosition) {
2481 if (positionsToMap == null) {
2484 char[] source = scanner.source;
2485 int sourceLength = source.length;
2486 while (indexToMap < positionsToMap.length
2487 && positionsToMap[indexToMap] <= startPosition) {
2488 int posToMap = positionsToMap[indexToMap];
2489 if (posToMap < 0 || posToMap >= sourceLength) {
2490 // protection against out of bounds position
2491 if (posToMap == sourceLength) {
2492 mappedPositions[indexToMap] = formattedSource.length();
2494 indexToMap = positionsToMap.length; // no more mapping
2497 if (CharOperation.isWhitespace(source[posToMap])) {
2498 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2500 if (posToMap == sourceLength - 1) {
2501 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2503 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2509 private void updateMappedPositionsWhileSplitting(int startPosition,
2511 if (mappedPositions == null || mappedPositions.length == indexInMap)
2513 while (indexInMap < mappedPositions.length
2514 && startPosition <= mappedPositions[indexInMap]
2515 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2516 mappedPositions[indexInMap] += splitDelta;
2520 private int getLength(String s, int tabDepth) {
2522 for (int i = 0; i < tabDepth; i++) {
2523 length += options.tabSize;
2525 for (int i = 0, max = s.length(); i < max; i++) {
2526 char currentChar = s.charAt(i);
2527 switch (currentChar) {
2529 length += options.tabSize;
2538 * Sets the initial indentation level
2540 * @param indentationLevel
2541 * new indentation level
2545 public void setInitialIndentationLevel(int newIndentationLevel) {
2546 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;