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 // to remind of the position of the beginning of the line.
172 splitScanner = new Scanner(true /* comment */
173 , true /* whitespace */
176 , true, /* tokenizeStrings */
178 // secondary scanner to split long lines formed by primary scanning
179 // initialize current line buffer
180 currentLineBuffer = new StringBuffer();
181 this.options = new FormatterOptions(settings);
185 * Returns true if a lineSeparator has to be inserted before <code>operator</code> false otherwise.
187 private static boolean breakLineBeforeOperator(int operator) {
190 case TokenNameSEMICOLON:
199 * @deprecated backport 1.0 internal functionality
201 private static Map convertConfigurableOptions(ConfigurableOption[] settings) {
202 Hashtable options = new Hashtable(10);
203 for (int i = 0; i < settings.length; i++) {
204 if (settings[i].getComponentName().equals(CodeFormatter.class.getName())) {
205 String optionName = settings[i].getOptionName();
206 int valueIndex = settings[i].getCurrentValueIndex();
207 if (optionName.equals("newline.openingBrace")) { //$NON-NLS-1$
208 options.put("net.sourceforge.phpdt.core.formatter.newline.openingBrace", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
209 } else if (optionName.equals("newline.controlStatement")) { //$NON-NLS-1$
211 .put("net.sourceforge.phpdt.core.formatter.newline.controlStatement", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
212 } else if (optionName.equals("newline.clearAll")) { //$NON-NLS-1$
213 options.put("net.sourceforge.phpdt.core.formatter.newline.clearAll", valueIndex == 0 ? "clear all" : "preserve one"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
214 } else if (optionName.equals("newline.elseIf")) { //$NON-NLS-1$
215 options.put("net.sourceforge.phpdt.core.formatter.newline.elseIf", valueIndex == 0 ? "do not insert" : "insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216 } else if (optionName.equals("newline.emptyBlock")) { //$NON-NLS-1$
217 options.put("net.sourceforge.phpdt.core.formatter.newline.emptyBlock", valueIndex == 0 ? "insert" : "do not insert"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
218 } else if (optionName.equals("lineSplit")) { //$NON-NLS-1$
219 options.put("net.sourceforge.phpdt.core.formatter.lineSplit", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
220 } else if (optionName.equals("style.assignment")) { //$NON-NLS-1$
221 options.put("net.sourceforge.phpdt.core.formatter.style.assignment", valueIndex == 0 ? "compact" : "normal"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
222 } else if (optionName.equals("tabulation.char")) { //$NON-NLS-1$
223 options.put("net.sourceforge.phpdt.core.formatter.tabulation.char", valueIndex == 0 ? "tab" : "space"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 } else if (optionName.equals("tabulation.size")) { //$NON-NLS-1$
225 options.put("net.sourceforge.phpdt.core.formatter.tabulation.size", String.valueOf(valueIndex)); //$NON-NLS-1$ //$NON-NLS-2$
233 * Returns the end of the source code.
235 private final String copyRemainingSource() {
236 char str[] = scanner.source;
237 int startPosition = scanner.startPosition;
238 int length = str.length - startPosition;
239 StringBuffer bufr = new StringBuffer(length);
240 if (startPosition < str.length) {
241 bufr.append(str, startPosition, length);
243 return (bufr.toString());
247 * Inserts <code>tabCount</code> tab character or their equivalent number of spaces.
249 private void dumpTab(int tabCount) {
250 if (options.indentWithTab) {
251 for (int j = 0; j < tabCount; j++) {
252 formattedSource.append('\t');
253 increaseSplitDelta(1);
256 for (int i = 0, max = options.tabSize * tabCount; i < max; i++) {
257 formattedSource.append(' ');
258 increaseSplitDelta(1);
264 * Dumps <code>currentLineBuffer</code> into the formatted string.
266 private void flushBuffer() {
267 String currentString = currentLineBuffer.toString();
269 beginningOfLineIndex = formattedSource.length();
270 if (containsOpenCloseBraces) {
271 containsOpenCloseBraces = false;
272 outputLine(currentString, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
273 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
275 outputLine(currentString, false, currentLineIndentationLevel, 0, -1, null, 0);
277 int scannerSourceLength = scanner.source.length;
278 if (scannerSourceLength > 2) {
279 if (scanner.source[scannerSourceLength - 1] == '\n' && scanner.source[scannerSourceLength - 2] == '\r') {
280 formattedSource.append(options.lineSeparatorSequence);
281 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
282 } else if (scanner.source[scannerSourceLength - 1] == '\n') {
283 formattedSource.append(options.lineSeparatorSequence);
284 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
285 } else if (scanner.source[scannerSourceLength - 1] == '\r') {
286 formattedSource.append(options.lineSeparatorSequence);
287 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
290 updateMappedPositions(scanner.startPosition);
294 * Formats the input string.
296 private void format() {
298 int previousToken = 0;
299 int previousCompilableToken = 0;
300 int indentationOffset = 0;
301 int newLinesInWhitespace = 0;
302 // number of new lines in the previous whitespace token
303 // (used to leave blank lines before comments)
304 int pendingNewLines = 0;
305 boolean expectingOpenBrace = false;
306 boolean clearNonBlockIndents = false;
307 // true if all indentations till the 1st { (usefull after } or ;)
308 boolean pendingSpace = true;
309 boolean pendingNewlineAfterParen = false;
310 // true when a cr is to be put after a ) (in conditional statements)
311 boolean inAssignment = false;
312 boolean inArrayAssignment = false;
313 boolean inThrowsClause = false;
314 boolean inClassOrInterfaceHeader = false;
315 int dollarBraceCount = 0;
316 // openBracketCount is used to count the number of open brackets not closed
318 int openBracketCount = 0;
319 int unarySignModifier = 0;
320 // openParenthesis[0] is used to count the parenthesis not belonging to a
322 // (eg foo();). parenthesis in for (...) are count elsewhere in the array.
323 int openParenthesisCount = 1;
324 int[] openParenthesis = new int[10];
325 // tokenBeforeColon is used to know what token goes along with the current
327 // it can be case or ?
328 int tokenBeforeColonCount = 0;
329 int[] tokenBeforeColon = new int[10];
330 constructionsCount = 0; // initializes the constructions count.
331 // contains DO if in a DO..WHILE statement, UNITIALIZED otherwise.
333 // fix for 1FF17XY: LFCOM:ALL - Format problem on not matching } and else
334 boolean specialElse = false;
335 // OPTION (IndentationLevel): initial indentation level may be non-zero.
336 currentLineIndentationLevel += constructionsCount;
337 // An InvalidInputException exception might cause the termination of this
341 // Get the next token. Catch invalid input and output it
342 // with minimal formatting, also catch end of input and
345 token = scanner.getNextToken();
347 int currentEndPosition = scanner.getCurrentTokenEndPosition();
348 int currentStartPosition = scanner.getCurrentTokenStartPosition();
349 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
350 System.out.println(scanner.toStringAction(token));
352 // Patch for line comment
353 // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=23096
354 if (token == ITerminalSymbols.TokenNameCOMMENT_LINE) {
355 int length = scanner.currentPosition;
356 loop: for (int index = length - 1; index >= 0; index--) {
357 switch (scanner.source[index]) {
360 scanner.currentPosition--;
367 } catch (InvalidInputException e) {
368 if (!handleInvalidToken(e)) {
373 if (token == Scanner.TokenNameEOF)
376 * ## MODIFYING the indentation level before generating new lines and indentation in the output string
378 // Removes all the indentations made by statements not followed by a
380 // except if the current token is ELSE, CATCH or if we are in a
382 if (clearNonBlockIndents && (token != Scanner.TokenNameWHITESPACE)) {
385 if (constructionsCount > 0 && constructions[constructionsCount - 1] == TokenNameelse) {
389 indentationLevel += popInclusiveUntil(TokenNameif);
391 // case TokenNamecatch :
392 // indentationLevel += popInclusiveUntil(TokenNamecatch);
394 // case TokenNamefinally :
395 // indentationLevel += popInclusiveUntil(TokenNamecatch);
398 if (nlicsToken == TokenNamedo) {
399 indentationLevel += pop(TokenNamedo);
403 indentationLevel += popExclusiveUntilBlockOrCase();
404 // clear until a CASE, DEFAULT or BLOCK is encountered.
405 // Thus, the indentationLevel is correctly cleared either
406 // in a switch/case statement or in any other situation.
408 clearNonBlockIndents = false;
410 // returns to the indentation level created by the SWITCH keyword
411 // if the current token is a CASE or a DEFAULT
412 if (token == TokenNamecase || token == TokenNamedefault) {
413 indentationLevel += pop(TokenNamecase);
415 // if (token == Scanner.TokenNamethrows) {
416 // inThrowsClause = true;
418 if ((token == Scanner.TokenNameclass || token == Scanner.TokenNameinterface) && previousToken != Scanner.TokenNameDOT) {
419 inClassOrInterfaceHeader = true;
422 * ## APPEND newlines and indentations to the output string
424 // Do not add a new line between ELSE and IF, if the option
425 // elseIfOnSameLine is true.
426 // Fix for 1ETLWPZ: IVJCOM:ALL - incorrect "else if" formatting
427 // if (pendingNewlineAfterParen
428 // && previousCompilableToken == TokenNameelse
429 // && token == TokenNameif
430 // && options.compactElseIfMode) {
431 // pendingNewlineAfterParen = false;
432 // pendingNewLines = 0;
433 // indentationLevel += pop(TokenNameelse);
434 // // because else if is now one single statement,
435 // // the indentation level after it is increased by one and not by 2
436 // // (else = 1 indent, if = 1 indent, but else if = 1 indent, not 2).
438 // Add a newline & indent to the formatted source string if
439 // a for/if-else/while statement was scanned and there is no block
441 pendingNewlineAfterParen = pendingNewlineAfterParen
442 || (previousCompilableToken == TokenNameRPAREN && token == TokenNameLBRACE);
443 if (pendingNewlineAfterParen && token != Scanner.TokenNameWHITESPACE) {
444 pendingNewlineAfterParen = false;
445 // Do to add a newline & indent sequence if the current token is an
446 // open brace or a period or if the current token is a semi-colon and
448 // previous token is a close paren.
449 // add a new line if a parenthesis belonging to a for() statement
450 // has been closed and the current token is not an opening brace
451 if (token != TokenNameLBRACE && !isComment(token)
452 // to avoid adding new line between else and a comment
453 && token != TokenNameDOT && !(previousCompilableToken == TokenNameRPAREN && token == TokenNameSEMICOLON)) {
455 currentLineIndentationLevel = indentationLevel;
457 pendingSpace = false;
459 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode) {
461 if (constructionsCount > 0 && constructions[constructionsCount - 1] != BLOCK
462 && constructions[constructionsCount - 1] != NONINDENT_BLOCK) {
463 currentLineIndentationLevel = indentationLevel - 1;
465 currentLineIndentationLevel = indentationLevel;
468 pendingSpace = false;
472 if (token == TokenNameLBRACE && options.newLineBeforeOpeningBraceMode && constructionsCount > 0
473 && constructions[constructionsCount - 1] == TokenNamedo) {
475 currentLineIndentationLevel = indentationLevel - 1;
477 pendingSpace = false;
480 if (token == TokenNameLBRACE && inThrowsClause) {
481 inThrowsClause = false;
482 if (options.newLineBeforeOpeningBraceMode) {
484 currentLineIndentationLevel = indentationLevel;
486 pendingSpace = false;
490 if (token == TokenNameLBRACE && inClassOrInterfaceHeader) {
491 inClassOrInterfaceHeader = false;
492 if (options.newLineBeforeOpeningBraceMode) {
494 currentLineIndentationLevel = indentationLevel;
496 pendingSpace = false;
499 // Add pending new lines to the formatted source string.
500 // Note: pending new lines are not added if the current token
501 // is a single line comment or whitespace.
502 // if the comment is between parenthesis, there is no blank line
504 // (if it's a one-line comment, a blank line is added after it).
505 if (((pendingNewLines > 0 && (!isComment(token)))
506 || (newLinesInWhitespace > 0 && (openParenthesisCount <= 1 && isComment(token))) || (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE))
507 && token != Scanner.TokenNameWHITESPACE) {
508 // Do not add newline & indent between an adjoining close brace and
509 // close paren. Anonymous inner classes may use this form.
510 boolean closeBraceAndCloseParen = previousToken == TokenNameRBRACE && token == TokenNameRPAREN;
511 // OPTION (NewLineInCompoundStatement): do not add newline & indent
512 // between close brace and else, (do) while, catch, and finally if
513 // newlineInCompoundStatement is true.
514 boolean nlicsOption = previousToken == TokenNameRBRACE
515 && !options.newlineInControlStatementMode
516 && (token == TokenNameelse || (token == TokenNamewhile && nlicsToken == TokenNamedo) || token == TokenNamecatch || token == TokenNamefinally);
517 // Do not add a newline & indent between a close brace and
519 boolean semiColonAndCloseBrace = previousToken == TokenNameRBRACE && token == TokenNameSEMICOLON;
520 // Do not add a new line & indent between a multiline comment and a
522 boolean commentAndOpenBrace = previousToken == Scanner.TokenNameCOMMENT_BLOCK && 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 && token == TokenNameCOMMA;
526 // Add a newline and indent, if appropriate.
528 || (!commentAndOpenBrace && !closeBraceAndCloseParen && !nlicsOption && !semiColonAndCloseBrace && !commaAndCloseBrace)) {
529 // if clearAllBlankLinesMode=false, leaves the blank lines
530 // inserted by the user
531 // if clearAllBlankLinesMode=true, removes all of then
532 // and insert only blank lines required by the formatting.
533 if (!options.clearAllBlankLinesMode) {
534 // (isComment(token))
535 pendingNewLines = (pendingNewLines < newLinesInWhitespace) ? newLinesInWhitespace : pendingNewLines;
536 pendingNewLines = (pendingNewLines > 2) ? 2 : pendingNewLines;
538 if (previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE) {
539 containsOpenCloseBraces = true;
540 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
541 if (isComment(previousToken)) {
542 newLine(pendingNewLines);
545 * if (!(constructionsCount > 1 && constructions[constructionsCount-1] == NONINDENT_BLOCK &&
546 * (constructions[constructionsCount-2] == TokenNamefor
548 if (options.newLineInEmptyBlockMode) {
549 if (inArrayAssignment) {
550 newLine(1); // array assigment with an empty block
552 newLine(pendingNewLines);
558 // see PR 1FKKC3U: LFCOM:WINNT - Format problem with a comment
560 if (!((previousToken == Scanner.TokenNameCOMMENT_BLOCK || previousToken == Scanner.TokenNameCOMMENT_PHPDOC) && token == TokenNameSEMICOLON)) {
561 newLine(pendingNewLines);
564 if (((previousCompilableToken == TokenNameSEMICOLON) || (previousCompilableToken == TokenNameLBRACE)
565 || (previousCompilableToken == TokenNameRBRACE) || (isComment(previousToken)))
566 && (token == TokenNameRBRACE)) {
567 indentationOffset = -1;
568 indentationLevel += popExclusiveUntilBlock();
570 if (previousToken == Scanner.TokenNameCOMMENT_LINE && inAssignment) {
572 currentLineIndentationLevel++;
574 currentLineIndentationLevel = indentationLevel + indentationOffset;
576 pendingSpace = false;
577 indentationOffset = 0;
580 newLinesInWhitespace = 0;
582 if (nlicsToken == TokenNamedo && token == TokenNamewhile) {
586 boolean phpTagAndWhitespace = previousToken == TokenNameINLINE_HTML && token == TokenNameWHITESPACE;
588 // case TokenNameDOLLAR :
589 // dollarBraceCount++;
592 // case TokenNamefinally :
593 expectingOpenBrace = true;
594 pendingNewlineAfterParen = true;
595 indentationLevel += pushControlStatement(token);
598 case TokenNamedefault:
599 if (tokenBeforeColonCount == tokenBeforeColon.length) {
601 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
603 tokenBeforeColon[tokenBeforeColonCount++] = TokenNamecase;
604 indentationLevel += pushControlStatement(TokenNamecase);
606 case TokenNameQUESTION:
607 if (tokenBeforeColonCount == tokenBeforeColon.length) {
609 .arraycopy(tokenBeforeColon, 0, (tokenBeforeColon = new int[tokenBeforeColonCount * 2]), 0, tokenBeforeColonCount);
611 tokenBeforeColon[tokenBeforeColonCount++] = token;
613 case TokenNameswitch:
617 if (openParenthesisCount == openParenthesis.length) {
618 System.arraycopy(openParenthesis, 0, (openParenthesis = new int[openParenthesisCount * 2]), 0, openParenthesisCount);
620 openParenthesis[openParenthesisCount++] = 0;
621 expectingOpenBrace = true;
622 indentationLevel += pushControlStatement(token);
625 pendingNewlineAfterParen = true;
627 // several CATCH statements can be contiguous.
628 // a CATCH is encountered pop until first CATCH (if a CATCH
629 // follows a TRY it works the same way,
630 // as CATCH and TRY are the same token in the stack).
631 expectingOpenBrace = true;
632 indentationLevel += pushControlStatement(TokenNamecatch);
635 expectingOpenBrace = true;
636 indentationLevel += pushControlStatement(token);
641 case TokenNameLPAREN:
642 // if (previousToken == TokenNamesynchronized) {
643 // indentationLevel += pushControlStatement(previousToken);
645 // Put a space between the previous and current token if the
646 // previous token was not a keyword, open paren, logical
647 // compliment (eg: !), semi-colon, open brace, close brace,
649 if (previousCompilableToken != TokenNameLBRACKET && previousToken != TokenNameIdentifier && previousToken != 0
650 && previousToken != TokenNameNOT && previousToken != TokenNameLPAREN && previousToken != TokenNameTWIDDLE
651 && previousToken != TokenNameSEMICOLON && previousToken != TokenNameLBRACE && previousToken != TokenNameRBRACE
652 && previousToken != TokenNamesuper) {
653 // && previousToken != TokenNamethis) {
656 // If in a for/if/while statement, increase the parenthesis count
657 // for the current openParenthesisCount
658 // else increase the count for stand alone parenthesis.
659 if (openParenthesisCount > 0)
660 openParenthesis[openParenthesisCount - 1]++;
662 openParenthesis[0]++;
663 pendingSpace = false;
666 case TokenNameRPAREN:
667 // Decrease the parenthesis count
668 // if there is no more unclosed parenthesis,
669 // a new line and indent may be append (depending on the next
671 if ((openParenthesisCount > 1) && (openParenthesis[openParenthesisCount - 1] > 0)) {
672 openParenthesis[openParenthesisCount - 1]--;
673 if (openParenthesis[openParenthesisCount - 1] <= 0) {
674 pendingNewlineAfterParen = true;
675 inAssignment = false;
676 openParenthesisCount--;
679 openParenthesis[0]--;
681 pendingSpace = false;
683 case TokenNameLBRACE:
684 if (previousCompilableToken == TokenNameDOLLAR) {
687 if ((previousCompilableToken == TokenNameRBRACKET) || (previousCompilableToken == TokenNameEQUAL)) {
688 // if (previousCompilableToken == TokenNameRBRACKET) {
689 inArrayAssignment = true;
690 inAssignment = false;
692 if (inArrayAssignment) {
693 indentationLevel += pushBlock();
695 // Add new line and increase indentation level after open brace.
697 indentationLevel += pushBlock();
701 case TokenNameRBRACE:
702 if (dollarBraceCount > 0) {
706 if (previousCompilableToken == TokenNameRPAREN) {
707 pendingSpace = false;
709 if (inArrayAssignment) {
710 inArrayAssignment = false;
712 indentationLevel += popInclusiveUntilBlock();
715 indentationLevel += popInclusiveUntilBlock();
716 if (previousCompilableToken == TokenNameRPAREN) {
717 // fix for 1FGDDV6: LFCOM:WIN98 - Weird splitting on message
719 currentLineBuffer.append(options.lineSeparatorSequence);
720 increaseLineDelta(options.lineSeparatorSequence.length);
722 if (constructionsCount > 0) {
723 switch (constructions[constructionsCount - 1]) {
725 //indentationLevel += popExclusiveUntilBlock();
727 case TokenNameswitch:
732 case TokenNamefinally:
735 // case TokenNamesynchronized :
736 clearNonBlockIndents = true;
743 case TokenNameLBRACKET:
745 pendingSpace = false;
747 case TokenNameRBRACKET:
748 openBracketCount -= (openBracketCount > 0) ? 1 : 0;
749 // if there is no left bracket to close, the right bracket is
751 pendingSpace = false;
755 pendingSpace = false;
757 case TokenNameSEMICOLON:
758 // Do not generate line terminators in the definition of
759 // the for statement.
760 // if not in this case, jump a line and reduce indentation after
762 // if the block it closes belongs to a conditional statement (if,
764 if (openParenthesisCount <= 1) {
766 if (expectingOpenBrace) {
767 clearNonBlockIndents = true;
768 expectingOpenBrace = false;
771 inAssignment = false;
772 pendingSpace = false;
774 case TokenNamePLUS_PLUS:
775 case TokenNameMINUS_MINUS:
776 // Do not put a space between a post-increment/decrement
777 // and the identifier being modified.
778 if (previousToken == TokenNameIdentifier || previousToken == TokenNameRBRACKET) {
779 pendingSpace = false;
783 // previously ADDITION
785 // Handle the unary operators plus and minus via a flag
786 if (!isLiteralToken(previousToken) && previousToken != TokenNameIdentifier && previousToken != TokenNameRPAREN
787 && previousToken != TokenNameRBRACKET) {
788 unarySignModifier = 1;
792 // In a switch/case statement, add a newline & indent
793 // when a colon is encountered.
794 if (tokenBeforeColonCount > 0) {
795 if (tokenBeforeColon[tokenBeforeColonCount - 1] == TokenNamecase) {
798 tokenBeforeColonCount--;
804 case Scanner.TokenNameCOMMENT_LINE:
807 currentLineIndentationLevel++;
809 break; // a line is always inserted after a one-line comment
810 case Scanner.TokenNameCOMMENT_PHPDOC:
811 case Scanner.TokenNameCOMMENT_BLOCK:
812 currentCommentOffset = getCurrentCommentOffset();
815 case Scanner.TokenNameWHITESPACE:
816 if (!phpTagAndWhitespace) {
817 // Count the number of line terminators in the whitespace so
818 // line spacing can be preserved near comments.
819 char[] source = scanner.source;
820 newLinesInWhitespace = 0;
821 for (int i = scanner.startPosition, max = scanner.currentPosition; i < max; i++) {
822 if (source[i] == '\r') {
824 if (source[++i] == '\n') {
825 newLinesInWhitespace++;
827 newLinesInWhitespace++;
830 newLinesInWhitespace++;
832 } else if (source[i] == '\n') {
833 newLinesInWhitespace++;
836 increaseLineDelta(scanner.startPosition - scanner.currentPosition);
839 // case TokenNameHTML :
840 // // Add the next token to the formatted source string.
841 // // outputCurrentToken(token);
842 // int startPosition = scanner.startPosition;
844 // for (int i = startPosition, max = scanner.currentPosition; i <
846 // char currentCharacter = scanner.source[i];
847 // updateMappedPositions(i);
848 // currentLineBuffer.append(currentCharacter);
852 if ((token == TokenNameIdentifier) || isLiteralToken(token) || token == TokenNamesuper) {
853 // || token == TokenNamethis) {
854 // Do not put a space between a unary operator
855 // (eg: ++, --, +, -) and the identifier being modified.
856 if (previousToken == TokenNamePLUS_PLUS || previousToken == TokenNameMINUS_MINUS
857 || (previousToken == TokenNameMINUS_GREATER && options.compactDereferencingMode) // ->
858 || (previousToken == TokenNamePLUS && unarySignModifier > 0)
859 || (previousToken == TokenNameMINUS && unarySignModifier > 0)) {
860 pendingSpace = false;
862 unarySignModifier = 0;
866 // Do not output whitespace tokens.
867 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
869 * Add pending space to the formatted source string. Do not output a space under the following circumstances: 1) this is
870 * the first pass 2) previous token is an open paren 3) previous token is a period 4) previous token is the logical
871 * compliment (eg: !) 5) previous token is the bitwise compliment (eg: ~) 6) previous token is the open bracket (eg: [) 7)
872 * in an assignment statement, if the previous token is an open brace or the current token is a close brace 8) previous
873 * token is a single line comment 9) current token is a '->'
875 if (token == TokenNameMINUS_GREATER && options.compactDereferencingMode)
876 pendingSpace = false;
878 boolean openAndCloseBrace = previousCompilableToken == TokenNameLBRACE && token == TokenNameRBRACE;
879 if (pendingSpace && insertSpaceAfter(previousToken)
880 && !(inAssignment && (previousToken == TokenNameLBRACE || token == TokenNameRBRACE))
881 && previousToken != Scanner.TokenNameCOMMENT_LINE) {
882 if ((!(options.compactAssignmentMode && token == TokenNameEQUAL)) && !openAndCloseBrace)
885 // Add the next token to the formatted source string.
886 outputCurrentToken(token);
887 if (token == Scanner.TokenNameCOMMENT_LINE && openParenthesisCount > 1) {
889 currentLineBuffer.append(options.lineSeparatorSequence);
890 increaseLineDelta(options.lineSeparatorSequence.length);
894 // Whitespace tokens do not need to be remembered.
895 if (token != Scanner.TokenNameWHITESPACE || phpTagAndWhitespace) {
896 previousToken = token;
897 if (token != Scanner.TokenNameCOMMENT_BLOCK && token != Scanner.TokenNameCOMMENT_LINE
898 && token != Scanner.TokenNameCOMMENT_PHPDOC) {
899 previousCompilableToken = token;
903 output(copyRemainingSource());
905 // dump the last token of the source in the formatted output.
906 } catch (InvalidInputException e) {
907 output(copyRemainingSource());
909 // dump the last token of the source in the formatted output.
914 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
916 * @return the formatted ouput.
918 public String formatSourceString(String sourceString) {
919 char[] sourceChars = sourceString.toCharArray();
920 formattedSource = new StringBuffer(sourceChars.length);
921 scanner.setSource(sourceChars);
923 return formattedSource.toString();
927 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version.
930 * the string to format
931 * @param indentationLevel
932 * the initial indentation level
933 * @return the formatted ouput.
935 public String format(String string, int indentationLevel) {
936 return format(string, indentationLevel, (int[]) null);
940 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The positions array
941 * is modified to contain the mapped positions.
944 * the string to format
945 * @param indentationLevel
946 * the initial indentation level
948 * the array of positions to map
949 * @return the formatted ouput.
951 public String format(String string, int indentationLevel, int[] positions) {
952 return this.format(string, indentationLevel, positions, null);
955 public String format(String string, int indentationLevel, int[] positions, String lineSeparator) {
956 if (lineSeparator != null) {
957 this.options.setLineSeparator(lineSeparator);
959 if (positions != null) {
960 this.setPositionsToMap(positions);
961 this.setInitialIndentationLevel(indentationLevel);
962 String formattedString = this.formatSourceString(string);
963 int[] mappedPositions = this.getMappedPositions();
964 System.arraycopy(mappedPositions, 0, positions, 0, positions.length);
965 return formattedString;
967 this.setInitialIndentationLevel(indentationLevel);
968 return this.formatSourceString(string);
973 * Formats the char array <code>sourceString</code>, and returns a string containing the formatted version. The initial
974 * indentation level is 0.
977 * the string to format
978 * @return the formatted ouput.
980 public String format(String string) {
981 return this.format(string, 0, (int[]) null);
985 * Formats a given source string, starting indenting it at a particular depth and using the given options
987 * @deprecated backport 1.0 internal functionality
989 public static String format(String sourceString, int initialIndentationLevel, ConfigurableOption[] options) {
990 CodeFormatter formatter = new CodeFormatter(options);
991 formatter.setInitialIndentationLevel(initialIndentationLevel);
992 return formatter.formatSourceString(sourceString);
996 * Returns the number of characters and tab char between the beginning of the line and the beginning of the comment.
998 private int getCurrentCommentOffset() {
999 int linePtr = scanner.linePtr;
1000 // if there is no beginning of line, return 0.
1004 int beginningOfLine = scanner.lineEnds[linePtr];
1005 int currentStartPosition = scanner.startPosition;
1006 char[] source = scanner.source;
1007 // find the position of the beginning of the line containing the comment
1008 while (beginningOfLine > currentStartPosition) {
1010 beginningOfLine = scanner.lineEnds[--linePtr];
1012 beginningOfLine = 0;
1016 for (int i = currentStartPosition - 1; i >= beginningOfLine; i--) {
1017 char currentCharacter = source[i];
1018 switch (currentCharacter) {
1020 offset += options.tabSize;
1036 * Returns an array of descriptions for the configurable options. The descriptions may be changed and passed back to a different
1039 * @deprecated backport 1.0 internal functionality
1041 public static ConfigurableOption[] getDefaultOptions(Locale locale) {
1042 String componentName = CodeFormatter.class.getName();
1043 FormatterOptions options = new FormatterOptions();
1044 return new ConfigurableOption[] {
1045 new ConfigurableOption(componentName, "newline.openingBrace", locale, options.newLineBeforeOpeningBraceMode ? 0 : 1),
1047 new ConfigurableOption(componentName, "newline.controlStatement", locale, options.newlineInControlStatementMode ? 0 : 1),
1049 new ConfigurableOption(componentName, "newline.clearAll", locale, options.clearAllBlankLinesMode ? 0 : 1),
1051 // new ConfigurableOption(componentName, "newline.elseIf", locale,
1052 // options.compactElseIfMode ? 0 : 1), //$NON-NLS-1$
1053 new ConfigurableOption(componentName, "newline.emptyBlock", locale, options.newLineInEmptyBlockMode ? 0 : 1),
1055 new ConfigurableOption(componentName, "line.split", locale, options.maxLineLength),
1057 new ConfigurableOption(componentName, "style.compactAssignment", locale, options.compactAssignmentMode ? 0 : 1),
1059 new ConfigurableOption(componentName, "tabulation.char", locale, options.indentWithTab ? 0 : 1),
1061 new ConfigurableOption(componentName, "tabulation.size", locale, options.tabSize) //$NON-NLS-1$
1066 * Returns the array of mapped positions. Returns null is no positions have been set.
1069 * @deprecated There is no need to retrieve the mapped positions anymore.
1071 public int[] getMappedPositions() {
1072 return mappedPositions;
1076 * Returns the priority of the token given as argument <br>
1077 * The most prioritary the token is, the smallest the return value is.
1079 * @return the priority of <code>token</code>
1081 * the token of which the priority is requested
1083 private static int getTokenPriority(int token) {
1085 case TokenNameextends:
1086 // case TokenNameimplements :
1087 // case TokenNamethrows :
1089 case TokenNameSEMICOLON:
1092 case TokenNameCOMMA:
1095 case TokenNameEQUAL:
1098 case TokenNameAND_AND:
1100 case TokenNameOR_OR:
1103 case TokenNameQUESTION:
1105 case TokenNameCOLON:
1107 return 50; // it's better cutting on ?: than on ;
1108 case TokenNameEQUAL_EQUAL:
1110 case TokenNameEQUAL_EQUAL_EQUAL:
1112 case TokenNameNOT_EQUAL:
1114 case TokenNameNOT_EQUAL_EQUAL:
1119 case TokenNameLESS_EQUAL:
1121 case TokenNameGREATER:
1123 case TokenNameGREATER_EQUAL:
1125 // case TokenNameinstanceof : // instanceof
1129 case TokenNameMINUS:
1132 case TokenNameMULTIPLY:
1134 case TokenNameDIVIDE:
1136 case TokenNameREMAINDER:
1139 case TokenNameLEFT_SHIFT:
1141 case TokenNameRIGHT_SHIFT:
1143 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>>
1152 case TokenNameMULTIPLY_EQUAL:
1154 case TokenNameDIVIDE_EQUAL:
1156 case TokenNameREMAINDER_EQUAL:
1158 case TokenNamePLUS_EQUAL:
1160 case TokenNameMINUS_EQUAL:
1162 case TokenNameLEFT_SHIFT_EQUAL:
1164 case TokenNameRIGHT_SHIFT_EQUAL:
1166 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>=
1167 case TokenNameAND_EQUAL:
1169 case TokenNameXOR_EQUAL:
1171 case TokenNameOR_EQUAL:
1173 case TokenNameDOT_EQUAL:
1180 return Integer.MAX_VALUE;
1185 * Handles the exception raised when an invalid token is encountered. Returns true if the exception has been handled, false
1188 private boolean handleInvalidToken(Exception e) {
1189 if (e.getMessage().equals(Scanner.INVALID_CHARACTER_CONSTANT) || e.getMessage().equals(Scanner.INVALID_CHAR_IN_STRING)
1190 || e.getMessage().equals(Scanner.INVALID_ESCAPE)) {
1196 private final void increaseGlobalDelta(int offset) {
1197 globalDelta += offset;
1200 private final void increaseLineDelta(int offset) {
1201 lineDelta += offset;
1204 private final void increaseSplitDelta(int offset) {
1205 splitDelta += offset;
1209 * Returns true if a space has to be inserted after <code>operator</code> false otherwise.
1211 private boolean insertSpaceAfter(int token) {
1213 case TokenNameLPAREN:
1215 case TokenNameTWIDDLE:
1219 case TokenNameWHITESPACE:
1220 case TokenNameLBRACKET:
1221 case TokenNameDOLLAR:
1222 case Scanner.TokenNameCOMMENT_LINE:
1230 * Returns true if a space has to be inserted before <code>operator</code> false otherwise. <br>
1231 * Cannot be static as it uses the code formatter options (to know if the compact assignment mode is on).
1233 private boolean insertSpaceBefore(int token) {
1235 case TokenNameEQUAL:
1236 return (!options.compactAssignmentMode);
1242 private static boolean isComment(int token) {
1243 boolean result = token == Scanner.TokenNameCOMMENT_BLOCK || token == Scanner.TokenNameCOMMENT_LINE
1244 || token == Scanner.TokenNameCOMMENT_PHPDOC;
1248 private static boolean isLiteralToken(int token) {
1249 boolean result = token == TokenNameIntegerLiteral
1250 // || token == TokenNameLongLiteral
1251 // || token == TokenNameFloatingPointLiteral
1252 || token == TokenNameDoubleLiteral
1253 // || token == TokenNameCharacterLiteral
1254 || token == TokenNameStringDoubleQuote;
1259 * If the length of <code>oneLineBuffer</code> exceeds <code>maxLineLength</code>, it is split and the result is dumped in
1260 * <code>formattedSource</code>
1262 * @param newLineCount
1263 * the number of new lines to append
1265 private void newLine(int newLineCount) {
1266 // format current line
1268 beginningOfLineIndex = formattedSource.length();
1269 String currentLine = currentLineBuffer.toString();
1270 if (containsOpenCloseBraces) {
1271 containsOpenCloseBraces = false;
1272 outputLine(currentLine, false, indentationLevelForOpenCloseBraces, 0, -1, null, 0);
1273 indentationLevelForOpenCloseBraces = currentLineIndentationLevel;
1275 outputLine(currentLine, false, currentLineIndentationLevel, 0, -1, null, 0);
1277 // dump line break(s)
1278 for (int i = 0; i < newLineCount; i++) {
1279 formattedSource.append(options.lineSeparatorSequence);
1280 increaseSplitDelta(options.lineSeparatorSequence.length);
1282 // reset formatter for next line
1283 int currentLength = currentLine.length();
1284 currentLineBuffer = new StringBuffer(currentLength > maxLineSize ? maxLineSize = currentLength : maxLineSize);
1285 increaseGlobalDelta(splitDelta);
1286 increaseGlobalDelta(lineDelta);
1288 currentLineIndentationLevel = initialIndentationLevel;
1291 private String operatorString(int operator) {
1293 case TokenNameextends:
1294 return "extends"; //$NON-NLS-1$
1295 // case TokenNameimplements :
1296 // return "implements"; //$NON-NLS-1$
1298 // case TokenNamethrows :
1299 // return "throws"; //$NON-NLS-1$
1300 case TokenNameSEMICOLON:
1302 return ";"; //$NON-NLS-1$
1303 case TokenNameCOMMA:
1305 return ","; //$NON-NLS-1$
1306 case TokenNameEQUAL:
1308 return "="; //$NON-NLS-1$
1309 case TokenNameAND_AND:
1311 return "&&"; //$NON-NLS-1$
1312 case TokenNameOR_OR:
1314 return "||"; //$NON-NLS-1$
1315 case TokenNameQUESTION:
1317 return "?"; //$NON-NLS-1$
1318 case TokenNameCOLON:
1320 return ":"; //$NON-NLS-1$
1321 case TokenNamePAAMAYIM_NEKUDOTAYIM:
1323 return "::"; //$NON-NLS-1$
1324 case TokenNameEQUAL_EQUAL:
1325 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1326 return "=="; //$NON-NLS-1$
1327 case TokenNameEQUAL_EQUAL_EQUAL:
1328 // == (15.20, 15.20.1, 15.20.2, 15.20.3)
1329 return "==="; //$NON-NLS-1$
1330 case TokenNameEQUAL_GREATER:
1332 return "=>"; //$NON-NLS-1$
1333 case TokenNameNOT_EQUAL:
1334 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1335 return "!="; //$NON-NLS-1$
1336 case TokenNameNOT_EQUAL_EQUAL:
1337 // != (15.20, 15.20.1, 15.20.2, 15.20.3)
1338 return "!=="; //$NON-NLS-1$
1341 return "<"; //$NON-NLS-1$
1342 case TokenNameLESS_EQUAL:
1344 return "<="; //$NON-NLS-1$
1345 case TokenNameGREATER:
1347 return ">"; //$NON-NLS-1$
1348 case TokenNameGREATER_EQUAL:
1350 return ">="; //$NON-NLS-1$
1351 // case TokenNameinstanceof : // instanceof
1352 // return "instanceof"; //$NON-NLS-1$
1354 // + (15.17, 15.17.2)
1355 return "+"; //$NON-NLS-1$
1356 case TokenNameMINUS:
1358 return "-"; //$NON-NLS-1$
1359 case TokenNameMULTIPLY:
1361 return "*"; //$NON-NLS-1$
1362 case TokenNameDIVIDE:
1364 return "/"; //$NON-NLS-1$
1365 case TokenNameREMAINDER:
1367 return "%"; //$NON-NLS-1$
1368 case TokenNameLEFT_SHIFT:
1370 return "<<"; //$NON-NLS-1$
1371 case TokenNameRIGHT_SHIFT:
1373 return ">>"; //$NON-NLS-1$
1374 // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
1375 // return ">>>"; //$NON-NLS-1$
1377 // & (15.21, 15.21.1, 15.21.2)
1378 return "&"; //$NON-NLS-1$
1380 // | (15.21, 15.21.1, 15.21.2)
1381 return "|"; //$NON-NLS-1$
1383 // ^ (15.21, 15.21.1, 15.21.2)
1384 return "^"; //$NON-NLS-1$
1385 case TokenNameMULTIPLY_EQUAL:
1387 return "*="; //$NON-NLS-1$
1388 case TokenNameDIVIDE_EQUAL:
1390 return "/="; //$NON-NLS-1$
1391 case TokenNameREMAINDER_EQUAL:
1393 return "%="; //$NON-NLS-1$
1394 case TokenNamePLUS_EQUAL:
1396 return "+="; //$NON-NLS-1$
1397 case TokenNameMINUS_EQUAL:
1399 return "-="; //$NON-NLS-1$
1400 case TokenNameMINUS_GREATER:
1402 return "->"; //$NON-NLS-1$
1403 case TokenNameLEFT_SHIFT_EQUAL:
1405 return "<<="; //$NON-NLS-1$
1406 case TokenNameRIGHT_SHIFT_EQUAL:
1408 return ">>="; //$NON-NLS-1$
1409 // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
1410 // return ">>>="; //$NON-NLS-1$
1411 case TokenNameAND_EQUAL:
1413 return "&="; //$NON-NLS-1$
1414 case TokenNameXOR_EQUAL:
1416 return "^="; //$NON-NLS-1$
1417 case TokenNameOR_EQUAL:
1419 return "|="; //$NON-NLS-1$
1420 case TokenNameDOT_EQUAL:
1422 return ".="; //$NON-NLS-1$
1425 return "."; //$NON-NLS-1$
1427 return ""; //$NON-NLS-1$
1432 * Appends <code>stringToOutput</code> to the formatted output. <br>
1433 * If it contains \n, append a LINE_SEPARATOR and indent after it.
1435 private void output(String stringToOutput) {
1436 char currentCharacter;
1437 for (int i = 0, max = stringToOutput.length(); i < max; i++) {
1438 currentCharacter = stringToOutput.charAt(i);
1439 if (currentCharacter != '\t') {
1440 currentLineBuffer.append(currentCharacter);
1446 * Appends <code>token</code> to the formatted output. <br>
1447 * If it contains <code>\n</code>, append a LINE_SEPARATOR and indent after it.
1449 private void outputCurrentToken(int token) {
1450 char[] source = scanner.source;
1451 int startPosition = scanner.startPosition;
1453 case Scanner.TokenNameCOMMENT_PHPDOC:
1454 case Scanner.TokenNameCOMMENT_BLOCK:
1455 case Scanner.TokenNameCOMMENT_LINE:
1456 boolean endOfLine = false;
1457 int currentCommentOffset = getCurrentCommentOffset();
1458 int beginningOfLineSpaces = 0;
1460 currentCommentOffset = getCurrentCommentOffset();
1461 beginningOfLineSpaces = 0;
1462 boolean pendingCarriageReturn = false;
1463 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1464 char currentCharacter = source[i];
1465 updateMappedPositions(i);
1466 switch (currentCharacter) {
1468 pendingCarriageReturn = true;
1472 if (pendingCarriageReturn) {
1473 increaseGlobalDelta(options.lineSeparatorSequence.length - 2);
1475 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1477 pendingCarriageReturn = false;
1478 currentLineBuffer.append(options.lineSeparatorSequence);
1479 beginningOfLineSpaces = 0;
1483 if (pendingCarriageReturn) {
1484 pendingCarriageReturn = false;
1485 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1486 currentLineBuffer.append(options.lineSeparatorSequence);
1487 beginningOfLineSpaces = 0;
1491 // we remove a maximum of currentCommentOffset characters (tabs
1492 // are converted to space numbers).
1493 beginningOfLineSpaces += options.tabSize;
1494 if (beginningOfLineSpaces > currentCommentOffset) {
1495 currentLineBuffer.append(currentCharacter);
1497 increaseGlobalDelta(-1);
1500 currentLineBuffer.append(currentCharacter);
1504 if (pendingCarriageReturn) {
1505 pendingCarriageReturn = false;
1506 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1507 currentLineBuffer.append(options.lineSeparatorSequence);
1508 beginningOfLineSpaces = 0;
1512 // we remove a maximum of currentCommentOffset characters (tabs
1513 // are converted to space numbers).
1514 beginningOfLineSpaces++;
1515 if (beginningOfLineSpaces > currentCommentOffset) {
1516 currentLineBuffer.append(currentCharacter);
1518 increaseGlobalDelta(-1);
1521 currentLineBuffer.append(currentCharacter);
1525 if (pendingCarriageReturn) {
1526 pendingCarriageReturn = false;
1527 increaseGlobalDelta(options.lineSeparatorSequence.length - 1);
1528 currentLineBuffer.append(options.lineSeparatorSequence);
1529 beginningOfLineSpaces = 0;
1532 beginningOfLineSpaces = 0;
1533 currentLineBuffer.append(currentCharacter);
1538 updateMappedPositions(scanner.currentPosition - 1);
1539 multipleLineCommentCounter++;
1542 for (int i = startPosition, max = scanner.currentPosition; i < max; i++) {
1543 char currentCharacter = source[i];
1544 updateMappedPositions(i);
1545 currentLineBuffer.append(currentCharacter);
1551 * Outputs <code>currentString</code>:<br>
1553 * <li>If its length is < maxLineLength, output
1554 * <li>Otherwise it is split.
1557 * @param currentString
1559 * @param preIndented
1560 * whether the string to output was pre-indented
1562 * number of indentation to put in front of <code>currentString</code>
1564 * value of the operator belonging to <code>currentString</code>.
1566 private void outputLine(String currentString, boolean preIndented, int depth, int operator, int substringIndex,
1567 int[] startSubstringIndexes, int offsetInGlobalLine) {
1568 boolean emptyFirstSubString = false;
1569 String operatorString = operatorString(operator);
1570 boolean placeOperatorBehind = !breakLineBeforeOperator(operator);
1571 boolean placeOperatorAhead = !placeOperatorBehind;
1572 // dump prefix operator?
1573 if (placeOperatorAhead) {
1578 if (operator != 0) {
1579 if (insertSpaceBefore(operator)) {
1580 formattedSource.append(' ');
1581 increaseSplitDelta(1);
1583 formattedSource.append(operatorString);
1584 increaseSplitDelta(operatorString.length());
1585 if (insertSpaceAfter(operator) && operator != TokenNameimplements && operator != TokenNameextends) {
1586 // && operator != TokenNamethrows) {
1587 formattedSource.append(' ');
1588 increaseSplitDelta(1);
1592 SplitLine splitLine = null;
1593 if (options.maxLineLength == 0 || getLength(currentString, depth) < options.maxLineLength
1594 || (splitLine = split(currentString, offsetInGlobalLine)) == null) {
1595 // depending on the type of operator, outputs new line before of after
1597 // indent before postfix operator
1598 // indent also when the line cannot be split
1599 if (operator == TokenNameextends || operator == TokenNameimplements) {
1600 // || operator == TokenNamethrows) {
1601 formattedSource.append(' ');
1602 increaseSplitDelta(1);
1604 if (placeOperatorBehind) {
1609 int max = currentString.length();
1610 if (multipleLineCommentCounter != 0) {
1612 BufferedReader reader = new BufferedReader(new StringReader(currentString));
1613 String line = reader.readLine();
1614 while (line != null) {
1615 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + line.length()
1616 + options.lineSeparatorSequence.length);
1617 formattedSource.append(line);
1618 beginningOfLineIndex = beginningOfLineIndex + line.length();
1619 if ((line = reader.readLine()) != null) {
1620 formattedSource.append(options.lineSeparatorSequence);
1621 beginningOfLineIndex += options.lineSeparatorSequence.length;
1622 dumpTab(currentLineIndentationLevel);
1626 } catch (IOException e) {
1627 e.printStackTrace();
1630 updateMappedPositionsWhileSplitting(beginningOfLineIndex, beginningOfLineIndex + max);
1631 for (int i = 0; i < max; i++) {
1632 char currentChar = currentString.charAt(i);
1633 switch (currentChar) {
1638 // fix for 1FFYL5C: LFCOM:ALL - Incorrect indentation when
1639 // split with a comment inside a condition
1640 // a substring cannot end with a lineSeparatorSequence,
1641 // except if it has been added by format() after a one-line
1643 formattedSource.append(options.lineSeparatorSequence);
1644 // 1FGDDV6: LFCOM:WIN98 - Weird splitting on message expression
1649 formattedSource.append(currentChar);
1653 // update positions inside the mappedPositions table
1654 if (substringIndex != -1) {
1655 if (multipleLineCommentCounter == 0) {
1656 int startPosition = beginningOfLineIndex + startSubstringIndexes[substringIndex];
1657 updateMappedPositionsWhileSplitting(startPosition, startPosition + max);
1659 // compute the splitDelta resulting with the operator and blank removal
1660 if (substringIndex + 1 != startSubstringIndexes.length) {
1661 increaseSplitDelta(startSubstringIndexes[substringIndex] + max - startSubstringIndexes[substringIndex + 1]);
1664 // dump postfix operator?
1665 if (placeOperatorBehind) {
1666 if (insertSpaceBefore(operator)) {
1667 formattedSource.append(' ');
1668 if (operator != 0) {
1669 increaseSplitDelta(1);
1672 formattedSource.append(operatorString);
1673 if (operator != 0) {
1674 increaseSplitDelta(operatorString.length());
1679 // fix for 1FG0BA3: LFCOM:WIN98 - Weird splitting on interfaces
1680 // extends has to stand alone on a line when currentString has been split.
1681 if (options.maxLineLength != 0 && splitLine != null && (operator == TokenNameextends)) {
1682 // || operator == TokenNameimplements
1683 // || operator == TokenNamethrows)) {
1684 formattedSource.append(options.lineSeparatorSequence);
1685 increaseSplitDelta(options.lineSeparatorSequence.length);
1688 if (operator == TokenNameextends) {
1689 // || operator == TokenNameimplements
1690 // || operator == TokenNamethrows) {
1691 formattedSource.append(' ');
1692 increaseSplitDelta(1);
1695 // perform actual splitting
1696 String result[] = splitLine.substrings;
1697 int[] splitOperators = splitLine.operators;
1698 if (result[0].length() == 0) {
1699 // when the substring 0 is null, the substring 1 is correctly indented.
1701 emptyFirstSubString = true;
1703 // the operator going in front of the result[0] string is the operator
1705 for (int i = 0, max = result.length; i < max; i++) {
1706 // the new depth is the current one if this is the first substring,
1707 // the current one + 1 otherwise.
1708 // if the substring is a comment, use the current indentation Level
1709 // instead of the depth
1710 // (-1 because the ouputline increases depth).
1711 // (fix for 1FFC72R: LFCOM:ALL - Incorrect line split in presence of line
1713 String currentResult = result[i];
1714 if (currentResult.length() != 0 || splitOperators[i] != 0) {
1715 int newDepth = (currentResult.startsWith("/*") //$NON-NLS-1$
1716 || currentResult.startsWith("//")) //$NON-NLS-1$
1717 ? indentationLevel - 1 : depth;
1718 outputLine(currentResult, i == 0 || (i == 1 && emptyFirstSubString) ? preIndented : false,
1719 i == 0 ? newDepth : newDepth + 1, splitOperators[i], i, splitLine.startSubstringsIndexes, currentString
1720 .indexOf(currentResult));
1722 formattedSource.append(options.lineSeparatorSequence);
1723 increaseSplitDelta(options.lineSeparatorSequence.length);
1727 if (result.length == splitOperators.length - 1) {
1728 int lastOperator = splitOperators[result.length];
1729 String lastOperatorString = operatorString(lastOperator);
1730 formattedSource.append(options.lineSeparatorSequence);
1731 increaseSplitDelta(options.lineSeparatorSequence.length);
1732 if (breakLineBeforeOperator(lastOperator)) {
1734 if (lastOperator != 0) {
1735 if (insertSpaceBefore(lastOperator)) {
1736 formattedSource.append(' ');
1737 increaseSplitDelta(1);
1739 formattedSource.append(lastOperatorString);
1740 increaseSplitDelta(lastOperatorString.length());
1741 if (insertSpaceAfter(lastOperator) && lastOperator != TokenNameimplements && lastOperator != TokenNameextends) {
1742 // && lastOperator != TokenNamethrows) {
1743 formattedSource.append(' ');
1744 increaseSplitDelta(1);
1749 if (placeOperatorBehind) {
1750 if (insertSpaceBefore(operator)) {
1751 formattedSource.append(' ');
1752 increaseSplitDelta(1);
1754 formattedSource.append(operatorString);
1755 //increaseSplitDelta(operatorString.length());
1760 * Pops the top statement of the stack if it is <code>token</code>
1762 private int pop(int token) {
1764 if ((constructionsCount > 0) && (constructions[constructionsCount - 1] == token)) {
1766 constructionsCount--;
1772 * Pops the top statement of the stack if it is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.
1774 private int popBlock() {
1776 if ((constructionsCount > 0)
1777 && ((constructions[constructionsCount - 1] == BLOCK) || (constructions[constructionsCount - 1] == NONINDENT_BLOCK))) {
1778 if (constructions[constructionsCount - 1] == BLOCK)
1780 constructionsCount--;
1786 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1787 * Does not remove <code>token</code> from the stack.
1790 * the token to be left as the top of the stack
1792 private int popExclusiveUntil(int token) {
1794 int startCount = constructionsCount;
1795 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1796 if (constructions[i] != NONINDENT_BLOCK)
1798 constructionsCount--;
1804 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1805 * Does not remove it from the stack.
1807 private int popExclusiveUntilBlock() {
1808 int startCount = constructionsCount;
1810 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK; i--) {
1811 constructionsCount--;
1818 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code>, a <code>NONINDENT_BLOCK</code> or a
1819 * <code>CASE</code>.<br>
1820 * Does not remove it from the stack.
1822 private int popExclusiveUntilBlockOrCase() {
1823 int startCount = constructionsCount;
1825 for (int i = startCount - 1; i >= 0 && constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK
1826 && constructions[i] != TokenNamecase; i--) {
1827 constructionsCount--;
1834 * Pops elements until the stack is empty or the top element is <code>token</code>.<br>
1835 * Removes <code>token</code> from the stack too.
1838 * the token to remove from the stack
1840 private int popInclusiveUntil(int token) {
1841 int startCount = constructionsCount;
1843 for (int i = startCount - 1; i >= 0 && constructions[i] != token; i--) {
1844 if (constructions[i] != NONINDENT_BLOCK)
1846 constructionsCount--;
1848 if (constructionsCount > 0) {
1849 if (constructions[constructionsCount - 1] != NONINDENT_BLOCK)
1851 constructionsCount--;
1857 * Pops elements until the stack is empty or the top element is a <code>BLOCK</code> or a <code>NONINDENT_BLOCK</code>.<br>
1858 * Does not remove it from the stack.
1860 private int popInclusiveUntilBlock() {
1861 int startCount = constructionsCount;
1863 for (int i = startCount - 1; i >= 0 && (constructions[i] != BLOCK && constructions[i] != NONINDENT_BLOCK); i--) {
1865 constructionsCount--;
1867 if (constructionsCount > 0) {
1868 if (constructions[constructionsCount - 1] == BLOCK)
1870 constructionsCount--;
1876 * Pushes a block in the stack. <br>
1877 * Pushes a <code>BLOCK</code> if the stack is empty or if the top element is a <code>BLOCK</code>, pushes
1878 * <code>NONINDENT_BLOCK</code> otherwise. Creates a new bigger array if the current one is full.
1880 private int pushBlock() {
1882 if (constructionsCount == constructions.length)
1883 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1884 if ((constructionsCount == 0) || (constructions[constructionsCount - 1] == BLOCK)
1885 || (constructions[constructionsCount - 1] == NONINDENT_BLOCK) || (constructions[constructionsCount - 1] == TokenNamecase)) {
1887 constructions[constructionsCount++] = BLOCK;
1889 constructions[constructionsCount++] = NONINDENT_BLOCK;
1895 * Pushes <code>token</code>.<br>
1896 * Creates a new bigger array if the current one is full.
1898 private int pushControlStatement(int token) {
1899 if (constructionsCount == constructions.length)
1900 System.arraycopy(constructions, 0, (constructions = new int[constructionsCount * 2]), 0, constructionsCount);
1901 constructions[constructionsCount++] = token;
1905 private static boolean separateFirstArgumentOn(int currentToken) {
1906 //return (currentToken == TokenNameCOMMA || currentToken ==
1907 // TokenNameSEMICOLON);
1908 return currentToken != TokenNameif && currentToken != TokenNameLPAREN && currentToken != TokenNameNOT
1909 && currentToken != TokenNamewhile && currentToken != TokenNamefor && currentToken != TokenNameswitch;
1913 * Set the positions to map. The mapped positions should be retrieved using the getMappedPositions() method.
1917 * @deprecated Set the positions to map using the format(String, int, int[]) method.
1919 * @see #getMappedPositions()
1921 public void setPositionsToMap(int[] positions) {
1922 positionsToMap = positions;
1925 mappedPositions = new int[positions.length];
1929 * Appends a space character to the current line buffer.
1931 private void space() {
1932 currentLineBuffer.append(' ');
1933 increaseLineDelta(1);
1937 * Splits <code>stringToSplit</code> on the top level token <br>
1938 * If there are several identical token at the same level, the string is cut into many pieces.
1940 * @return an object containing the operator and all the substrings or null if the string cannot be split
1942 public SplitLine split(String stringToSplit) {
1943 return split(stringToSplit, 0);
1947 * Splits <code>stringToSplit</code> on the top level token <br>
1948 * If there are several identical token at the same level, the string is cut into many pieces.
1950 * @return an object containing the operator and all the substrings or null if the string cannot be split
1952 public SplitLine split(String stringToSplit, int offsetInGlobalLine) {
1954 * See http://dev.eclipse.org/bugs/show_bug.cgi?id=12540 and http://dev.eclipse.org/bugs/show_bug.cgi?id=14387
1956 if (stringToSplit.indexOf("//$NON-NLS") != -1) { //$NON-NLS-1$
1959 // split doesn't work correct for PHP
1962 // int currentToken = 0;
1963 // int splitTokenType = 0;
1964 // int splitTokenDepth = Integer.MAX_VALUE;
1965 // int splitTokenPriority = Integer.MAX_VALUE;
1966 // int[] substringsStartPositions = new int[10];
1967 // // contains the start position of substrings
1968 // int[] substringsEndPositions = new int[10];
1969 // // contains the start position of substrings
1970 // int substringsCount = 1; // index in the substringsStartPosition array
1971 // int[] splitOperators = new int[10];
1972 // // contains the start position of substrings
1973 // int splitOperatorsCount = 0; // index in the substringsStartPosition array
1974 // int[] openParenthesisPosition = new int[10];
1975 // int openParenthesisPositionCount = 0;
1976 // int position = 0;
1977 // int lastOpenParenthesisPosition = -1;
1978 // // used to remember the position of the 1st open parenthesis
1979 // // needed for a pattern like: A.B(C); we want formatted like A.B( split C);
1980 // // setup the scanner with a new source
1981 // int lastCommentStartPosition = -1;
1982 // // to remember the start position of the last comment
1983 // int firstTokenOnLine = -1;
1984 // // to remember the first token of the line
1985 // int previousToken = -1;
1986 // // to remember the previous token.
1987 // splitScanner.setSource(stringToSplit.toCharArray());
1989 // // start the loop
1991 // // takes the next token
1993 // if (currentToken != Scanner.TokenNameWHITESPACE)
1994 // previousToken = currentToken;
1995 // currentToken = splitScanner.getNextToken();
1996 // if (Scanner.DEBUG) {
1997 // int currentEndPosition = splitScanner.getCurrentTokenEndPosition();
1998 // int currentStartPosition = splitScanner
1999 // .getCurrentTokenStartPosition();
2000 // System.out.print(currentStartPosition + "," + currentEndPosition
2002 // System.out.println(scanner.toStringAction(currentToken));
2004 // } catch (InvalidInputException e) {
2005 // if (!handleInvalidToken(e))
2007 // currentToken = 0;
2008 // // this value is not modify when an exception is raised.
2010 // if (currentToken == TokenNameEOF)
2012 // if (firstTokenOnLine == -1) {
2013 // firstTokenOnLine = currentToken;
2015 // switch (currentToken) {
2016 // case TokenNameRBRACE :
2017 // case TokenNameRPAREN :
2018 // if (openParenthesisPositionCount > 0) {
2019 // if (openParenthesisPositionCount == 1
2020 // && lastOpenParenthesisPosition < openParenthesisPosition[0]) {
2021 // lastOpenParenthesisPosition = openParenthesisPosition[0];
2022 // } else if ((splitTokenDepth == Integer.MAX_VALUE)
2023 // || (splitTokenDepth > openParenthesisPositionCount && openParenthesisPositionCount == 1)) {
2024 // splitTokenType = 0;
2025 // splitTokenDepth = openParenthesisPositionCount;
2026 // splitTokenPriority = Integer.MAX_VALUE;
2027 // substringsStartPositions[0] = 0;
2028 // // better token means the whole line until now is the first
2030 // substringsCount = 1; // resets the count of substrings
2031 // substringsEndPositions[0] = openParenthesisPosition[0];
2032 // // substring ends on operator start
2033 // position = openParenthesisPosition[0];
2034 // // the string mustn't be cut before the closing parenthesis but
2035 // // after the opening one.
2036 // splitOperatorsCount = 1; // resets the count of split operators
2037 // splitOperators[0] = 0;
2039 // openParenthesisPositionCount--;
2042 // case TokenNameLBRACE :
2043 // case TokenNameLPAREN :
2044 // if (openParenthesisPositionCount == openParenthesisPosition.length) {
2047 // openParenthesisPosition,
2049 // (openParenthesisPosition = new int[openParenthesisPositionCount * 2]),
2050 // 0, openParenthesisPositionCount);
2052 // openParenthesisPosition[openParenthesisPositionCount++] = splitScanner.currentPosition;
2053 // if (currentToken == TokenNameLPAREN
2054 // && previousToken == TokenNameRPAREN) {
2055 // openParenthesisPosition[openParenthesisPositionCount - 1] = splitScanner.startPosition;
2058 // case TokenNameSEMICOLON :
2060 // case TokenNameCOMMA :
2062 // case TokenNameEQUAL :
2064 // if (openParenthesisPositionCount < splitTokenDepth
2065 // || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority > getTokenPriority(currentToken))) {
2066 // // the current token is better than the one we currently have
2067 // // (in level or in priority if same level)
2068 // // reset the substringsCount
2069 // splitTokenDepth = openParenthesisPositionCount;
2070 // splitTokenType = currentToken;
2071 // splitTokenPriority = getTokenPriority(currentToken);
2072 // substringsStartPositions[0] = 0;
2073 // // better token means the whole line until now is the first
2075 // if (separateFirstArgumentOn(firstTokenOnLine)
2076 // && openParenthesisPositionCount > 0) {
2077 // substringsCount = 2; // resets the count of substrings
2078 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2079 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2080 // substringsEndPositions[1] = splitScanner.startPosition;
2081 // splitOperatorsCount = 2; // resets the count of split operators
2082 // splitOperators[0] = 0;
2083 // splitOperators[1] = currentToken;
2084 // position = splitScanner.currentPosition;
2085 // // next substring will start from operator end
2087 // substringsCount = 1; // resets the count of substrings
2088 // substringsEndPositions[0] = splitScanner.startPosition;
2089 // // substring ends on operator start
2090 // position = splitScanner.currentPosition;
2091 // // next substring will start from operator end
2092 // splitOperatorsCount = 1; // resets the count of split operators
2093 // splitOperators[0] = currentToken;
2096 // if ((openParenthesisPositionCount == splitTokenDepth && splitTokenPriority == getTokenPriority(currentToken))
2097 // && splitTokenType != TokenNameEQUAL
2098 // && currentToken != TokenNameEQUAL) {
2099 // // fix for 1FG0BCN: LFCOM:WIN98 - Missing one indentation after
2101 // // take only the 1st = into account.
2102 // // if another token with the same priority is found,
2103 // // push the start position of the substring and
2104 // // push the token into the stack.
2105 // // create a new array object if the current one is full.
2106 // if (substringsCount == substringsStartPositions.length) {
2109 // substringsStartPositions,
2111 // (substringsStartPositions = new int[substringsCount * 2]),
2112 // 0, substringsCount);
2113 // System.arraycopy(substringsEndPositions, 0,
2114 // (substringsEndPositions = new int[substringsCount * 2]),
2115 // 0, substringsCount);
2117 // if (splitOperatorsCount == splitOperators.length) {
2118 // System.arraycopy(splitOperators, 0,
2119 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2120 // splitOperatorsCount);
2122 // substringsStartPositions[substringsCount] = position;
2123 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2124 // // substring ends on operator start
2125 // position = splitScanner.currentPosition;
2126 // // next substring will start from operator end
2127 // splitOperators[splitOperatorsCount++] = currentToken;
2131 // case TokenNameCOLON :
2133 // // see 1FK7C5R, we only split on a colon, when it is associated
2134 // // with a question-mark.
2135 // // indeed it might appear also behind a case statement, and we do
2136 // // not to break at this point.
2137 // if ((splitOperatorsCount == 0)
2138 // || splitOperators[splitOperatorsCount - 1] != TokenNameQUESTION) {
2141 // case TokenNameextends :
2142 // case TokenNameimplements :
2143 // //case TokenNamethrows :
2144 // case TokenNameDOT :
2146 // case TokenNameMULTIPLY :
2148 // case TokenNameDIVIDE :
2150 // case TokenNameREMAINDER :
2152 // case TokenNamePLUS :
2153 // // + (15.17, 15.17.2)
2154 // case TokenNameMINUS :
2156 // case TokenNameLEFT_SHIFT :
2158 // case TokenNameRIGHT_SHIFT :
2160 // // case TokenNameUNSIGNED_RIGHT_SHIFT : // >>> (15.18)
2161 // case TokenNameLESS :
2163 // case TokenNameLESS_EQUAL :
2165 // case TokenNameGREATER :
2167 // case TokenNameGREATER_EQUAL :
2169 // // case TokenNameinstanceof : // instanceof
2170 // case TokenNameEQUAL_EQUAL :
2171 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2172 // case TokenNameEQUAL_EQUAL_EQUAL :
2173 // // == (15.20, 15.20.1, 15.20.2, 15.20.3)
2174 // case TokenNameNOT_EQUAL :
2175 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2176 // case TokenNameNOT_EQUAL_EQUAL :
2177 // // != (15.20, 15.20.1, 15.20.2, 15.20.3)
2178 // case TokenNameAND :
2179 // // & (15.21, 15.21.1, 15.21.2)
2180 // case TokenNameOR :
2181 // // | (15.21, 15.21.1, 15.21.2)
2182 // case TokenNameXOR :
2183 // // ^ (15.21, 15.21.1, 15.21.2)
2184 // case TokenNameAND_AND :
2186 // case TokenNameOR_OR :
2188 // case TokenNameQUESTION :
2190 // case TokenNameMULTIPLY_EQUAL :
2192 // case TokenNameDIVIDE_EQUAL :
2194 // case TokenNameREMAINDER_EQUAL :
2196 // case TokenNamePLUS_EQUAL :
2198 // case TokenNameMINUS_EQUAL :
2200 // case TokenNameLEFT_SHIFT_EQUAL :
2202 // case TokenNameRIGHT_SHIFT_EQUAL :
2204 // // case TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL : // >>>= (15.25.2)
2205 // case TokenNameAND_EQUAL :
2207 // case TokenNameXOR_EQUAL :
2209 // case TokenNameOR_EQUAL :
2211 // if ((openParenthesisPositionCount < splitTokenDepth || (openParenthesisPositionCount == splitTokenDepth && splitTokenPriority
2212 // > getTokenPriority(currentToken)))
2213 // && !((currentToken == TokenNamePLUS || currentToken == TokenNameMINUS) && (previousToken == TokenNameLBRACE
2214 // || previousToken == TokenNameLBRACKET || splitScanner.startPosition == 0))) {
2215 // // the current token is better than the one we currently have
2216 // // (in level or in priority if same level)
2217 // // reset the substringsCount
2218 // splitTokenDepth = openParenthesisPositionCount;
2219 // splitTokenType = currentToken;
2220 // splitTokenPriority = getTokenPriority(currentToken);
2221 // substringsStartPositions[0] = 0;
2222 // // better token means the whole line until now is the first
2224 // if (separateFirstArgumentOn(firstTokenOnLine)
2225 // && openParenthesisPositionCount > 0) {
2226 // substringsCount = 2; // resets the count of substrings
2227 // substringsEndPositions[0] = openParenthesisPosition[splitTokenDepth - 1];
2228 // substringsStartPositions[1] = openParenthesisPosition[splitTokenDepth - 1];
2229 // substringsEndPositions[1] = splitScanner.startPosition;
2230 // splitOperatorsCount = 3; // resets the count of split operators
2231 // splitOperators[0] = 0;
2232 // splitOperators[1] = 0;
2233 // splitOperators[2] = currentToken;
2234 // position = splitScanner.currentPosition;
2235 // // next substring will start from operator end
2237 // substringsCount = 1; // resets the count of substrings
2238 // substringsEndPositions[0] = splitScanner.startPosition;
2239 // // substring ends on operator start
2240 // position = splitScanner.currentPosition;
2241 // // next substring will start from operator end
2242 // splitOperatorsCount = 2; // resets the count of split operators
2243 // splitOperators[0] = 0;
2244 // // nothing for first operand since operator will be inserted in
2245 // // front of the second operand
2246 // splitOperators[1] = currentToken;
2249 // if (openParenthesisPositionCount == splitTokenDepth
2250 // && splitTokenPriority == getTokenPriority(currentToken)) {
2251 // // if another token with the same priority is found,
2252 // // push the start position of the substring and
2253 // // push the token into the stack.
2254 // // create a new array object if the current one is full.
2255 // if (substringsCount == substringsStartPositions.length) {
2258 // substringsStartPositions,
2260 // (substringsStartPositions = new int[substringsCount * 2]),
2261 // 0, substringsCount);
2262 // System.arraycopy(substringsEndPositions, 0,
2263 // (substringsEndPositions = new int[substringsCount * 2]),
2264 // 0, substringsCount);
2266 // if (splitOperatorsCount == splitOperators.length) {
2267 // System.arraycopy(splitOperators, 0,
2268 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2269 // splitOperatorsCount);
2271 // substringsStartPositions[substringsCount] = position;
2272 // substringsEndPositions[substringsCount++] = splitScanner.startPosition;
2273 // // substring ends on operator start
2274 // position = splitScanner.currentPosition;
2275 // // next substring will start from operator end
2276 // splitOperators[splitOperatorsCount++] = currentToken;
2282 // if (isComment(currentToken)) {
2283 // lastCommentStartPosition = splitScanner.startPosition;
2285 // lastCommentStartPosition = -1;
2288 // } catch (InvalidInputException e) {
2291 // // if the string cannot be split, return null.
2292 // if (splitOperatorsCount == 0)
2294 // // ## SPECIAL CASES BEGIN
2295 // if (((splitOperatorsCount == 2 && splitOperators[1] == TokenNameDOT
2296 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1)
2297 // || (splitOperatorsCount > 2 && splitOperators[1] == TokenNameDOT
2298 // && splitTokenDepth == 0 && lastOpenParenthesisPosition > -1 && lastOpenParenthesisPosition <= options.maxLineLength) ||
2299 // (separateFirstArgumentOn(firstTokenOnLine)
2300 // && splitTokenDepth > 0 && lastOpenParenthesisPosition > -1))
2301 // && (lastOpenParenthesisPosition < splitScanner.source.length && splitScanner.source[lastOpenParenthesisPosition] != ')')) {
2302 // // fix for 1FH4J2H: LFCOM:WINNT - Formatter - Empty parenthesis should
2303 // // not be broken on two lines
2304 // // only one split on a top level .
2305 // // or more than one split on . and substring before open parenthesis fits
2307 // // or split inside parenthesis and first token is not a for/while/if
2308 // SplitLine sl = split(
2309 // stringToSplit.substring(lastOpenParenthesisPosition),
2310 // lastOpenParenthesisPosition);
2311 // if (sl == null || sl.operators[0] != TokenNameCOMMA) {
2312 // // trim() is used to remove the extra blanks at the end of the
2313 // // substring. See PR 1FGYPI1
2314 // return new SplitLine(new int[]{0, 0}, new String[]{
2315 // stringToSplit.substring(0, lastOpenParenthesisPosition).trim(),
2316 // stringToSplit.substring(lastOpenParenthesisPosition)}, new int[]{
2317 // offsetInGlobalLine,
2318 // lastOpenParenthesisPosition + offsetInGlobalLine});
2320 // // right substring can be split and is split on comma
2321 // // copy substrings and operators
2322 // // except if the 1st string is empty.
2323 // int startIndex = (sl.substrings[0].length() == 0) ? 1 : 0;
2324 // int subStringsLength = sl.substrings.length + 1 - startIndex;
2325 // String[] result = new String[subStringsLength];
2326 // int[] startIndexes = new int[subStringsLength];
2327 // int operatorsLength = sl.operators.length + 1 - startIndex;
2328 // int[] operators = new int[operatorsLength];
2329 // result[0] = stringToSplit.substring(0, lastOpenParenthesisPosition);
2330 // operators[0] = 0;
2331 // System.arraycopy(sl.startSubstringsIndexes, startIndex, startIndexes,
2332 // 1, subStringsLength - 1);
2333 // for (int i = subStringsLength - 1; i >= 0; i--) {
2334 // startIndexes[i] += offsetInGlobalLine;
2336 // System.arraycopy(sl.substrings, startIndex, result, 1,
2337 // subStringsLength - 1);
2338 // System.arraycopy(sl.operators, startIndex, operators, 1,
2339 // operatorsLength - 1);
2340 // return new SplitLine(operators, result, startIndexes);
2343 // // if the last token is a comment and the substring before the comment fits
2345 // // split before the comment and return the result.
2346 // if (lastCommentStartPosition > -1
2347 // && lastCommentStartPosition < options.maxLineLength
2348 // && splitTokenPriority > 50) {
2349 // int end = lastCommentStartPosition;
2350 // int start = lastCommentStartPosition;
2351 // if (stringToSplit.charAt(end - 1) == ' ') {
2354 // if (start != end && stringToSplit.charAt(start) == ' ') {
2357 // return new SplitLine(new int[]{0, 0}, new String[]{
2358 // stringToSplit.substring(0, end), stringToSplit.substring(start)},
2359 // new int[]{0, start});
2361 // if (position != stringToSplit.length()) {
2362 // if (substringsCount == substringsStartPositions.length) {
2363 // System.arraycopy(substringsStartPositions, 0,
2364 // (substringsStartPositions = new int[substringsCount * 2]), 0,
2365 // substringsCount);
2366 // System.arraycopy(substringsEndPositions, 0,
2367 // (substringsEndPositions = new int[substringsCount * 2]), 0,
2368 // substringsCount);
2370 // // avoid empty extra substring, e.g. line terminated with a semi-colon
2371 // substringsStartPositions[substringsCount] = position;
2372 // substringsEndPositions[substringsCount++] = stringToSplit.length();
2374 // if (splitOperatorsCount == splitOperators.length) {
2375 // System.arraycopy(splitOperators, 0,
2376 // (splitOperators = new int[splitOperatorsCount * 2]), 0,
2377 // splitOperatorsCount);
2379 // splitOperators[splitOperatorsCount] = 0;
2380 // // the last element of the stack is the position of the end of
2382 // // +1 because the substring method excludes the last character
2383 // String[] result = new String[substringsCount];
2384 // for (int i = 0; i < substringsCount; i++) {
2385 // int start = substringsStartPositions[i];
2386 // int end = substringsEndPositions[i];
2387 // if (stringToSplit.charAt(start) == ' ') {
2389 // substringsStartPositions[i]++;
2391 // if (end != start && stringToSplit.charAt(end - 1) == ' ') {
2394 // result[i] = stringToSplit.substring(start, end);
2395 // substringsStartPositions[i] += offsetInGlobalLine;
2397 // if (splitOperatorsCount > substringsCount) {
2398 // System.arraycopy(substringsStartPositions, 0,
2399 // (substringsStartPositions = new int[splitOperatorsCount]), 0,
2400 // substringsCount);
2401 // System.arraycopy(substringsEndPositions, 0,
2402 // (substringsEndPositions = new int[splitOperatorsCount]), 0,
2403 // substringsCount);
2404 // for (int i = substringsCount; i < splitOperatorsCount; i++) {
2405 // substringsStartPositions[i] = position;
2406 // substringsEndPositions[i] = position;
2408 // System.arraycopy(splitOperators, 0,
2409 // (splitOperators = new int[splitOperatorsCount]), 0,
2410 // splitOperatorsCount);
2412 // System.arraycopy(substringsStartPositions, 0,
2413 // (substringsStartPositions = new int[substringsCount]), 0,
2414 // substringsCount);
2415 // System.arraycopy(substringsEndPositions, 0,
2416 // (substringsEndPositions = new int[substringsCount]), 0,
2417 // substringsCount);
2418 // System.arraycopy(splitOperators, 0,
2419 // (splitOperators = new int[substringsCount]), 0, substringsCount);
2421 // SplitLine splitLine = new SplitLine(splitOperators, result,
2422 // substringsStartPositions);
2423 // return splitLine;
2426 private void updateMappedPositions(int startPosition) {
2427 if (positionsToMap == null) {
2430 char[] source = scanner.source;
2431 int sourceLength = source.length;
2432 while (indexToMap < positionsToMap.length && positionsToMap[indexToMap] <= startPosition) {
2433 int posToMap = positionsToMap[indexToMap];
2434 if (posToMap < 0 || posToMap >= sourceLength) {
2435 // protection against out of bounds position
2436 if (posToMap == sourceLength) {
2437 mappedPositions[indexToMap] = formattedSource.length();
2439 indexToMap = positionsToMap.length; // no more mapping
2442 if (CharOperation.isWhitespace(source[posToMap])) {
2443 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2445 if (posToMap == sourceLength - 1) {
2446 mappedPositions[indexToMap] = startPosition + globalDelta + lineDelta;
2448 mappedPositions[indexToMap] = posToMap + globalDelta + lineDelta;
2455 private void updateMappedPositionsWhileSplitting(int startPosition, int endPosition) {
2456 if (mappedPositions == null || mappedPositions.length == indexInMap)
2458 while (indexInMap < mappedPositions.length && startPosition <= mappedPositions[indexInMap]
2459 && mappedPositions[indexInMap] < endPosition && indexInMap < indexToMap) {
2460 mappedPositions[indexInMap] += splitDelta;
2465 private int getLength(String s, int tabDepth) {
2467 for (int i = 0; i < tabDepth; i++) {
2468 length += options.tabSize;
2470 for (int i = 0, max = s.length(); i < max; i++) {
2471 char currentChar = s.charAt(i);
2472 switch (currentChar) {
2474 length += options.tabSize;
2484 * Sets the initial indentation level
2486 * @param indentationLevel
2487 * new indentation level
2491 public void setInitialIndentationLevel(int newIndentationLevel) {
2492 this.initialIndentationLevel = currentLineIndentationLevel = indentationLevel = newIndentationLevel;