KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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 import java.util.Collections JavaDoc;
15 import java.util.Comparator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18
19 import org.eclipse.jdt.core.JavaCore;
20 import org.eclipse.jdt.core.compiler.CharOperation;
21 import org.eclipse.jdt.core.compiler.InvalidInputException;
22 import org.eclipse.jdt.core.dom.ASTVisitor;
23 import org.eclipse.jdt.core.dom.Annotation;
24 import org.eclipse.jdt.core.dom.Comment;
25 import org.eclipse.jdt.core.dom.CompilationUnit;
26 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
27 import org.eclipse.jdt.internal.compiler.parser.Scanner;
28 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
29 import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
30 import org.eclipse.jdt.internal.formatter.align.Alignment;
31 import org.eclipse.jdt.internal.formatter.align.Alignment2;
32 import org.eclipse.jdt.internal.formatter.align.AlignmentException;
33 import org.eclipse.text.edits.MultiTextEdit;
34 import org.eclipse.text.edits.ReplaceEdit;
35 import org.eclipse.text.edits.TextEdit;
36
37 /**
38  * This class is responsible for dumping formatted source
39  * @since 2.1
40  */

41 public class Scribe2 {
42     public static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
43

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

364     public int getColumnIndentationLevel() {
365         return this.column - 1;
366     }
367     
368     public String JavaDoc getEmptyLines(int linesNumber) {
369         if (this.nlsTagCounter > 0) {
370             return EMPTY_STRING;
371         }
372         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
373         if (lastNumberOfNewLines == 0) {
374             linesNumber++; // add an extra line breaks
375
for (int i = 0; i < linesNumber; i++) {
376                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
377                 buffer.append(this.lineSeparator);
378             }
379             lastNumberOfNewLines += linesNumber;
380             line += linesNumber;
381             column = 1;
382             needSpace = false;
383             this.pendingSpace = false;
384         } else if (lastNumberOfNewLines == 1) {
385             for (int i = 0; i < linesNumber; i++) {
386                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
387                 buffer.append(this.lineSeparator);
388             }
389             lastNumberOfNewLines += linesNumber;
390             line += linesNumber;
391             column = 1;
392             needSpace = false;
393             this.pendingSpace = false;
394         } else {
395             if ((lastNumberOfNewLines - 1) >= linesNumber) {
396                 // there is no need to add new lines
397
return EMPTY_STRING;
398             }
399             final int realNewLineNumber = linesNumber - lastNumberOfNewLines + 1;
400             for (int i = 0; i < realNewLineNumber; i++) {
401                 if (indentEmptyLines) printIndentationIfNecessary(buffer);
402                 buffer.append(this.lineSeparator);
403             }
404             lastNumberOfNewLines += realNewLineNumber;
405             line += realNewLineNumber;
406             column = 1;
407             needSpace = false;
408             this.pendingSpace = false;
409         }
410         return String.valueOf(buffer);
411     }
412
413     public OptimizedReplaceEdit getLastEdit() {
414         if (this.editsIndex > 0) {
415             return this.edits[this.editsIndex - 1];
416         }
417         return null;
418     }
419     Alignment2 getMemberAlignment() {
420         return this.memberAlignment;
421     }
422     
423     public String JavaDoc getNewLine() {
424         if (this.nlsTagCounter > 0) {
425             return EMPTY_STRING;
426         }
427         if (lastNumberOfNewLines >= 1) {
428             column = 1; // ensure that the scribe is at the beginning of a new line
429
return EMPTY_STRING;
430         }
431         line++;
432         lastNumberOfNewLines = 1;
433         column = 1;
434         needSpace = false;
435         this.pendingSpace = false;
436         return this.lineSeparator;
437     }
438
439     /**
440      * Answer next indentation level based on column estimated position
441      * (if column is not indented, then use indentationLevel)
442      */

443     public int getNextIndentationLevel(int someColumn) {
444         int indent = someColumn - 1;
445         if (indent == 0)
446             return this.indentationLevel;
447         if (this.tabChar == DefaultCodeFormatterOptions.TAB) {
448             if (this.useTabsOnlyForLeadingIndents) {
449                 return indent;
450             }
451             int rem = indent % this.indentationSize;
452             int addition = rem == 0 ? 0 : this.indentationSize - rem; // round to superior
453
return indent + addition;
454         } else {
455             return indent;
456         }
457     }
458
459     private String JavaDoc getPreserveEmptyLines(int count) {
460         if (count > 0) {
461             if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
462                 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve);
463                 return this.getEmptyLines(linesToPreserve);
464             } else {
465                 return getNewLine();
466             }
467         }
468         return EMPTY_STRING;
469     }
470     
471     public TextEdit getRootEdit() {
472         MultiTextEdit edit = null;
473         int length = this.textRegionEnd - this.textRegionStart + 1;
474         if (this.textRegionStart <= 0) {
475             if (length <= 0) {
476                 edit = new MultiTextEdit(0, 0);
477             } else {
478                 edit = new MultiTextEdit(0, this.textRegionEnd + 1);
479             }
480         } else {
481             edit = new MultiTextEdit(this.textRegionStart, this.textRegionEnd - this.textRegionStart + 1);
482         }
483         for (int i= 0, max = this.editsIndex; i < max; i++) {
484             OptimizedReplaceEdit currentEdit = edits[i];
485             if (isValidEdit(currentEdit)) {
486                 edit.addChild(new ReplaceEdit(currentEdit.offset, currentEdit.length, currentEdit.replacement));
487             }
488         }
489         this.edits = null;
490         return edit;
491     }
492     
493     public void handleLineTooLong() {
494         // search for closest breakable alignment, using tiebreak rules
495
// look for outermost breakable one
496
int relativeDepth = 0, outerMostDepth = -1;
497         Alignment2 targetAlignment = this.currentAlignment;
498         while (targetAlignment != null){
499             if (targetAlignment.tieBreakRule == Alignment.R_OUTERMOST && targetAlignment.couldBreak()){
500                 outerMostDepth = relativeDepth;
501             }
502             targetAlignment = targetAlignment.enclosing;
503             relativeDepth++;
504         }
505         if (outerMostDepth >= 0) {
506             throw new AlignmentException(AlignmentException.LINE_TOO_LONG, outerMostDepth);
507         }
508         // look for innermost breakable one
509
relativeDepth = 0;
510         targetAlignment = this.currentAlignment;
511         while (targetAlignment != null){
512             if (targetAlignment.couldBreak()){
513                 throw new AlignmentException(AlignmentException.LINE_TOO_LONG, relativeDepth);
514             }
515             targetAlignment = targetAlignment.enclosing;
516             relativeDepth++;
517         }
518         // did not find any breakable location - proceed
519
}
520
521     /*
522      * Check if there is a NLS tag on this line. If yes, return true, returns false otherwise.
523      */

