KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > formatter > Scribe


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.formatter;
12
13 import java.util.Arrays JavaDoc;
14
15 import org.eclipse.jdt.core.compiler.CharOperation;
16 import org.eclipse.jdt.core.compiler.InvalidInputException;
17 import org.eclipse.jdt.internal.compiler.ASTVisitor;
18 import org.eclipse.jdt.internal.compiler.ast.Annotation;
19 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
20 import org.eclipse.jdt.internal.compiler.parser.Scanner;
21 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
22 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
23 import org.eclipse.jdt.internal.compiler.util.Util;
24 import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil;
25 import org.eclipse.jdt.internal.core.util.RecordedParsingInformation;
26 import org.eclipse.jdt.internal.formatter.align.Alignment;
27 import org.eclipse.jdt.internal.formatter.align.AlignmentException;
28 import org.eclipse.text.edits.MultiTextEdit;
29 import org.eclipse.text.edits.ReplaceEdit;
30 import org.eclipse.text.edits.TextEdit;
31
32 /**
33  * This class is responsible for dumping formatted source
34  * @since 2.1
35  */

36 public class Scribe {
37     private static final int INITIAL_SIZE = 100;
38     
39     private boolean checkLineWrapping;
40     /** one-based column */
41     public int column;
42     private int[][] commentPositions;
43         
44     // Most specific alignment.
45
public Alignment currentAlignment;
46     public int currentToken;
47     
48     // edits management
49
private OptimizedReplaceEdit[] edits;
50     public int editsIndex;
51     
52     public CodeFormatterVisitor formatter;
53     public int indentationLevel;
54     public int lastNumberOfNewLines;
55     public int line;
56     
57     private int[] lineEnds;
58     private String JavaDoc lineSeparator;
59     public Alignment memberAlignment;
60     public boolean needSpace = false;
61     
62     public int nlsTagCounter;
63     public int pageWidth;
64     public boolean pendingSpace = false;
65
66     public Scanner scanner;
67     public int scannerEndPosition;
68     public int tabLength;
69     public int indentationSize;
70     private int textRegionEnd;
71     private int textRegionStart;
72     public int tabChar;
73     public int numberOfIndentations;
74     private boolean useTabsOnlyForLeadingIndents;
75
76     /** indent empty lines*/
77     private final boolean indentEmptyLines;
78     
79     private final boolean formatJavadocComment;
80     private final boolean formatBlockComment;
81     
82     Scribe(CodeFormatterVisitor formatter, long sourceLevel, int offset, int length, CodeSnippetParsingUtil codeSnippetParsingUtil) {
83         this.scanner = new Scanner(true, true, false/*nls*/, sourceLevel/*sourceLevel*/, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
84         this.formatter = formatter;
85         this.pageWidth = formatter.preferences.page_width;
86         this.tabLength = formatter.preferences.tab_size;
87         this.indentationLevel= 0; // initialize properly
88
this.numberOfIndentations = 0;
89         this.useTabsOnlyForLeadingIndents = formatter.preferences.use_tabs_only_for_leading_indentations;
90         this.indentEmptyLines = formatter.preferences.indent_empty_lines;
91         this.tabChar = formatter.preferences.tab_char;
92         if (this.tabChar == DefaultCodeFormatterOptions.MIXED) {
93             this.indentationSize = formatter.preferences.indentation_size;
94         } else {
95             this.indentationSize = this.tabLength;
96         }
97         this.lineSeparator = formatter.preferences.line_separator;
98         this.indentationLevel = formatter.preferences.initial_indentation_level * this.indentationSize;
99         this.textRegionStart = offset;
100         this.textRegionEnd = offset + length - 1;
101         if (codeSnippetParsingUtil != null) {
102             final RecordedParsingInformation information = codeSnippetParsingUtil.recordedParsingInformation;
103             if (information != null) {
104                 this.lineEnds = information.lineEnds;
105                 this.commentPositions = information.commentPositions;
106             }
107         }
108         this.formatBlockComment = formatter.preferences.comment_format_block_comment;
109         this.formatJavadocComment = formatter.preferences.comment_format_javadoc_comment;
110         reset();
111     }
112     
113     private final void addDeleteEdit(int start, int end) {
114         if (this.edits.length == this.editsIndex) {
115             // resize
116
resize();
117         }
118         addOptimizedReplaceEdit(start, end - start + 1, Util.EMPTY_STRING);
119     }
120
121     public final void addInsertEdit(int insertPosition, String JavaDoc insertedString) {
122         if (this.edits.length == this.editsIndex) {
123             // resize
124
resize();
125         }
126         addOptimizedReplaceEdit(insertPosition, 0, insertedString);
127     }
128
129     private final void addOptimizedReplaceEdit(int offset, int length, String JavaDoc replacement) {
130         if (this.editsIndex > 0) {
131             // try to merge last two edits
132
final OptimizedReplaceEdit previous = this.edits[this.editsIndex-1];
133             final int previousOffset = previous.offset;
134             final int previousLength = previous.length;
135             final int endOffsetOfPreviousEdit = previousOffset + previousLength;
136             final int replacementLength = replacement.length();
137             final String JavaDoc previousReplacement = previous.replacement;
138             final int previousReplacementLength = previousReplacement.length();
139             if (previousOffset == offset && previousLength == length && (replacementLength == 0 || previousReplacementLength == 0)) {
140                 if (this.currentAlignment != null) {
141                     final Location location = this.currentAlignment.location;
142                     if (location.editsIndex == this.editsIndex) {
143                         location.editsIndex--;
144                         location.textEdit = previous;
145                     }
146                 }
147                 this.editsIndex--;
148                 return;
149             }
150             if (endOffsetOfPreviousEdit == offset) {
151                 if (length != 0) {
152                     if (replacementLength != 0) {
153                         this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement + replacement);
154                     } else if (previousLength + length == previousReplacementLength) {
155                         // check the characters. If they are identical, we can get rid of the previous edit
156
boolean canBeRemoved = true;
157                         loop: for (int i = previousOffset; i < previousOffset + previousReplacementLength; i++) {
158                             if (scanner.source[i] != previousReplacement.charAt(i - previousOffset)) {
159                                 this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousReplacementLength, previousReplacement);
160                                 canBeRemoved = false;
161                                 break loop;
162                             }
163                         }
164                         if (canBeRemoved) {
165                             if (this.currentAlignment != null) {
166                                 final Location location = this.currentAlignment.location;
167                                 if (location.editsIndex == this.editsIndex) {
168                                     location.editsIndex--;
169                                     location.textEdit = previous;
170                                 }
171                             }
172                             this.editsIndex--;
173                         }
174                     } else {
175                         this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength + length, previousReplacement);
176                     }
177                 } else {
178                     if (replacementLength != 0) {
179                         this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(previousOffset, previousLength, previousReplacement + replacement);
180                     }
181                 }
182             } else if ((offset + length == previousOffset) && (previousLength + length == replacementLength + previousReplacementLength)) {
183                 // check if both edits corresponds to the orignal source code
184
boolean canBeRemoved = true;
185                 String JavaDoc totalReplacement = replacement + previousReplacement;
186                 loop: for (int i = 0; i < previousLength + length; i++) {
187                     if (scanner.source[i + offset] != totalReplacement.charAt(i)) {
188                         this.edits[this.editsIndex - 1] = new OptimizedReplaceEdit(offset, previousLength + length, totalReplacement);
189                         canBeRemoved = false;
190                         break loop;
191                     }
192                 }
193                 if (canBeRemoved) {
194                     if (this.currentAlignment != null) {
195                         final Location location = this.currentAlignment.location;
196                         if (location.editsIndex == this.editsIndex) {
197                             location.editsIndex--;
198                             location.textEdit = previous;
199                         }
200                     }
201                     this.editsIndex--;
202                 }
203             } else {
204                 this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement);
205             }
206         } else {
207             this.edits[this.editsIndex++] = new OptimizedReplaceEdit(offset, length, replacement);
208         }
209     }
210     
211     public final void addReplaceEdit(int start, int end, String JavaDoc replacement) {
212         if (this.edits.length == this.editsIndex) {
213             // resize
214
resize();
215         }
216         addOptimizedReplaceEdit(start, end - start + 1, replacement);
217     }
218
219     public void alignFragment(Alignment alignment, int fragmentIndex){
220         alignment.fragmentIndex = fragmentIndex;
221         alignment.checkColumn();
222         alignment.performFragmentEffect();
223     }
224     
225     public void checkNLSTag(int sourceStart) {
226         if (hasNLSTag(sourceStart)) {
227             this.nlsTagCounter++;
228         }
229     }
230     public void consumeNextToken() {
231         printComment();
232         try {
233             this.currentToken = this.scanner.getNextToken();
234             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
235         } catch (InvalidInputException e) {
236             throw new AbortFormatting(e);
237         }
238     }
239     public Alignment createAlignment(String JavaDoc name, int mode, int count, int sourceRestart){
240         return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart);
241     }
242
243     public Alignment createAlignment(String JavaDoc name, int mode, int count, int sourceRestart, boolean adjust){
244         return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, adjust);
245     }
246     
247     public Alignment createAlignment(String JavaDoc name, int mode, int tieBreakRule, int count, int sourceRestart){
248         return createAlignment(name, mode, tieBreakRule, count, sourceRestart, this.formatter.preferences.continuation_indentation, false);
249     }
250
251     public Alignment createAlignment(String JavaDoc name, int mode, int count, int sourceRestart, int continuationIndent, boolean adjust){
252         return createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart, continuationIndent, adjust);
253     }
254
255     public Alignment createAlignment(String JavaDoc name, int mode, int tieBreakRule, int count, int sourceRestart, int continuationIndent, boolean adjust){
256         Alignment alignment = new Alignment(name, mode, tieBreakRule, this, count, sourceRestart, continuationIndent);
257         // adjust break indentation
258
if (adjust && this.memberAlignment != null) {
259             Alignment current = this.memberAlignment;
260             while (current.enclosing != null) {
261                 current = current.enclosing;
262             }
263             if ((current.mode & Alignment.M_MULTICOLUMN) != 0) {
264                 final int indentSize = this.indentationSize;
265                 switch(current.chunkKind) {
266                     case Alignment.CHUNK_METHOD :
267                     case Alignment.CHUNK_TYPE :
268                         if ((mode & Alignment.M_INDENT_BY_ONE) != 0) {
269                             alignment.breakIndentationLevel = this.indentationLevel + indentSize;
270                         } else {
271                             alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize;
272                         }
273                         alignment.update();
274                         break;
275                     case Alignment.CHUNK_FIELD :
276                         if ((mode & Alignment.M_INDENT_BY_ONE) != 0) {
277                             alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize;
278                         } else {
279                             alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize;
280                         }
281                         alignment.update();
282                         break;
283                 }
284             } else {
285                 switch(current.mode & Alignment.SPLIT_MASK) {
286                     case Alignment.M_COMPACT_SPLIT :
287                     case Alignment.M_COMPACT_FIRST_BREAK_SPLIT :
288                     case Alignment.M_NEXT_PER_LINE_SPLIT :
289                     case Alignment.M_NEXT_SHIFTED_SPLIT :
290                     case Alignment.M_ONE_PER_LINE_SPLIT :
291                         final int indentSize = this.indentationSize;
292                         switch(current.chunkKind) {
293                             case Alignment.CHUNK_METHOD :
294                             case Alignment.CHUNK_TYPE :
295                                 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) {
296                                     alignment.breakIndentationLevel = this.indentationLevel + indentSize;
297                                 } else {
298                                     alignment.breakIndentationLevel = this.indentationLevel + continuationIndent * indentSize;
299                                 }
300                                 alignment.update();
301                                 break;
302                             case Alignment.CHUNK_FIELD :
303                                 if ((mode & Alignment.M_INDENT_BY_ONE) != 0) {
304                                     alignment.breakIndentationLevel = current.originalIndentationLevel + indentSize;
305                                 } else {
306                                     alignment.breakIndentationLevel = current.originalIndentationLevel + continuationIndent * indentSize;
307                                 }
308                                 alignment.update();
309                                 break;
310                         }
311                         break;
312                 }
313             }
314         }
315         return alignment;
316     }
317
318     public Alignment createMemberAlignment(String JavaDoc name, int mode, int count, int sourceRestart) {
319         Alignment mAlignment = createAlignment(name, mode, Alignment.R_INNERMOST, count, sourceRestart);
320         mAlignment.breakIndentationLevel = this.indentationLevel;
321         return mAlignment;
322     }
323     
324     public void enterAlignment(Alignment alignment){
325         alignment.enclosing = this.currentAlignment;
326         alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart;
327         this.currentAlignment = alignment;
328     }
329
330     public void enterMemberAlignment(Alignment alignment) {
331         alignment.enclosing = this.memberAlignment;
332         alignment.location.lastLocalDeclarationSourceStart = this.formatter.lastLocalDeclarationSourceStart;
333         this.memberAlignment = alignment;
334     }
335
336     public void exitAlignment(Alignment alignment, boolean discardAlignment){
337         Alignment current = this.currentAlignment;
338         while (current != null){
339             if (current == alignment) break;
340             current = current.enclosing;
341         }
342         if (current == null) {
343             throw new AbortFormatting("could not find matching alignment: "+alignment); //$NON-NLS-1$
344
}
345         this.indentationLevel = alignment.location.outputIndentationLevel;
346         this.numberOfIndentations = alignment.location.numberOfIndentations;
347         this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart;
348         if (discardAlignment){
349             this.currentAlignment = alignment.enclosing;
350         }
351     }
352     
353     public void exitMemberAlignment(Alignment alignment){
354         Alignment current = this.memberAlignment;
355         while (current != null){
356             if (current == alignment) break;
357             current = current.enclosing;
358         }
359         if (current == null) {
360             throw new AbortFormatting("could not find matching alignment: "+alignment); //$NON-NLS-1$
361
}
362         this.indentationLevel = current.location.outputIndentationLevel;
363         this.numberOfIndentations = current.location.numberOfIndentations;
364         this.formatter.lastLocalDeclarationSourceStart = alignment.location.lastLocalDeclarationSourceStart;
365         this.memberAlignment = current.enclosing;
366     }
367     
368     public Alignment getAlignment(String JavaDoc name){
369         if (this.currentAlignment != null) {
370             return this.currentAlignment.getAlignment(name);
371         }
372         return null;
373     }
374     
375     /**
376      * Answer actual indentation level based on true column position
377      * @return int
378      */