524     private boolean hasNLSTag(int sourceStart) {
525         final Comment comment = this.unit.getAST().newBlockComment();
526         comment.setSourceRange(sourceStart, 1);
527         int index = Collections.binarySearch(this.comments, comment, new Comparator JavaDoc() {
528             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
529                 Comment comment1 = (Comment) o1;
530                 Comment comment2 = (Comment) o2;
531                 return comment1.getStartPosition() - comment2.getStartPosition();
532             }
533         });
534         final int lineNumber = this.unit.getLineNumber(sourceStart);
535         if (index < 0) {
536             index = -index - 1;
537             final int commentLength = this.comments.size();
538             for (int i = index; i < commentLength; i++) {
539                 Comment currentComment = (Comment) comments.get(i);
540                 final int start = currentComment.getStartPosition();
541                 if (this.unit.getLineNumber(start) == lineNumber) {
542                     if (currentComment.isLineComment()) {
543                         return CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, start) != -1;
544                     }
545                 } else {
546                     return false;
547                 }
548             }
549         }
550         return false;
551     }
552     
553     public void indent() {
554         this.indentationLevel += this.indentationSize;
555         this.numberOfIndentations++;
556     }
557
558     /**
559      * @param compilationUnitSource
560      */

561     public void initializeScanner(char[] compilationUnitSource) {
562         this.scanner.setSource(compilationUnitSource);
563         this.scannerEndPosition = compilationUnitSource.length;
564         this.scanner.resetTo(0, this.scannerEndPosition);
565         this.edits = new OptimizedReplaceEdit[INITIAL_SIZE];
566     }
567     
568     private boolean isValidEdit(OptimizedReplaceEdit edit) {
569         final int editLength= edit.length;
570         final int editReplacementLength= edit.replacement.length();
571         final int editOffset= edit.offset;
572         if (editLength != 0) {
573             if (this.textRegionStart <= editOffset && (editOffset + editLength - 1) <= this.textRegionEnd) {
574                 if (editReplacementLength != 0 && editLength == editReplacementLength) {
575                     for (int i = editOffset, max = editOffset + editLength; i < max; i++) {
576                         if (scanner.source[i] != edit.replacement.charAt(i - editOffset)) {
577                             return true;
578                         }
579                     }
580                     return false;
581                 } else {
582                     return true;
583                 }
584             } else if (editOffset + editLength == this.textRegionStart) {
585                 int i = editOffset;
586                 for (int max = editOffset + editLength; i < max; i++) {
587                     int replacementStringIndex = i - editOffset;
588                     if (replacementStringIndex >= editReplacementLength || scanner.source[i] != edit.replacement.charAt(replacementStringIndex)) {
589                         break;
590                     }
591                 }
592                 if (i - editOffset != editReplacementLength && i != editOffset + editLength - 1) {
593                     edit.offset = textRegionStart;
594                     edit.length = 0;
595                     edit.replacement = edit.replacement.substring(i - editOffset);
596                     return true;
597                 }
598             }
599         } else if (this.textRegionStart <= editOffset && editOffset <= this.textRegionEnd) {
600             return true;
601         } else if (editOffset == this.scannerEndPosition && editOffset == this.textRegionEnd + 1) {
602             return true;
603         }
604         return false;
605     }
606
607     private void preserveEmptyLines(int count, int insertPosition) {
608         if (count > 0) {
609             if (this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
610                 int linesToPreserve = Math.min(count, this.formatter.preferences.number_of_empty_lines_to_preserve);
611                 this.printEmptyLines(linesToPreserve, insertPosition);
612             } else {
613                 printNewLine(insertPosition);
614             }
615         }
616     }
617
618     private void print(char[] s, boolean considerSpaceIfAny) {
619         if (checkLineWrapping && s.length + column > this.pageWidth) {
620             handleLineTooLong();
621         }
622         this.lastNumberOfNewLines = 0;
623         printIndentationIfNecessary();
624         if (considerSpaceIfAny) {
625             this.space();
626         }
627         if (this.pendingSpace) {
628             this.addInsertEdit(this.scanner.getCurrentTokenStartPosition(), " "); //$NON-NLS-1$
629
}
630         this.pendingSpace = false;
631         this.needSpace = false;
632         column += s.length;
633         needSpace = true;
634     }
635
636     private void printBlockComment(char[] s, boolean isJavadoc) {
637         int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
638         int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1;
639         
640         this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1);
641         int currentCharacter;
642         boolean isNewLine = false;
643         int start = currentTokenStartPosition;
644         int nextCharacterStart = currentTokenStartPosition;
645         printIndentationIfNecessary();
646         if (this.pendingSpace) {
647             this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$
648
}
649         this.needSpace = false;
650         this.pendingSpace = false;
651         int previousStart = currentTokenStartPosition;
652
653         while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) {
654             nextCharacterStart = this.scanner.currentPosition;
655
656             switch(currentCharacter) {
657                 case '\r' :
658                     start = previousStart;
659                     isNewLine = true;
660                     if (this.scanner.getNextChar('\n')) {
661                         currentCharacter = '\n';
662                         nextCharacterStart = this.scanner.currentPosition;
663                     }
664                     break;
665                 case '\n' :
666                     start = previousStart;
667                     isNewLine = true;
668                     break;
669                 default:
670                     if (isNewLine) {
671                         if (ScannerHelper.isWhitespace((char) currentCharacter)) {
672                             int previousStartPosition = this.scanner.currentPosition;
673                             while(currentCharacter != -1 && currentCharacter != '\r' && currentCharacter != '\n' && ScannerHelper.isWhitespace((char) currentCharacter)) {
674                                 previousStart = nextCharacterStart;
675                                 previousStartPosition = this.scanner.currentPosition;
676                                 currentCharacter = this.scanner.getNextChar();
677                                 nextCharacterStart = this.scanner.currentPosition;
678                             }
679                             if (currentCharacter == '\r' || currentCharacter == '\n') {
680                                 nextCharacterStart = previousStartPosition;
681                             }
682                         }
683                         this.column = 1;
684                         this.line++;
685
686                         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
687                         buffer.append(this.lineSeparator);
688                         printIndentationIfNecessary(buffer);
689                         buffer.append(' ');
690                 
691                         addReplaceEdit(start, previousStart - 1, String.valueOf(buffer));
692                     } else {
693                         this.column += (nextCharacterStart - previousStart);
694                     }
695                     isNewLine = false;
696             }
697             previousStart = nextCharacterStart;
698             this.scanner.currentPosition = nextCharacterStart;
699         }
700         this.lastNumberOfNewLines = 0;
701         needSpace = false;
702         this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1);
703         if (isJavadoc) {
704             printNewLine();
705         }
706     }
707     
708     public void printEndOfCompilationUnit() {
709         try {
710             // if we have a space between two tokens we ensure it will be dumped in the formatted string
711
int currentTokenStartPosition = this.scanner.currentPosition;
712             boolean hasComment = false;
713             boolean hasLineComment = false;
714             boolean hasWhitespace = false;
715             int count = 0;
716             while (true) {
717                 this.currentToken = this.scanner.getNextToken();
718                 switch(this.currentToken) {
719                     case TerminalTokens.TokenNameWHITESPACE :
720                         char[] whiteSpaces = this.scanner.getCurrentTokenSource();
721                         count = 0;
722                         for (int i = 0, max = whiteSpaces.length; i < max; i++) {
723                             switch(whiteSpaces[i]) {
724                                 case '\r' :
725                                     if ((i + 1) < max) {
726                                         if (whiteSpaces[i + 1] == '\n') {
727                                             i++;
728                                         }
729                                     }
730                                     count++;
731                                     break;
732                                 case '\n' :
733                                     count++;
734                             }
735                         }
736                         if (count == 0) {
737                             hasWhitespace = true;
738                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
739                         } else if (hasComment) {
740                             if (count == 1) {
741                                 this.printNewLine(this.scanner.getCurrentTokenStartPosition());
742                             } else {
743                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
744                             }
745                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
746                         } else if (hasLineComment) {
747                             this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition());
748                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
749                         } else {
750                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
751                         }
752                         currentTokenStartPosition = this.scanner.currentPosition;
753                         break;
754                     case TerminalTokens.TokenNameCOMMENT_LINE :
755                         if (count >= 1) {
756                             if (count > 1) {
757                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
758                             } else if (count == 1) {
759                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
760                             }
761                         } else if (hasWhitespace) {
762                             space();
763                         }
764                         hasWhitespace = false;
765                         this.printCommentLine(this.scanner.getRawTokenSource());
766                         currentTokenStartPosition = this.scanner.currentPosition;
767                         hasLineComment = true;
768                         count = 0;
769                         break;
770                     case TerminalTokens.TokenNameCOMMENT_BLOCK :
771                         if (count >= 1) {
772                             if (count > 1) {
773                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
774                             } else if (count == 1) {
775                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
776                             }
777                         } else if (hasWhitespace) {
778                             space();
779                         }
780                         hasWhitespace = false;
781                         this.printBlockComment(this.scanner.getRawTokenSource(), false);
782                         currentTokenStartPosition = this.scanner.currentPosition;
783                         hasLineComment = false;
784                         hasComment = true;
785                         count = 0;
786                         break;
787                     case TerminalTokens.TokenNameCOMMENT_JAVADOC :
788                         if (count >= 1) {
789                             if (count > 1) {
790                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
791                             } else if (count == 1) {
792                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
793                             }
794                         } else if (hasWhitespace) {
795                             space();
796                         }
797                         hasWhitespace = false;
798                         this.printBlockComment(this.scanner.getRawTokenSource(), true);
799                         currentTokenStartPosition = this.scanner.currentPosition;
800                         hasLineComment = false;
801                         hasComment = true;
802                         count = 0;
803                         break;
804                     case TerminalTokens.TokenNameSEMICOLON :
805                         char[] currentTokenSource = this.scanner.getRawTokenSource();
806                         this.print(currentTokenSource, this.formatter.preferences.insert_space_before_semicolon);
807                         break;
808                     case TerminalTokens.TokenNameEOF :
809                         if (count >= 1 || this.formatter.preferences.insert_new_line_at_end_of_file_if_missing) {
810                             this.printNewLine(this.scannerEndPosition);
811                         }
812                         return;
813                     default :
814                         // step back one token
815
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
816                         return;
817                 }
818             }
819         } catch (InvalidInputException e) {
820             throw new AbortFormatting(e);
821         }
822     }
823
824     public void printComment() {
825         try {
826             // if we have a space between two tokens we ensure it will be dumped in the formatted string
827
int currentTokenStartPosition = this.scanner.currentPosition;
828             boolean hasComment = false;
829             boolean hasLineComment = false;
830             boolean hasWhitespace = false;
831             int count = 0;
832             while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
833                 switch(this.currentToken) {
834                     case TerminalTokens.TokenNameWHITESPACE :
835                         char[] whiteSpaces = this.scanner.getCurrentTokenSource();
836                         count = 0;
837                         for (int i = 0, max = whiteSpaces.length; i < max; i++) {
838                             switch(whiteSpaces[i]) {
839                                 case '\r' :
840                                     if ((i + 1) < max) {
841                                         if (whiteSpaces[i + 1] == '\n') {
842                                             i++;
843                                         }
844                                     }
845                                     count++;
846                                     break;
847                                 case '\n' :
848                                     count++;
849                             }
850                         }
851                         if (count == 0) {
852                             hasWhitespace = true;
853                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
854                         } else if (hasComment) {
855                             if (count == 1) {
856                                 this.printNewLine(this.scanner.getCurrentTokenStartPosition());
857                             } else {
858                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
859                             }
860                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
861                         } else if (hasLineComment) {
862                             this.preserveEmptyLines(count, this.scanner.getCurrentTokenStartPosition());
863                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
864                         } else if (count != 0 && this.formatter.preferences.number_of_empty_lines_to_preserve != 0) {
865                             addReplaceEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition(), this.getPreserveEmptyLines(count - 1));
866                         } else {
867                             addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
868                         }
869                         currentTokenStartPosition = this.scanner.currentPosition;
870                         break;
871                     case TerminalTokens.TokenNameCOMMENT_LINE :
872                         if (count >= 1) {
873                             if (count > 1) {
874                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
875                             } else if (count == 1) {
876                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
877                             }
878                         } else if (hasWhitespace) {
879                             space();
880                         }
881                         hasWhitespace = false;
882                         this.printCommentLine(this.scanner.getRawTokenSource());
883                         currentTokenStartPosition = this.scanner.currentPosition;
884                         hasLineComment = true;
885                         count = 0;
886                         break;
887                     case TerminalTokens.TokenNameCOMMENT_BLOCK :
888                         if (count >= 1) {
889                             if (count > 1) {
890                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
891                             } else if (count == 1) {
892                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
893                             }
894                         } else if (hasWhitespace) {
895                             space();
896                         }
897                         hasWhitespace = false;
898                         this.printBlockComment(this.scanner.getRawTokenSource(), false);
899                         currentTokenStartPosition = this.scanner.currentPosition;
900                         hasLineComment = false;
901                         hasComment = true;
902                         count = 0;
903                         break;
904                     case TerminalTokens.TokenNameCOMMENT_JAVADOC :
905                         if (count >= 1) {
906                             if (count > 1) {
907                                 preserveEmptyLines(count - 1, this.scanner.getCurrentTokenStartPosition());
908                             } else if (count == 1) {
909                                 printNewLine(this.scanner.getCurrentTokenStartPosition());
910                             }
911                         } else if (hasWhitespace) {
912                             space();
913                         }
914                         hasWhitespace = false;
915                         this.printBlockComment(this.scanner.getRawTokenSource(), true);
916                         currentTokenStartPosition = this.scanner.currentPosition;
917                         hasLineComment = false;
918                         hasComment = true;
919                         count = 0;
920                         break;
921                     default :
922                         // step back one token
923
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
924                         return;
925                 }
926             }
927         } catch (InvalidInputException e) {
928             throw new AbortFormatting(e);
929         }
930     }
931     
932     private void printCommentLine(char[] s) {
933         int currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
934         int currentTokenEndPosition = this.scanner.getCurrentTokenEndPosition() + 1;
935         if (CharOperation.indexOf(Scanner.TAG_PREFIX, this.scanner.source, true, currentTokenStartPosition) != -1) {
936             this.nlsTagCounter = 0;
937         }
938         this.scanner.resetTo(currentTokenStartPosition, currentTokenEndPosition - 1);
939         int currentCharacter;
940         int start = currentTokenStartPosition;
941         int nextCharacterStart = currentTokenStartPosition;
942         printIndentationIfNecessary();
943         if (this.pendingSpace) {
944             this.addInsertEdit(currentTokenStartPosition, " "); //$NON-NLS-1$
945
}
946         this.needSpace = false;
947         this.pendingSpace = false;
948         int previousStart = currentTokenStartPosition;
949
950         loop: while (nextCharacterStart <= currentTokenEndPosition && (currentCharacter = this.scanner.getNextChar()) != -1) {
951             nextCharacterStart = this.scanner.currentPosition;
952
953             switch(currentCharacter) {
954                 case '\r' :
955                     start = previousStart;
956                     break loop;
957                 case '\n' :
958                     start = previousStart;
959                     break loop;
960             }
961             previousStart = nextCharacterStart;
962         }
963         if (start != currentTokenStartPosition) {
964             addReplaceEdit(start, currentTokenEndPosition - 1, lineSeparator);
965         }
966         line++;
967         column = 1;
968         needSpace = false;
969         this.pendingSpace = false;
970         lastNumberOfNewLines = 1;
971         // realign to the proper value
972
if (this.currentAlignment != null) {
973             if (this.memberAlignment != null) {
974                 // select the last alignment
975
if (this.currentAlignment.location.inputOffset > this.memberAlignment.location.inputOffset) {
976                     if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) {
977                         this.currentAlignment.performFragmentEffect();
978                     }
979                 } else {
980                     this.indentationLevel = Math.max(this.indentationLevel, this.memberAlignment.breakIndentationLevel);
981                 }
982             } else if (this.currentAlignment.couldBreak() && this.currentAlignment.wasSplit) {
983                 this.currentAlignment.performFragmentEffect();
984             }
985         }
986         this.scanner.resetTo(currentTokenEndPosition, this.scannerEndPosition - 1);
987     }
988     public void printEmptyLines(int linesNumber) {
989         this.printEmptyLines(linesNumber, this.scanner.getCurrentTokenEndPosition() + 1);
990     }
991
992     private void printEmptyLines(int linesNumber, int insertPosition) {
993         final String JavaDoc buffer = getEmptyLines(linesNumber);
994         if (EMPTY_STRING == buffer) return;
995         
996         addInsertEdit(insertPosition, buffer);
997     }
998
999     void printIndentationIfNecessary() {
1000        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1001        printIndentationIfNecessary(buffer);
1002        if (buffer.length() > 0) {
1003            addInsertEdit(this.scanner.getCurrentTokenStartPosition(), buffer.toString());
1004            this.pendingSpace = false;
1005        }
1006    }
1007
1008    private void printIndentationIfNecessary(StringBuffer JavaDoc buffer) {
1009        switch(this.tabChar) {
1010            case DefaultCodeFormatterOptions.TAB :
1011                boolean useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents;
1012                int numberOfLeadingIndents = this.numberOfIndentations;
1013                int indentationsAsTab = 0;
1014                if (useTabsForLeadingIndents) {
1015                    while (this.column <= this.indentationLevel) {
1016                        if (indentationsAsTab < numberOfLeadingIndents) {
1017                            buffer.append('\t');
1018                            indentationsAsTab++;
1019                            this.lastNumberOfNewLines = 0;
1020                            int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space
1021
this.column += complement;
1022                            this.needSpace = false;
1023                        } else {
1024                            buffer.append(' ');
1025                            this.column++;
1026                            this.needSpace = false;
1027                        }
1028                    }
1029                } else {
1030                    while (this.column <= this.indentationLevel) {
1031                        buffer.append('\t');
1032                        this.lastNumberOfNewLines = 0;
1033                        int complement = this.tabLength - ((this.column - 1) % this.tabLength); // amount of space
1034
this.column += complement;
1035                        this.needSpace = false;
1036                    }
1037                }
1038                break;
1039            case DefaultCodeFormatterOptions.SPACE :
1040                while (this.column <= this.indentationLevel) {
1041                    buffer.append(' ');
1042                    this.column++;
1043                    this.needSpace = false;
1044                }
1045                break;
1046            case DefaultCodeFormatterOptions.MIXED :
1047                useTabsForLeadingIndents = this.useTabsOnlyForLeadingIndents;
1048                numberOfLeadingIndents = this.numberOfIndentations;
1049                indentationsAsTab = 0;
1050                if (useTabsForLeadingIndents) {
1051                    final int columnForLeadingIndents = numberOfLeadingIndents * this.indentationSize;
1052                    while (this.column <= this.indentationLevel) {
1053                        if (this.column <= columnForLeadingIndents) {
1054                            if ((this.column - 1 + this.tabLength) <= this.indentationLevel) {
1055                                buffer.append('\t');
1056                                this.column += this.tabLength;
1057                            } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) {
1058                                // print one indentation
1059
for (int i = 0, max = this.indentationSize; i < max; i++) {
1060                                    buffer.append(' ');
1061                                    this.column++;
1062                                }
1063                            } else {
1064                                buffer.append(' ');
1065                                this.column++;
1066                            }
1067                        } else {
1068                            for (int i = this.column, max = this.indentationLevel; i <= max; i++) {
1069                                buffer.append(' ');
1070                                this.column++;
1071                            }
1072                        }
1073                        this.needSpace = false;
1074                    }
1075                } else {
1076                    while (this.column <= this.indentationLevel) {
1077                        if ((this.column - 1 + this.tabLength) <= this.indentationLevel) {
1078                            buffer.append('\t');
1079                            this.column += this.tabLength;
1080                        } else if ((this.column - 1 + this.indentationSize) <= this.indentationLevel) {
1081                            // print one indentation
1082
for (int i = 0, max = this.indentationSize; i < max; i++) {
1083                                buffer.append(' ');
1084                                this.column++;
1085                            }
1086                        } else {
1087                            buffer.append(' ');
1088                            this.column++;
1089                        }
1090                        this.needSpace = false;
1091                    }
1092                }
1093                break;
1094        }
1095    }
1096
1097    /**
1098     * @param modifiers list of IExtendedModifiers
1099     * @param visitor
1100     */