379     public int getColumnIndentationLevel() {
380         return this.column - 1;
381     }
382     
383     public final int getCommentIndex(int position) {
384         if (this.commentPositions == null)
385             return -1;
386         int length = this.commentPositions.length;
387         if (length == 0) {
388             return -1;
389         }
390         int g = 0, d = length - 1;
391         int m = 0;
392         while (g <= d) {
393             m = g + (d - g) / 2;
394             int bound = this.commentPositions[m][1];
395             if (bound < 0) {
396                 bound = -bound;
397             }
398             if (bound < position) {
399                 g = m + 1;
400             } else if (bound > position) {
401                 d = m - 1;
402             } else {
403                 return m;
404             }
405         }
406         return -(g + 1);
407     }
408
409     private int getCurrentCommentOffset(int start) {
410         int linePtr = -Arrays.binarySearch(this.lineEnds, start);
411         int offset = 0;
412         int beginningOfLine = this.getLineEnd(linePtr - 1);
413         if (beginningOfLine == -1) {
414             beginningOfLine = 0;
415         }
416         int currentStartPosition = start;
417         char[] source = scanner.source;
418
419         // find the position of the beginning of the line containing the comment
420
while (beginningOfLine > currentStartPosition) {
421             if (linePtr > 0) {
422                 beginningOfLine = this.getLineEnd(--linePtr);
423             } else {
424                 beginningOfLine = 0;
425                 break;
426             }
427         }
428         for (int i = currentStartPosition - 1; i >= beginningOfLine ; i--) {
429             char currentCharacter = source[i];
430             switch (currentCharacter) {
431                 case '\t' :
432                     offset += this.tabLength;
433                     break;
434                 case ' ' :
435                     offset++;
436                     break;
437                 case '\r' :
438                 case '\n' :
439                     break;
440                 default:
441                     return offset;
442             }
443         }
444         return offset;
445     }
446
447     public String JavaDoc getEmptyLines(int linesNumber) {
448         if (this.nlsTagCounter > 0) {
449             return Util.EMPTY_STRING;
450         }
451         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
452         if (lastNumberOfNewLines == 0) {
453             linesNumber++; // add an extra line breaks
454
for (int i = 0; i < linesNumber; i++) {
455                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
456                 buffer.append(this.lineSeparator);
457             }
458             lastNumberOfNewLines += linesNumber;
459             line += linesNumber;
460             column = 1;
461             needSpace = false;
462             this.pendingSpace = false;
463         } else if (lastNumberOfNewLines == 1) {
464             for (int i = 0; i < linesNumber; i++) {
465                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
466                 buffer.append(this.lineSeparator);
467             }
468             lastNumberOfNewLines += linesNumber;
469             line += linesNumber;
470             column = 1;
471             needSpace = false;
472             this.pendingSpace = false;
473         } else {
474             if ((lastNumberOfNewLines - 1) >= linesNumber) {
475                 // there is no need to add new lines
476
return Util.EMPTY_STRING;
477             }
478             final int realNewLineNumber = linesNumber - lastNumberOfNewLines + 1;
479             for (int i = 0; i < realNewLineNumber; i++) {
480                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
481                 buffer.append(this.lineSeparator);
482             }
483             lastNumberOfNewLines += realNewLineNumber;
484             line += realNewLineNumber;
485             column = 1;
486             needSpace = false;
487             this.pendingSpace = false;
488         }
489         return String.valueOf(buffer);
490     }
491
492     public OptimizedReplaceEdit getLastEdit() {
493         if (this.editsIndex > 0) {
494             return this.edits[this.editsIndex - 1];
495         }
496         return null;
497     }
498     
499     public final int getLineEnd(int lineNumber) {
500         if (this.lineEnds == null)
501             return -1;
502         if (lineNumber >= this.lineEnds.length + 1)
503             return this.scannerEndPosition;
504         if (lineNumber <= 0)
505             return -1;
506         return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line
507
}
508     
509     Alignment getMemberAlignment() {
510         return this.memberAlignment;
511     }
512     
513     public String JavaDoc getNewLine() {
514         if (this.nlsTagCounter > 0) {
515             return Util.EMPTY_STRING;
516         }
517         if (lastNumberOfNewLines >= 1) {
518             column = 1; // ensure that the scribe is at the beginning of a new line
519
return Util.EMPTY_STRING;
520         }
521         line++;
522         lastNumberOfNewLines = 1;
523         column = 1;
524         needSpace = false;
525         this.pendingSpace = false;
526         return this.lineSeparator;
527     }
528
529     /**
530      * Answer next indentation level based on column estimated position
531      * (if column is not indented, then use indentationLevel)
532      */

533     public int getNextIndentationLevel(int someColumn) {
534         int indent = someColumn - 1;
535         if (indent == 0)
536             return this.indentationLevel;
537         if (this.tabChar == DefaultCodeFormatterOptions.TAB) {
538             if (this.useTabsOnlyForLeadingIndents) {
539                 return indent;
540             }
541             int rem = indent % this.indentationSize;
542             int addition = rem == 0 ? 0 : this.indentationSize - rem; // round to superior
543
return indent + addition;
544         } else {
545             return indent;
546         }
547     }
548
549     private String JavaDoc getPreserveEmptyLines(int count) {
550         if (count > 0) {
551             if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
552                 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve);
553                 return this.getEmptyLines(linesToPreserve);
554             } else {
555                 return getNewLine();
556             }
557         }
558         return Util.EMPTY_STRING;
559     }
560     
561     public TextEdit getRootEdit() {
562         MultiTextEdit edit = null;
563         int length = this.textRegionEnd - this.textRegionStart + 1;
564         if (this.textRegionStart <= 0) {
565             if (length <= 0) {
566                 edit = new MultiTextEdit(0, 0);
567             } else {
568                 edit = new MultiTextEdit(0, this.textRegionEnd + 1);
569             }
570         } else {
571             edit = new MultiTextEdit(this.textRegionStart, this.textRegionEnd - this.textRegionStart + 1);
572         }
573         for (int i= 0, max = this.editsIndex; i < max; i++) {
574             OptimizedReplaceEdit currentEdit = edits[i];
575             if (isValidEdit(currentEdit)) {
576                 edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement));
577             }
578         }
579         this.edits = null;
580         return edit;
581     }
582     
583     public void handleLineTooLong() {
584         // search for closest breakable alignment, using tiebreak rules
585
// look for outermost breakable one
586
int relativeDepth = 0, outerMostDepth = -1;
587         Alignment targetAlignment = this.currentAlignment;
588         while (targetAlignment != null){
589             if (targetAlignment.tieBreakRule == Alignment.R_OUTERMOST && targetAlignment.couldBreak()){
590                 outerMostDepth = relativeDepth;
591             }
592             targetAlignment = targetAlignment.enclosing;
593             relativeDepth++;
594         }
595         if (outerMostDepth >= 0) {
596             throw new AlignmentException(AlignmentException.LINE_TOO_LONG, outerMostDepth);
597         }
598         // look for innermost breakable one
599
relativeDepth = 0;
600         targetAlignment = this.currentAlignment;
601         while (targetAlignment != null){
602             if (targetAlignment.couldBreak()){
603                 throw new AlignmentException(AlignmentException.LINE_TOO_LONG, relativeDepth);
604             }
605             targetAlignment = targetAlignment.enclosing;
606             relativeDepth++;
607         }
608         // did not find any breakable location - proceed
609
}
610
611     /*
612      * Check if there is a NLS tag on this line. If yes, return true, returns false otherwise.
613      */

614     private boolean hasNLSTag(int sourceStart) {
615         // search the last comment where commentEnd < current lineEnd
616
if (this.lineEnds == null) return false;
617         int index = Arrays.binarySearch(this.lineEnds, sourceStart);
618         int currentLineEnd = this.getLineEnd(-index);
619         if (currentLineEnd != -1) {
620             int commentIndex = getCommentIndex(currentLineEnd);
621             if (commentIndex < 0) {
622                 commentIndex = -commentIndex - 2;
623             }
624             if (commentIndex >= 0 && commentIndex < this.commentPositions.length) {
625                 int start = this.commentPositions[commentIndex][0];
626                 if (start < 0) {
627                     start = -start;
628                     // check that we are on the same line
629
int lineIndexForComment = Arrays.binarySearch(this.lineEnds, start);
630                     if (lineIndexForComment == index) {
631                         return CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, start, currentLineEnd) != -1;
632                     }
633                 }
634             }
635         }
636         return false;
637     }
638
639     public void indent() {
640         this.indentationLevel += this.indentationSize;
641         this.numberOfIndentations++;
642     }
643
644     /**
645      * @param compilationUnitSource
646      */