1101    public void printModifiers(List JavaDoc modifiers, ASTVisitor visitor) {
1102        try {
1103            int modifiersIndex = 0;
1104            boolean isFirstModifier = true;
1105            int currentTokenStartPosition = this.scanner.currentPosition;
1106            boolean hasComment = false;
1107            while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
1108                switch(this.currentToken) {
1109                    case TerminalTokens.TokenNamepublic :
1110                    case TerminalTokens.TokenNameprotected :
1111                    case TerminalTokens.TokenNameprivate :
1112                    case TerminalTokens.TokenNamestatic :
1113                    case TerminalTokens.TokenNameabstract :
1114                    case TerminalTokens.TokenNamefinal :
1115                    case TerminalTokens.TokenNamenative :
1116                    case TerminalTokens.TokenNamesynchronized :
1117                    case TerminalTokens.TokenNametransient :
1118                    case TerminalTokens.TokenNamevolatile :
1119                    case TerminalTokens.TokenNamestrictfp :
1120                        this.print(this.scanner.getRawTokenSource(), !isFirstModifier);
1121                        isFirstModifier = false;
1122                        currentTokenStartPosition = this.scanner.currentPosition;
1123                        modifiersIndex++;
1124                        break;
1125                    case TerminalTokens.TokenNameAT :
1126                        if (!isFirstModifier) {
1127                            this.space();
1128                        }
1129                        this.scanner.resetTo(this.scanner.getCurrentTokenStartPosition(), this.scannerEndPosition - 1);
1130                        ((Annotation) modifiers.get(modifiersIndex)).accept(visitor);
1131                        if (this.formatter.preferences.insert_new_line_after_annotation) {
1132                            this.printNewLine();
1133                        }
1134                        isFirstModifier = false;
1135                        currentTokenStartPosition = this.scanner.currentPosition;
1136                        modifiersIndex++;
1137                        break;
1138                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1139                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1140                        currentTokenStartPosition = this.scanner.currentPosition;
1141                        hasComment = true;
1142                        break;
1143                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1144                        this.printBlockComment(this.scanner.getRawTokenSource(), true);
1145                        currentTokenStartPosition = this.scanner.currentPosition;
1146                        hasComment = true;
1147                        break;
1148                    case TerminalTokens.TokenNameCOMMENT_LINE :
1149                        this.printCommentLine(this.scanner.getRawTokenSource());
1150                        currentTokenStartPosition = this.scanner.currentPosition;
1151                        break;
1152                    case TerminalTokens.TokenNameWHITESPACE :
1153                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1154                        int count = 0;
1155                        char[] whiteSpaces = this.scanner.getCurrentTokenSource();
1156                        for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1157                            switch(whiteSpaces[i]) {
1158                                case '\r' :
1159                                    if ((i + 1) < max) {
1160                                        if (whiteSpaces[i + 1] == '\n') {
1161                                            i++;
1162                                        }
1163                                    }
1164                                    count++;
1165                                    break;
1166                                case '\n' :
1167                                    count++;
1168                            }
1169                        }
1170                        if (count >= 1 && hasComment) {
1171                            printNewLine();
1172                        }
1173                        currentTokenStartPosition = this.scanner.currentPosition;
1174                        hasComment = false;
1175                        break;
1176                    default:
1177                        // step back one token
1178
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1179                        return;
1180                }
1181            }
1182        } catch (InvalidInputException e) {
1183            throw new AbortFormatting(e);
1184        }
1185    }
1186    
1187    public void printNewLine() {
1188        if (this.nlsTagCounter > 0) {
1189            return;
1190        }
1191        if (lastNumberOfNewLines >= 1) {
1192            column = 1; // ensure that the scribe is at the beginning of a new line
1193
return;
1194        }
1195        addInsertEdit(this.scanner.getCurrentTokenEndPosition() + 1, this.lineSeparator);
1196        line++;
1197        lastNumberOfNewLines = 1;
1198        column = 1;
1199        needSpace = false;
1200        this.pendingSpace = false;
1201    }
1202
1203    public void printNewLine(int insertPosition) {
1204        if (this.nlsTagCounter > 0) {
1205            return;
1206        }
1207        if (lastNumberOfNewLines >= 1) {
1208            column = 1; // ensure that the scribe is at the beginning of a new line
1209
return;
1210        }
1211        addInsertEdit(insertPosition, this.lineSeparator);
1212        line++;
1213        lastNumberOfNewLines = 1;
1214        column = 1;
1215        needSpace = false;
1216        this.pendingSpace = false;
1217    }
1218
1219    public void printNextToken(int expectedTokenType){
1220        printNextToken(expectedTokenType, false);
1221    }
1222
1223    public void printNextToken(int expectedTokenType, boolean considerSpaceIfAny){
1224        printComment();
1225        try {
1226            this.currentToken = this.scanner.getNextToken();
1227            char[] currentTokenSource = this.scanner.getRawTokenSource();
1228            if (expectedTokenType != this.currentToken) {
1229                throw new AbortFormatting("unexpected token type, expecting:"+expectedTokenType+", actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$
1230
}
1231            this.print(currentTokenSource, considerSpaceIfAny);
1232        } catch (InvalidInputException e) {
1233            throw new AbortFormatting(e);
1234        }
1235    }
1236
1237    public void printNextToken(int[] expectedTokenTypes) {
1238        printNextToken(expectedTokenTypes, false);
1239    }
1240
1241    public void printNextToken(int[] expectedTokenTypes, boolean considerSpaceIfAny){
1242        printComment();
1243        try {
1244            this.currentToken = this.scanner.getNextToken();
1245            char[] currentTokenSource = this.scanner.getRawTokenSource();
1246            if (Arrays.binarySearch(expectedTokenTypes, this.currentToken) < 0) {
1247                StringBuffer JavaDoc expectations = new StringBuffer JavaDoc(5);
1248                for (int i = 0; i < expectedTokenTypes.length; i++){
1249                    if (i > 0) {
1250                        expectations.append(',');
1251                    }
1252                    expectations.append(expectedTokenTypes[i]);
1253                }
1254                throw new AbortFormatting("unexpected token type, expecting:["+expectations.toString()+"], actual:"+this.currentToken);//$NON-NLS-1$//$NON-NLS-2$
1255
}
1256            this.print(currentTokenSource, considerSpaceIfAny);
1257        } catch (InvalidInputException e) {
1258            throw new AbortFormatting(e);
1259        }
1260    }
1261
1262    public void printArrayQualifiedReference(int numberOfTokens, int sourceEnd) {
1263        int currentTokenStartPosition = this.scanner.currentPosition;
1264        int numberOfIdentifiers = 0;
1265        try {
1266            do {
1267                this.printComment();
1268                switch(this.currentToken = this.scanner.getNextToken()) {
1269                    case TerminalTokens.TokenNameEOF :
1270                        return;
1271                    case TerminalTokens.TokenNameWHITESPACE :
1272                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1273                        currentTokenStartPosition = this.scanner.currentPosition;
1274                        break;
1275                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1276                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1277                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1278                        currentTokenStartPosition = this.scanner.currentPosition;
1279                        break;
1280                    case TerminalTokens.TokenNameCOMMENT_LINE :
1281                        this.printCommentLine(this.scanner.getRawTokenSource());
1282                        currentTokenStartPosition = this.scanner.currentPosition;
1283                        break;
1284                    case TerminalTokens.TokenNameIdentifier :
1285                        this.print(this.scanner.getRawTokenSource(), false);
1286                        currentTokenStartPosition = this.scanner.currentPosition;
1287                        if (++ numberOfIdentifiers == numberOfTokens) {
1288                            this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1289                            return;
1290                        }
1291                        break;
1292                    case TerminalTokens.TokenNameDOT :
1293                        this.print(this.scanner.getRawTokenSource(), false);
1294                        currentTokenStartPosition = this.scanner.currentPosition;
1295                        break;
1296                    default:
1297                        this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1298                        return;
1299                }
1300            } while (this.scanner.currentPosition <= sourceEnd);
1301        } catch(InvalidInputException e) {
1302            throw new AbortFormatting(e);
1303        }
1304    }
1305/*
1306    public void printQualifiedReference(Name name) {
1307        final int sourceEnd = name.getStartPosition() + name.getLength() - 1;
1308        int currentTokenStartPosition = this.scanner.currentPosition;
1309        try {
1310            do {
1311                this.printComment();
1312                switch(this.currentToken = this.scanner.getNextToken()) {
1313                    case TerminalTokens.TokenNameEOF :
1314                        return;
1315                    case TerminalTokens.TokenNameWHITESPACE :
1316                        addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1317                        currentTokenStartPosition = this.scanner.currentPosition;
1318                        break;
1319                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1320                    case TerminalTokens.TokenNameCOMMENT_JAVADOC :
1321                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1322                        currentTokenStartPosition = this.scanner.currentPosition;
1323                        break;
1324                    case TerminalTokens.TokenNameCOMMENT_LINE :
1325                        this.printCommentLine(this.scanner.getRawTokenSource());
1326                        currentTokenStartPosition = this.scanner.currentPosition;
1327                        break;
1328                    case TerminalTokens.TokenNameIdentifier :
1329                    case TerminalTokens.TokenNameDOT :
1330                        this.print(this.scanner.getRawTokenSource(), false);
1331                        currentTokenStartPosition = this.scanner.currentPosition;
1332                        break;
1333                    default:
1334                        this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1335                        return;
1336                }
1337            } while (this.scanner.currentPosition <= sourceEnd);
1338        } catch(InvalidInputException e) {
1339            throw new AbortFormatting(e);
1340        }
1341    }
1342*/

1343    private void printRule(StringBuffer JavaDoc stringBuffer) {
1344        for (int i = 0; i < this.pageWidth; i++){
1345            if ((i % this.tabLength) == 0) {
1346                stringBuffer.append('+');
1347            } else {
1348                stringBuffer.append('-');
1349            }
1350        }
1351        stringBuffer.append(this.lineSeparator);
1352        
1353        for (int i = 0; i < (pageWidth / tabLength); i++) {
1354            stringBuffer.append(i);
1355            stringBuffer.append('\t');
1356        }
1357    }
1358
1359    public void printTrailingComment() {
1360        try {
1361            // if we have a space between two tokens we ensure it will be dumped in the formatted string
1362
int currentTokenStartPosition = this.scanner.currentPosition;
1363            boolean hasWhitespaces = false;
1364            boolean hasComment = false;
1365            boolean hasLineComment = false;
1366            while ((this.currentToken = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) {
1367                switch(this.currentToken) {
1368                    case TerminalTokens.TokenNameWHITESPACE :
1369                        int count = 0;
1370                        char[] whiteSpaces = this.scanner.getCurrentTokenSource();
1371                        for (int i = 0, max = whiteSpaces.length; i < max; i++) {
1372                            switch(whiteSpaces[i]) {
1373                                case '\r' :
1374                                    if ((i + 1) < max) {
1375                                        if (whiteSpaces[i + 1] == '\n') {
1376                                            i++;
1377                                        }
1378                                    }
1379                                    count++;
1380                                    break;
1381                                case '\n' :
1382                                    count++;
1383                            }
1384                        }
1385                        if (hasLineComment) {
1386                            if (count >= 1) {
1387                                currentTokenStartPosition = this.scanner.getCurrentTokenStartPosition();
1388                                this.preserveEmptyLines(count, currentTokenStartPosition);
1389                                addDeleteEdit(currentTokenStartPosition, this.scanner.getCurrentTokenEndPosition());
1390                                this.scanner.resetTo(this.scanner.currentPosition, this.scannerEndPosition - 1);
1391                                return;
1392                            } else {
1393                                this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1394                                return;
1395                            }
1396                        } else if (count >= 1) {
1397                            if (hasComment) {
1398                                this.printNewLine(this.scanner.getCurrentTokenStartPosition());
1399                            }
1400                            this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1401                            return;
1402                        } else {
1403                            hasWhitespaces = true;
1404                            currentTokenStartPosition = this.scanner.currentPosition;
1405                            addDeleteEdit(this.scanner.getCurrentTokenStartPosition(), this.scanner.getCurrentTokenEndPosition());
1406                        }
1407                        break;
1408                    case TerminalTokens.TokenNameCOMMENT_LINE :
1409                        if (hasWhitespaces) {
1410                            space();
1411                        }
1412                        this.printCommentLine(this.scanner.getRawTokenSource());
1413                        currentTokenStartPosition = this.scanner.currentPosition;
1414                        hasLineComment = true;
1415                        break;
1416                    case TerminalTokens.TokenNameCOMMENT_BLOCK :
1417                        if (hasWhitespaces) {
1418                            space();
1419                        }
1420                        this.printBlockComment(this.scanner.getRawTokenSource(), false);
1421                        currentTokenStartPosition = this.scanner.currentPosition;
1422                        hasComment = true;
1423                        break;
1424                    default :
1425                        // step back one token
1426
this.scanner.resetTo(currentTokenStartPosition, this.scannerEndPosition - 1);
1427                        return;
1428                }
1429            }
1430        } catch (InvalidInputException e) {
1431            throw new AbortFormatting(e);
1432        }
1433    }
1434
1435    void redoAlignment(AlignmentException e){
1436        if (e.relativeDepth > 0) { // if exception targets a distinct context
1437
e.relativeDepth--; // record fact that current context got traversed
1438
this.currentAlignment = this.currentAlignment.enclosing; // pop currentLocation
1439
throw e; // rethrow
1440
}
1441        // reset scribe/scanner to restart at this given location
1442
this.resetAt(this.currentAlignment.location);
1443        this.scanner.resetTo(this.currentAlignment.location.inputOffset, this.scanner.eofPosition);
1444        // clean alignment chunkKind so it will think it is a new chunk again
1445
this.currentAlignment.chunkKind = 0;
1446    }
1447
1448    void redoMemberAlignment(AlignmentException e){
1449        // reset scribe/scanner to restart at this given location
1450
this.resetAt(this.memberAlignment.location);
1451        this.scanner.resetTo(this.memberAlignment.location.inputOffset, this.scanner.eofPosition);
1452        // clean alignment chunkKind so it will think it is a new chunk again
1453
this.memberAlignment.chunkKind = 0;
1454    }
1455
1456    public void reset() {
1457        this.checkLineWrapping = true;
1458        this.line = 0;
1459        this.column = 1;
1460        this.editsIndex = 0;
1461        this.nlsTagCounter = 0;
1462    }
1463        
1464    private void resetAt(Location2 location) {
1465        this.line = location.outputLine;
1466        this.column = location.outputColumn;
1467        this.indentationLevel = location.outputIndentationLevel;
1468        this.numberOfIndentations = location.numberOfIndentations;
1469        this.lastNumberOfNewLines = location.lastNumberOfNewLines;
1470        this.needSpace = location.needSpace;
1471        this.pendingSpace = location.pendingSpace;
1472        this.editsIndex = location.editsIndex;
1473        this.nlsTagCounter = location.nlsTagCounter;
1474        if (this.editsIndex > 0) {
1475            this.edits[this.editsIndex - 1] = location.textEdit;
1476        }
1477    }
1478
1479    private void resize() {
1480        System.arraycopy(this.edits, 0, (this.edits = new OptimizedReplaceEdit[this.editsIndex * 2]), 0, this.editsIndex);
1481    }
1482
1483    public void space() {
1484        if (!this.needSpace) return;
1485        this.lastNumberOfNewLines = 0;
1486        this.pendingSpace = true;
1487        this.column++;
1488        this.needSpace = false;
1489    }
1490
1491    public String JavaDoc toString() {
1492        StringBuffer JavaDoc stringBuffer = new StringBuffer JavaDoc();
1493        stringBuffer
1494            .append("(page width = " + this.pageWidth + ") - (tabChar = ");//$NON-NLS-1$//$NON-NLS-2$
1495
switch(this.tabChar) {
1496            case DefaultCodeFormatterOptions.TAB :
1497                 stringBuffer.append("TAB");//$NON-NLS-1$
1498
break;
1499            case DefaultCodeFormatterOptions.SPACE :
1500                 stringBuffer.append("SPACE");//$NON-NLS-1$
1501
break;
1502            default :
1503                 stringBuffer.append("MIXED");//$NON-NLS-1$
1504
}
1505        stringBuffer
1506            .append(") - (tabSize = " + this.tabLength + ")")//$NON-NLS-1$//$NON-NLS-2$
1507
.append(this.lineSeparator)
1508            .append("(line = " + this.line + ") - (column = " + this.column + ") - (identationLevel = " + this.indentationLevel + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1509
.append(this.lineSeparator)
1510            .append("(needSpace = " + this.needSpace + ") - (lastNumberOfNewLines = " + this.lastNumberOfNewLines + ") - (checkLineWrapping = " + this.checkLineWrapping + ")") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1511
.append(this.lineSeparator)
1512            .append("==================================================================================") //$NON-NLS-1$
1513
.append(this.lineSeparator);
1514        printRule(stringBuffer);
1515        return stringBuffer.toString();
1516    }
1517    
1518    public void unIndent() {
1519        this.indentationLevel -= this.indentationSize;
1520        this.numberOfIndentations--;
1521    }
1522}
1523
Popular Tags