647     public void initializeScanner(char[] compilationUnitSource) {
648         this.scanner.setSource(compilationUnitSource);
649         this.scannerEndPosition = compilationUnitSource.length;
650         this.scanner.resetTo(0, this.scannerEndPosition - 1);
651         this.edits = new OptimizedReplaceEdit[INITIAL_SIZE];
652     }
653
654     private boolean isOnFirstColumn(int start) {
655         if (this.lineEnds == null) return start == 0;
656         int index = Arrays.binarySearch(this.lineEnds, start);
657         // we want the line end of the previous line
658
int previousLineEnd = this.getLineEnd(-index - 1);
659         return previousLineEnd != -1 && previousLineEnd == start - 1;
660     }
661
662     private boolean isValidEdit(OptimizedReplaceEdit edit) {
663         final int editLength= edit.length;
664         final int editReplacementLength= edit.replacement.length();
665         final int editOffset= edit.offset;
666         if (editLength != 0) {
667             if (this.textRegionStart <= editOffset && (editOffset + editLength - 1) <= this.textRegionEnd) {
668                 if (editReplacementLength != 0 && editLength == editReplacementLength) {
669                     for (int i = editOffset, max = editOffset + editLength; i < max; i++) {
670                         if (scanner.source[i] != edit.replacement.charAt(i - editOffset)) {
671                             return true;
672                         }
673                     }
674                     return false;
675                 } else {
676                     return true;
677                 }
678             } else if (editOffset + editLength == this.textRegionStart) {
679                 int i = editOffset;
680                 for (int max = editOffset + editLength; i < max; i++) {
681                     int replacementStringIndex = i - editOffset;
682                     if (replacementStringIndex >= editReplacementLength || scanner.source[i] != edit.replacement.charAt(replacementStringIndex)) {
683                         break;
684                     }
685                 }
686                 if (i - editOffset != editReplacementLength && i != editOffset + editLength - 1) {
687                     edit.offset = textRegionStart;
688                     edit.length = 0;
689                     edit.replacement = edit.replacement.substring(i - editOffset);
690                     return true;
691                 }
692             }
693         } else if (this.textRegionStart <= editOffset && editOffset <= this.textRegionEnd) {
694             return true;
695         } else if (editOffset == this.scannerEndPosition && editOffset == this.textRegionEnd + 1) {
696             return true;
697         }
698         return false;
699     }
700
701     private void preserveEmptyLines(int count, int insertPosition) {
702         if (count > 0) {
703             if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
704                 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve);
705                 this.printEmptyLines(linesToPreserve, insertPosition);
706             } else {
707                 printNewLine(insertPosition);
708             }
709         }
710     }
711
712     private void print(char[] s, boolean considerSpaceIfAny) {
713         if (checkLineWrapping && s.length + column > this.pageWidth) {
714             handleLineTooLong();
715         }
716         this.lastNumberOfNewLines = 0;
717         if (this.indentationLevel != 0) {
718             printIndentationIfNecessary();
719         }
720         if (considerSpaceIfAny) {
721             this.space();
722         }
723         if (this.pendingSpace) {
724             this.addInsertEdit(this.scanner.getCurrentTokenStartPosition(), " "); //$NON-NLS-1$
725
}
726         this.pendingSpace = false;
727         this.needSpace = false;
728         column += s.length;
729         needSpace = true;
730     }
731
732     private void printBlockComment(char[] s, boolean isJavadoc) {
733         int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
734         int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1;
735         
736         this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1);
737         int currentCharacter;
738         boolean isNewLine = false;
739         int start = currentTokenStartPosition;
740         int nextCharacterStart = currentTokenStartPosition;
741         int previousStart = currentTokenStartPosition;
742         boolean onFirstColumn = isOnFirstColumn(start);
743
744         boolean indentComment = false;
745         if (this.indentationLevel != 0) {
746             if (isJavadoc
747                     || !this.formatter.preferences.never_indent_block_comments_on_first_column
748                     || !onFirstColumn) {
749                 indentComment = true;
750                 printIndentationIfNecessary();
751             }
752         }
753         if (this.pendingSpace) {
754             this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$
755
}
756         this.needSpace = false;
757         this.pendingSpace = false;
758
759         int currentCommentOffset = onFirstColumn ? 0 : getCurrentCommentOffset(start);
760         boolean formatComment = (isJavadoc && formatJavadocComment) || (!isJavadoc && formatBlockComment);
761
762         while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) {
763             nextCharacterStart = this.scanner.currentPosition;
764
765             switch(currentCharacter) {
766                 case '\r' :
767                     start = previousStart;
768                     isNewLine = true;
769                     if (this.scanner.getNextChar('\n')) {
770                         currentCharacter = '\n';
771                         nextCharacterStart = this.scanner.currentPosition;
772                     }
773                     break;
774                 case '\n' :
775                     start = previousStart;
776                     isNewLine = true;
777                     nextCharacterStart = this.scanner.currentPosition;
778                     break;
779                 default:
780                     if (isNewLine) {
781                         this.column = 1;
782                         this.line++;
783                         isNewLine = false;
784                         
785                         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
786                         if (onFirstColumn) {
787                             // simply insert indentation if necessary
788
buffer.append(this.lineSeparator);
789                             if (indentComment) {
790                                 printIndentationIfNecessary(buffer);
791                             }
792                             if (formatComment) {
793                                 if (ScannerHelper.isWhitespace((char) currentCharacter)) {
794                                     int previousStartPosition = this.scanner.currentPosition;
795                                     while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) {
796                                         previousStart = nextCharacterStart;
797                                         previousStartPosition = this.scanner.currentPosition;
798                                         currentCharacter = this.scanner.getNextChar();
799                                         nextCharacterStart = this.scanner.currentPosition;
800                                     }
801                                     if (currentCharacter == '\r' || currentCharacter == '\n') {
802                                         nextCharacterStart = previousStartPosition;
803                                     }
804                                 }
805                                 if (currentCharacter != '\r' && currentCharacter != '\n') {
806                                     buffer.append(' ');
807                                 }
808                             }
809                         } else {
810                             if (ScannerHelper.isWhitespace((char) currentCharacter)) {
811                                 int previousStartPosition = this.scanner.currentPosition;
812                                 int count = 0;
813                                 loop: while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) {
814                                     if (count >= currentCommentOffset) {
815                                         break loop;
816                                     }
817                                     previousStart = nextCharacterStart;
818                                     previousStartPosition = this.scanner.currentPosition;
819                                     switch(currentCharacter) {
820                                         case '\t' :
821                                             count += this.tabLength;
822                                             break;
823                                         default :
824                                             count ++;
825                                     }
826                                     currentCharacter = this.scanner.getNextChar();
827                                     nextCharacterStart = this.scanner.currentPosition;
828                                 }
829                                 if (currentCharacter == '\r' || currentCharacter == '\n') {
830                                     nextCharacterStart = previousStartPosition;
831                                 }
832                             }
833                             buffer.append(this.lineSeparator);
834                             if (indentComment) {
835                                 printIndentationIfNecessary(buffer);
836                             }
837                             if (formatComment) {
838                                 int previousStartTemp = previousStart;
839                                 int nextCharacterStartTemp = nextCharacterStart;
840                                 while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) {
841                                     previousStart = nextCharacterStart;
842                                     currentCharacter = this.scanner.getNextChar();
843                                     nextCharacterStart = this.scanner.currentPosition;
844                                 }
845                                 if (currentCharacter == '*') {
846                                     buffer.append(' ');
847                                 } else {
848                                     previousStart = previousStartTemp;
849                                     nextCharacterStart = nextCharacterStartTemp;
850                                 }
851                                 this.scanner.currentPosition = nextCharacterStart;
852                             }
853                         }
854                         addReplaceEdit(start, previousStart - 1, String.valueOf(buffer));
855                     } else {
856                         this.column += (nextCharacterStart - previousStart);
857                     }
858             }
859             previousStart = nextCharacterStart;
860             this.scanner.currentPosition = nextCharacterStart;
861         }
862         this.lastNumberOfNewLines = 0;
863         needSpace = false;
864         this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1);
865         if (isJavadoc) {
866             printNewLine();
867         }
868     }
869     
870     public void printEndOfCompilationUnit() {
871         try {
872             // if we have a space between two tokens we ensure it will be dumped in the formatted string
873
int currentTokenStartPosition = this.scanner.currentPosition;
874             boolean hasComment = false;
875             boolean hasLineComment = false;
876             boolean hasWhitespace = false;
877             int count = 0;
878             while (true) {
879                 this.currentToken = this.scanner.getNextToken();
880                 switch(this.currentToken) {
881                     case TerminalTokens.TokenNameWHITESPACE :
882                         char[] whiteSpaces = this.scanner.getCurrentTokenSource();
883                         count = 0;
884                         for (int i = 0, max = whiteSpaces.length; i < max; i++) {
885                             switch(whiteSpaces[i]) {
886                                 case '\r' :
887                                     if ((i + 1) < max) {
888                                         if (whiteSpaces[i + 1] == '\n') {
889                                             i++;
890                                         }
891                                     }
892                                     count++;
893                                     break;
894                                 case '\n' :
895                                     count++;
896                             }
897                         }
898                         if (count == 0) {
899                             hasWhitespace = true;
900                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
901                         } else if (hasComment) {
902                             if (count == 1) {
903                                 this.printNewLine(this.scanner.getCurrentTokenStartPosition());
904                             } else {
905                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
906                             }
907                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
908                         } else if (hasLineComment) {
909                             this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition());
910                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
911                         } else {
912                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
913                         }
914                         currentTokenStartPosition = this.scanner.currentPosition;
915                         break;
916                     case TerminalTokens.TokenNameCOMMENT_LINE :
917                         if (count >= 1) {
918                             if (count > 1) {
919                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
920                             } else if (count == 1) {
921                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
922                             }
923                         } else if (hasWhitespace) {
924                             space();
925                         }
926                         hasWhitespace = false;
927                         this.printLineComment(this.scanner.getRawTokenSource());
928                         currentTokenStartPosition = this.scanner.currentPosition;
929                         hasLineComment = true;
930                         count = 0;
931                         break;
932                     case TerminalTokens.TokenNameCOMMENT_BLOCK :
933                         if (count >= 1) {
934                             if (count > 1) {
935                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
936                             } else if (count == 1) {
937                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
938                             }
939                         } else if (hasWhitespace) {
940                             space();
941                         }
942                         hasWhitespace = false;
943                         this.printBlockComment(this.scanner.getRawTokenSource(), false);
944                         currentTokenStartPosition = this.scanner.currentPosition;
945                         hasLineComment = false;
946                         hasComment = true;
947                         count = 0;
948                         break;
949                     case TerminalTokens.TokenNameCOMMENT_JAVADOC :
950                         if (count >= 1) {
951                             if (count > 1) {
952                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
953                             } else if (count == 1) {
954                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
955                             }
956                         } else if (hasWhitespace) {
957                             space();
958                         }
959                         hasWhitespace = false;
960                         this.printBlockComment(this.scanner.getRawTokenSource(), true);
961                         currentTokenStartPosition = this.scanner.currentPosition;
962                         hasLineComment = false;
963                         hasComment = true;
964                         count = 0;
965                         break;
966                     case TerminalTokens.TokenNameSEMICOLON :
967                         char[] currentTokenSource = this.scanner.getRawTokenSource();
968                         this.print(currentTokenSource, this.formatter.preferences.insert_space_before_semicolon);
969                         break;
970                     case TerminalTokens.TokenNameEOF :
971                         if (count >= 1 || this.formatter.preferences.insert_new_line_at_end_of_file_if_missing) {
972                             this.printNewLine(this.scannerEndPosition);
973                         }
974                         return;
975                     default :
976                         // step back one token
977
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
978                         return;
979                 }
980             }
981         } catch (InvalidInputException e) {
982             throw new AbortFormatting(e);
983         }
984     }
985
986     public void printComment() {
987         try {
988             // if we have a space between two tokens we ensure it will be dumped in the formatted string
989
int currentTokenStartPosition = this.scanner.currentPosition;
990             boolean hasComment = false;
991             boolean hasLineComment = false;
992             boolean hasWhitespace = false;
993             int count = 0;
994             while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
995                 switch(this.currentToken) {
996                     case TerminalTokens.TokenNameWHITESPACE :
997                         char[] whiteSpaces = this.scanner.getCurrentTokenSource();
998                         count = 0;
999                         for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1000                            switch(whiteSpaces[i]) {
1001                                case '\r' :
1002                                    if ((i + 1) < max) {
1003                                        if (whiteSpaces[i + 1] == '\n') {
1004                                            i++;
1005                                        }
1006                                    }
1007                                    count++;
1008                                    break;
1009                                case '\n' :
1010                                    count++;
1011                            }
1012                        }
1013                        if (count == 0) {
1014                            hasWhitespace = true;
1015                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1016                        } else if (hasComment) {
1017                            if (count == 1) {
1018                                this.printNewLine(this.scanner.getCurrentTokenStartPosition());
1019                            } else {
1020                                preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
1021                            }
1022                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1023                        } else if (hasLineComment) {
1024                            this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition());
1025                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1026                        } else if (count != 0 && this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
1027                            addReplaceEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition(), this.getPreserveEmptyLines(count - 1));
1028                        } else {
1029                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1030                        }
1031                        currentTokenStartPosition = this.scanner.currentPosition;
1032                        break;
1033                    case TerminalTokens.TokenNameCOMMENT_LINE :
1034                        if (count >= 1) {
1035                            if (count > 1) {
1036                                preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
1037                            } else if (count == 1) {
1038                                printNewLine(this.scanner.getCurrentTokenStartPosition());
1039                            }
1040                        } else if (hasWhitespace) {
1041                            space();
1042                        }
1043                        hasWhitespace = false;
1044                        this.printLineComment(this.scanner.getRawTokenSource());
1045                        currentTokenStartPosition = this.scanner.currentPosition;
1046                        hasLineComment = true;
1047                        count = 0;
1048                        break;
1049                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1050                        if (count >= 1) {
1051                            if (count > 1) {
1052                                preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
1053                            } else if (count == 1) {
1054                                printNewLine(this.scanner.getCurrentTokenStartPosition());
1055                            }
1056                        } else if (hasWhitespace) {
1057                            space();
1058                        }
1059                        hasWhitespace = false;
1060                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1061                        currentTokenStartPosition = this.scanner.currentPosition;
1062                        hasLineComment = false;
1063                        hasComment = true;
1064                        count = 0;
1065                        break;
1066                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1067                        if (count >= 1) {
1068                            if (count > 1) {
1069                                preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
1070                            } else if (count == 1) {
1071                                printNewLine(this.scanner.getCurrentTokenStartPosition());
1072                            }
1073                        } else if (hasWhitespace) {
1074                            space();
1075                        }
1076                        hasWhitespace = false;
1077                        this.printBlockComment(this.scanner.getRawTokenSource(), true);
1078                        currentTokenStartPosition = this.scanner.currentPosition;
1079                        hasLineComment = false;
1080                        hasComment = true;
1081                        count = 0;
1082                        break;
1083                    default :
1084                        // step back one token
1085
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1086                        return;
1087                }
1088            }
1089        } catch (InvalidInputException e) {
1090            throw new AbortFormatting(e);
1091        }
1092    }
1093    
1094    private void printLineComment(char[] s) {
1095        int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
1096        int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1;
1097        if (CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, currentTokenStartPosition, currentTokenEndPosition) != -1) {
1098            this.nlsTagCounter = 0;
1099        }
1100        this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1);
1101        int currentCharacter;
1102        int start = currentTokenStartPosition;
1103        int nextCharacterStart = currentTokenStartPosition;
1104        
1105        if (this.indentationLevel != 0) {
1106            if (!this.formatter.preferences.never_indent_line_comments_on_first_column
1107                    || !isOnFirstColumn(start)) {
1108                printIndentationIfNecessary();
1109            }
1110        }
1111        if (this.pendingSpace) {
1112            this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$
1113
}
1114        this.needSpace = false;
1115        this.pendingSpace = false;
1116        int previousStart = currentTokenStartPosition;
1117
1118        loop: while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) {
1119            nextCharacterStart = this.scanner.currentPosition;
1120
1121            switch(currentCharacter) {
1122                case '\r' :
1123                    start = previousStart;
1124                    break loop;
1125                case '\n' :
1126                    start = previousStart;
1127                    break loop;
1128            }
1129            previousStart = nextCharacterStart;
1130        }
1131        if (start != currentTokenStartPosition) {
1132            // this means that the line comment doesn't end the file
1133
addReplaceEdit(start, currentTokenEndPosition - 1, lineSeparator);
1134            this.line++;
1135            this.column = 1;
1136            this.lastNumberOfNewLines = 1;
1137        }
1138        this.needSpace = false;
1139        this.pendingSpace = false;
1140        // realign to the proper value
1141
if (this.currentAlignment != null) {
1142            if (this.memberAlignment != null) {
1143                // select the last alignment
1144
if (this.currentAlignment.location.inputOffset > this.memberAlignment.location.inputOffset) {
1145                    if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) {
1146                        this.currentAlignment.performFragmentEffect();
1147                    }
1148                } else {
1149                    this.indentationLevel = Math.max(this.indentationLevel, this.memberAlignment.breakIndentationLevel);
1150                }
1151            } else if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) {
1152                this.currentAlignment.performFragmentEffect();
1153            }
1154        }
1155        this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1);
1156    }
1157    public void printEmptyLines(int linesNumber) {
1158        this.printEmptyLines(linesNumber, this.scanner.getCurrentTokenEndPosition() + 1);
1159    }
1160
1161    private void printEmptyLines(int linesNumber, int insertPosition) {
1162        final String JavaDoc buffer = getEmptyLines(linesNumber);
1163        if (Util.EMPTY_STRING == buffer) return;
1164
1165        addInsertEdit(insertPosition, buffer);
1166    }
1167
1168    void printIndentationIfNecessary() {
1169        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1170        printIndentationIfNecessary(buffer);
1171        if (buffer.length() > 0) {
1172            addInsertEdit(this.scanner.getCurrentTokenStartPosition(), buffer.toString());
1173            this.pendingSpace = false;
1174        }
1175    }
1176
1177    private void printIndentationIfNecessary(StringBuffer JavaDoc buffer) {
1178        switch(this.tabChar) {
1179            case DefaultCodeFormatterOptions.TAB :
1180                boolean useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents;
1181                int numberOfLeadingIndents = this.numberOfIndentations;
1182                int indentationsAsTab = 0;
1183                if (useTabsForLeadingIndents) {
1184                    while (this.column <= this.indentationLevel) {
1185                        if (indentationsAsTab < numberOfLeadingIndents) {
1186                            buffer.append('\t');
1187                            indentationsAsTab++;
1188                            int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space
1189
this.column += complement;
1190                            this.needSpace = false;
1191                        } else {
1192                            buffer.append(' ');
1193                            this.column++;
1194                            this.needSpace = false;
1195                        }
1196                    }
1197                } else {
1198                    while (this.column <= this.indentationLevel) {
1199                        buffer.append('\t');
1200                        int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space
1201
this.column += complement;
1202                        this.needSpace = false;
1203                    }
1204                }
1205                break;
1206            case DefaultCodeFormatterOptions.SPACE :
1207                while (this.column <= this.indentationLevel) {
1208                    buffer.append(' ');
1209                    this.column++;
1210                    this.needSpace = false;
1211                }
1212                break;
1213            case DefaultCodeFormatterOptions.MIXED :
1214                useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents;
1215                numberOfLeadingIndents = this.numberOfIndentations;
1216                indentationsAsTab = 0;
1217                if (useTabsForLeadingIndents) {
1218                    final int columnForLeadingIndents = numberOfLeadingIndents * this.indentationSize;
1219                    while (this.column <= this.indentationLevel) {
1220                        if (this.column <= columnForLeadingIndents) {
1221                            if ((this.column - 1 + this.tabLength) <= this.indentationLevel) {
1222                                buffer.append('\t');
1223                                this.column += this.tabLength;
1224                            } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) {
1225                                // print one indentation
1226
for (int i = 0, max = this.indentationSize; i < max; i++) {
1227                                    buffer.append(' ');
1228                                    this.column++;
1229                                }
1230                            } else {
1231                                buffer.append(' ');
1232                                this.column++;
1233                            }
1234                        } else {
1235                            for (int i = this.column, max = this.indentationLevel; i <= max; i++) {
1236                                buffer.append(' ');
1237                                this.column++;
1238                            }
1239                        }
1240                        this.needSpace = false;
1241                    }
1242                } else {
1243                    while (this.column <= this.indentationLevel) {
1244                        if ((this.column - 1 + this.tabLength) <= this.indentationLevel) {
1245                            buffer.append('\t');
1246                            this.column += this.tabLength;
1247                        } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) {
1248                            // print one indentation
1249
for (int i = 0, max = this.indentationSize; i < max; i++) {
1250                                buffer.append(' ');
1251                                this.column++;
1252                            }
1253                        } else {
1254                            buffer.append(' ');
1255                            this.column++;
1256                        }
1257                        this.needSpace = false;
1258                    }
1259                }
1260                break;
1261        }
1262    }
1263
1264    public void printModifiers(Annotation[] annotations, ASTVisitor visitor) {
1265        try {
1266            int annotationsLength = annotations != null ? annotations.length : 0;
1267            int annotationsIndex = 0;
1268            boolean isFirstModifier = true;
1269            int currentTokenStartPosition = this.scanner.currentPosition;
1270            boolean hasComment = false;
1271            boolean hasModifiers = false;
1272            while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
1273                switch(this.currentToken) {
1274                    case TerminalTokens.TokenNamepublic :
1275                    case TerminalTokens.TokenNameprotected :
1276                    case TerminalTokens.TokenNameprivate :
1277                    case TerminalTokens.TokenNamestatic :
1278                    case TerminalTokens.TokenNameabstract :
1279                    case TerminalTokens.TokenNamefinal :
1280                    case TerminalTokens.TokenNamenative :
1281                    case TerminalTokens.TokenNamesynchronized :
1282                    case TerminalTokens.TokenNametransient :
1283                    case TerminalTokens.TokenNamevolatile :
1284                    case TerminalTokens.TokenNamestrictfp :
1285                        hasModifiers = true;
1286                        this.print(this.scanner.getRawTokenSource(), !isFirstModifier);
1287                        isFirstModifier = false;
1288                        currentTokenStartPosition = this.scanner.currentPosition;
1289                        break;
1290                    case TerminalTokens.TokenNameAT :
1291                        hasModifiers = true;
1292                        if (!isFirstModifier) {
1293                            this.space();
1294                        }
1295                        this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1);
1296                        if (annotationsIndex < annotationsLength) {
1297                            annotations[annotationsIndex++].traverse(visitor, (BlockScope) null);
1298                            if (this.formatter.preferences.insert_new_line_after_annotation) {
1299                                this.printNewLine();
1300                            }
1301                        } else {
1302                            return;
1303                        }
1304                        isFirstModifier = false;
1305                        currentTokenStartPosition = this.scanner.currentPosition;
1306                        break;
1307                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1308                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1309                        currentTokenStartPosition = this.scanner.currentPosition;
1310                        hasComment = true;
1311                        break;
1312                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1313                        this.printBlockComment(this.scanner.getRawTokenSource(), true);
1314                        currentTokenStartPosition = this.scanner.currentPosition;
1315                        hasComment = true;
1316                        break;
1317                    case TerminalTokens.TokenNameCOMMENT_LINE :
1318                        this.printLineComment(this.scanner.getRawTokenSource());
1319                        currentTokenStartPosition = this.scanner.currentPosition;
1320                        break;
1321                    case TerminalTokens.TokenNameWHITESPACE :
1322                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1323                        int count = 0;
1324                        char[] whiteSpaces = this.scanner.getCurrentTokenSource();
1325                        for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1326                            switch(whiteSpaces[i]) {
1327                                case '\r' :
1328                                    if ((i + 1) < max) {
1329                                        if (whiteSpaces[i + 1] == '\n') {
1330                                            i++;
1331                                        }
1332                                    }
1333                                    count++;
1334                                    break;
1335                                case '\n' :
1336                                    count++;
1337                            }
1338                        }
1339                        if (count >= 1 && hasComment) {
1340                            printNewLine();
1341                        }
1342                        currentTokenStartPosition = this.scanner.currentPosition;
1343                        hasComment = false;
1344                        break;
1345                    default:
1346                        if (hasModifiers) {
1347                            this.space();
1348                        }
1349                        // step back one token
1350
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1351                        return;
1352                }
1353            }
1354        } catch (InvalidInputException e) {
1355            throw new AbortFormatting(e);
1356        }
1357    }
1358    
1359    public void printNewLine() {
1360        if (this.nlsTagCounter > 0) {
1361            return;
1362        }
1363        if (lastNumberOfNewLines >= 1) {
1364            column = 1; // ensure that the scribe is at the beginning of a new line
1365
return;
1366        }
1367        addInsertEdit(this.scanner.getCurrentTokenEndPosition() + 1, this.lineSeparator);
1368        line++;
1369        lastNumberOfNewLines = 1;
1370        column = 1;
1371        needSpace = false;
1372        this.pendingSpace = false;
1373    }
1374
1375    public void printNewLine(int insertPosition) {
1376        if (this.nlsTagCounter > 0) {
1377            return;
1378        }
1379        if (lastNumberOfNewLines >= 1) {
1380            column = 1; // ensure that the scribe is at the beginning of a new line
1381
return;
1382        }
1383        addInsertEdit(insertPosition, this.lineSeparator);
1384        line++;
1385        lastNumberOfNewLines = 1;
1386        column = 1;
1387        needSpace = false;
1388        this.pendingSpace = false;
1389    }
1390
1391    public void printNextToken(int expectedTokenType){
1392        printNextToken(expectedTokenType, false);
1393    }
1394
1395    public void printNextToken(int expectedTokenType, boolean considerSpaceIfAny){
1396        printComment();
1397        try {
1398            this.currentToken = this.scanner.getNextToken();
1399            char[] currentTokenSource = this.scanner.getRawTokenSource();
1400            if (expectedTokenType != this.currentToken) {
1401                throw new AbortFormatting("unexpected token type, expecting:"+expectedTokenType+", actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$
1402
}
1403            this.print(currentTokenSource, considerSpaceIfAny);
1404        } catch (InvalidInputException e) {
1405            throw new AbortFormatting(e);
1406        }
1407    }
1408
1409    public void printNextToken(int[] expectedTokenTypes) {
1410        printNextToken(expectedTokenTypes, false);
1411    }
1412
1413    public void printNextToken(int[] expectedTokenTypes, boolean considerSpaceIfAny){
1414        printComment();
1415        try {
1416            this.currentToken = this.scanner.getNextToken();
1417            char[] currentTokenSource = this.scanner.getRawTokenSource();
1418            if (Arrays.binarySearch(expectedTokenTypes, this.currentToken) < 0) {
1419                StringBuffer JavaDoc expectations = new StringBuffer JavaDoc(5);
1420                for (int i = 0; i < expectedTokenTypes.length; i++){
1421                    if (i > 0) {
1422                        expectations.append(',');
1423                    }
1424                    expectations.append(expectedTokenTypes[i]);
1425                }
1426                throw new AbortFormatting("unexpected token type, expecting:["+expectations.toString()+"], actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$
1427
}
1428            this.print(currentTokenSource, considerSpaceIfAny);
1429        } catch (InvalidInputException e) {
1430            throw new AbortFormatting(e);
1431        }
1432    }
1433
1434    public void printArrayQualifiedReference(int numberOfTokens, int sourceEnd) {
1435        int currentTokenStartPosition = this.scanner.currentPosition;
1436        int numberOfIdentifiers = 0;
1437        try {
1438            do {
1439                this.printComment();
1440                switch(this.currentToken = this.scanner.getNextToken()) {
1441                    case TerminalTokens.TokenNameEOF :
1442                        return;
1443                    case TerminalTokens.TokenNameWHITESPACE :
1444                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1445                        currentTokenStartPosition = this.scanner.currentPosition;
1446                        break;
1447                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1448                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1449                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1450                        currentTokenStartPosition = this.scanner.currentPosition;
1451                        break;
1452                    case TerminalTokens.TokenNameCOMMENT_LINE :
1453                        this.printLineComment(this.scanner.getRawTokenSource());
1454                        currentTokenStartPosition = this.scanner.currentPosition;
1455                        break;
1456                    case TerminalTokens.TokenNameIdentifier :
1457                        this.print(this.scanner.getRawTokenSource(), false);
1458                        currentTokenStartPosition = this.scanner.currentPosition;
1459                        if (++ numberOfIdentifiers == numberOfTokens) {
1460                            this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1461                            return;
1462                        }
1463                        break;
1464                    case TerminalTokens.TokenNameDOT :
1465                        this.print(this.scanner.getRawTokenSource(), false);
1466                        currentTokenStartPosition = this.scanner.currentPosition;
1467                        break;
1468                    default:
1469                        this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1470                        return;
1471                }
1472            } while (this.scanner.currentPosition <= sourceEnd);
1473        } catch(InvalidInputException e) {
1474            throw new AbortFormatting(e);
1475        }
1476    }
1477
1478    public void printQualifiedReference(int sourceEnd) {
1479        int currentTokenStartPosition = this.scanner.currentPosition;
1480        try {
1481            do {
1482                this.printComment();
1483                switch(this.currentToken = this.scanner.getNextToken()) {
1484                    case TerminalTokens.TokenNameEOF :
1485                        return;
1486                    case TerminalTokens.TokenNameWHITESPACE :
1487                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1488                        currentTokenStartPosition = this.scanner.currentPosition;
1489                        break;
1490                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1491                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1492                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1493                        currentTokenStartPosition = this.scanner.currentPosition;
1494                        break;
1495                    case TerminalTokens.TokenNameCOMMENT_LINE :
1496                        this.printLineComment(this.scanner.getRawTokenSource());
1497                        currentTokenStartPosition = this.scanner.currentPosition;
1498                        break;
1499                    case TerminalTokens.TokenNameIdentifier :
1500                    case TerminalTokens.TokenNameDOT :
1501                        this.print(this.scanner.getRawTokenSource(), false);
1502                        currentTokenStartPosition = this.scanner.currentPosition;
1503                        break;
1504                    default:
1505                        this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1506                        return;
1507                }
1508            } while (this.scanner.currentPosition <= sourceEnd);
1509        } catch(InvalidInputException e) {
1510            throw new AbortFormatting(e);
1511        }
1512    }
1513
1514    private void printRule(StringBuffer JavaDoc stringBuffer) {
1515        for (int i = 0; i < this.pageWidth; i++){
1516            if ((i % this.tabLength) == 0) {
1517                stringBuffer.append('+');
1518            } else {
1519                stringBuffer.append('-');
1520            }
1521        }
1522        stringBuffer.append(this.lineSeparator);
1523        
1524        for (int i = 0; i < (pageWidth / tabLength); i++) {
1525            stringBuffer.append(i);
1526            stringBuffer.append('\t');
1527        }
1528    }
1529
1530    public void printTrailingComment(int numberOfNewLinesToInsert) {
1531        try {
1532            // if we have a space between two tokens we ensure it will be dumped in the formatted string
1533
int currentTokenStartPosition = this.scanner.currentPosition;
1534            boolean hasWhitespaces = false;
1535            boolean hasLineComment = false;
1536            while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
1537                switch(this.currentToken) {
1538                    case TerminalTokens.TokenNameWHITESPACE :
1539                        int count = 0;
1540                        char[] whiteSpaces = this.scanner.getCurrentTokenSource();
1541                        for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1542                            switch(whiteSpaces[i]) {
1543                                case '\r' :
1544                                    if ((i + 1) < max) {
1545                                        if (whiteSpaces[i + 1] == '\n') {
1546                                            i++;
1547                                        }
1548                                    }
1549                                    count++;
1550                                    break;
1551                                case '\n' :
1552                                    count++;
1553                            }
1554                        }
1555                        if (hasLineComment) {
1556                            if (count >= 1) {
1557                                currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
1558                                this.preserveEmptyLines(numberOfNewLinesToInsert, currentTokenStartPosition);
1559                                addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition());
1560                                this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1);
1561                                return;
1562                            } else {
1563                                this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1564                                return;
1565                            }
1566                        } else if (count > 1) {
1567                            this.printEmptyLines(numberOfNewLinesToInsert, this.scanner.getCurrentTokenStartPosition());
1568                            this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1569                            return;
1570                        } else {
1571                            hasWhitespaces = true;
1572                            currentTokenStartPosition = this.scanner.currentPosition;
1573                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1574                        }
1575                        break;
1576                    case TerminalTokens.TokenNameCOMMENT_LINE :
1577                        if (hasWhitespaces) {
1578                            space();
1579                        }
1580                        this.printLineComment(this.scanner.getRawTokenSource());
1581                        currentTokenStartPosition = this.scanner.currentPosition;
1582                        hasLineComment = true;
1583                        break;
1584                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1585                        if (hasWhitespaces) {
1586                            space();
1587                        }
1588                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1589                        currentTokenStartPosition = this.scanner.currentPosition;
1590                        break;
1591                    default :
1592                        // step back one token
1593
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1594                        return;
1595                }
1596            }
1597        } catch (InvalidInputException e) {
1598            throw new AbortFormatting(e);
1599        }
1600    }
1601    public void printTrailingComment() {
1602        try {
1603            // if we have a space between two tokens we ensure it will be dumped in the formatted string
1604
int currentTokenStartPosition = this.scanner.currentPosition;
1605            boolean hasWhitespaces = false;
1606            boolean hasComment = false;
1607            boolean hasLineComment = false;
1608            while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
1609                switch(this.currentToken) {
1610                    case TerminalTokens.TokenNameWHITESPACE :
1611                        int count = 0;
1612                        char[] whiteSpaces = this.scanner.getCurrentTokenSource();
1613                        for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1614                            switch(whiteSpaces[i]) {
1615                                case '\r' :
1616                                    if ((i + 1) < max) {
1617                                        if (whiteSpaces[i + 1] == '\n') {
1618                                            i++;
1619                                        }
1620                                    }
1621                                    count++;
1622                                    break;
1623                                case '\n' :
1624                                    count++;
1625                            }
1626                        }
1627                        if (hasLineComment) {
1628                            if (count >= 1) {
1629                                currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
1630                                this.preserveEmptyLines(count, currentTokenStartPosition);
1631                                addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition());
1632                                this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1);
1633                                return;
1634                            } else {
1635                                this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1636                                return;
1637                            }
1638                        } else if (count >= 1) {
1639                            if (hasComment) {
1640                                this.printNewLine(this.scanner.getCurrentTokenStartPosition());
1641                            }
1642                            this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1643                            return;
1644                        } else {
1645                            hasWhitespaces = true;
1646                            currentTokenStartPosition = this.scanner.currentPosition;
1647                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1648                        }
1649                        break;
1650                    case TerminalTokens.TokenNameCOMMENT_LINE :
1651                        if (hasWhitespaces) {
1652                            space();
1653                        }
1654                        this.printLineComment(this.scanner.getRawTokenSource());
1655                        currentTokenStartPosition = this.scanner.currentPosition;
1656                        hasLineComment = true;
1657                        break;
1658                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1659                        if (hasWhitespaces) {
1660                            space();
1661                        }
1662                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1663                        currentTokenStartPosition = this.scanner.currentPosition;
1664                        hasComment = true;
1665                        break;
1666                    default :
1667                        // step back one token
1668
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1669                        return;
1670                }
1671            }
1672        } catch (InvalidInputException e) {
1673            throw new AbortFormatting(e);
1674        }
1675    }
1676
1677    void redoAlignment(AlignmentException e){
1678        if (e.relativeDepth > 0) { // if exception targets a distinct context
1679
e.relativeDepth--; // record fact that current context got traversed
1680
this.currentAlignment = this.currentAlignment.enclosing; // pop currentLocation
1681
throw e; // rethrow
1682
}
1683        // reset scribe/scanner to restart at this given location
1684
this.resetAt(this.currentAlignment.location);
1685        this.scanner.resetTo(this.currentAlignment.location.inputOffset, this.scanner.eofPosition);
1686        // clean alignment chunkKind so it will think it is a new chunk again
1687
this.currentAlignment.chunkKind = 0;
1688    }
1689
1690    void redoMemberAlignment(AlignmentException e){
1691        // reset scribe/scanner to restart at this given location
1692
this.resetAt(this.memberAlignment.location);
1693        this.scanner.resetTo(this.memberAlignment.location.inputOffset, this.scanner.eofPosition);
1694        // clean alignment chunkKind so it will think it is a new chunk again
1695
this.memberAlignment.chunkKind = 0;
1696    }
1697
1698    public void reset() {
1699        this.checkLineWrapping = true;
1700        this.line = 0;
1701        this.column = 1;
1702        this.editsIndex = 0;
1703        this.nlsTagCounter = 0;
1704    }
1705        
1706    private void resetAt(Location location) {
1707        this.line = location.outputLine;
1708        this.column = location.outputColumn;
1709        this.indentationLevel = location.outputIndentationLevel;
1710        this.numberOfIndentations = location.numberOfIndentations;
1711        this.lastNumberOfNewLines = location.lastNumberOfNewLines;
1712        this.needSpace = location.needSpace;
1713        this.pendingSpace = location.pendingSpace;
1714        this.editsIndex = location.editsIndex;
1715        this.nlsTagCounter = location.nlsTagCounter;
1716        if (this.editsIndex > 0) {
1717            this.edits[this.editsIndex - 1] = location.textEdit;
1718        }
1719        this.formatter.lastLocalDeclarationSourceStart = location.lastLocalDeclarationSourceStart;
1720    }
1721
1722    private void resize() {
1723        System.arraycopy(this.edits, 0, (this.edits = new OptimizedReplaceEdit[this.editsIndex * 2]), 0, this.editsIndex);
1724    }
1725
1726    public void space() {
1727        if (!this.needSpace) return;
1728        this.lastNumberOfNewLines = 0;
1729        this.pendingSpace = true;
1730        this.column++;
1731        this.needSpace = false;
1732    }
1733
1734    public String JavaDoc toString() {
1735        StringBuffer JavaDoc stringBuffer = new StringBuffer JavaDoc();
1736        stringBuffer
1737            .append("(page width = " + this.pageWidth + ") - (tabChar = ");//$NON-NLS-1$//$NON-NLS-2$
1738
switch(this.tabChar) {
1739            case DefaultCodeFormatterOptions.TAB :
1740                 stringBuffer.append("TAB");//$NON-NLS-1$
1741
break;
1742            case DefaultCodeFormatterOptions.SPACE :
1743                 stringBuffer.append("SPACE");//$NON-NLS-1$
1744
break;
1745            default :
1746                 stringBuffer.append("MIXED");//$NON-NLS-1$
1747
}
1748        stringBuffer
1749            .append(") - (tabSize = " + this.tabLength + ")")//$NON-NLS-1$//$NON-NLS-2$
1750
.append(this.lineSeparator)
1751            .append("(line = " + this.line + ") - (column = " + this.column + ") - (identationLevel = " + this.indentationLevel + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1752
.append(this.lineSeparator)
1753            .append("(needSpace = " + this.needSpace + ") - (lastNumberOfNewLines = " + this.lastNumberOfNewLines + ") - (checkLineWrapping = " + this.checkLineWrapping + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1754
.append(this.lineSeparator)
1755            .append("==================================================================================") //$NON-NLS-1$
1756
.append(this.lineSeparator);
1757        printRule(stringBuffer);
1758        return stringBuffer.toString();
1759    }
1760    
1761    public void unIndent() {
1762        this.indentationLevel -= this.indentationSize;
1763        this.numberOfIndentations--;
1764    }
1765}
1766
Popular Tags