KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > dom > rewrite > ASTRewriteAnalyzer


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.core.dom.rewrite;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.IdentityHashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Stack JavaDoc;
19
20 import org.eclipse.core.runtime.Assert;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.jdt.core.ToolFactory;
23 import org.eclipse.jdt.core.compiler.IScanner;
24 import org.eclipse.jdt.core.compiler.ITerminalSymbols;
25 import org.eclipse.jdt.core.dom.*;
26 import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer;
27 import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer.SourceRange;
28 import org.eclipse.jdt.core.formatter.IndentManipulation;
29 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
30 import org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteFormatter.BlockContext;
31 import org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteFormatter.NodeMarker;
32 import org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteFormatter.Prefix;
33 import org.eclipse.jdt.internal.core.dom.rewrite.NodeInfoStore.CopyPlaceholderData;
34 import org.eclipse.jdt.internal.core.dom.rewrite.NodeInfoStore.StringPlaceholderData;
35 import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore.CopySourceInfo;
36 import org.eclipse.text.edits.*;
37
38
39 /**
40  * Infrastructure to support code modifications. Existing code must stay untouched, new code
41  * added with correct formatting, moved code left with the user's formatting / comments.
42  * Idea:
43  * - Get the AST for existing code
44  * - Describe changes
45  * - This visitor analyzes the changes or annotations and generates text edits
46  * (text manipulation API) that describe the required code changes.
47  */

48 public final class ASTRewriteAnalyzer extends ASTVisitor {
49     
50     /**
51      * Internal synonym for deprecated constant AST.JLS2
52      * to alleviate deprecated warnings.
53      * @deprecated
54      */

55     /*package*/ static final int JLS2_INTERNAL = AST.JLS2;
56     
57     TextEdit currentEdit;
58     final RewriteEventStore eventStore; // used from inner classes
59

60     private TokenScanner tokenScanner; // shared scanner
61

62     private final Map JavaDoc sourceCopyInfoToEdit;
63     private final Stack JavaDoc sourceCopyEndNodes;
64     
65     private final char[] content;
66     private final LineInformation lineInfo;
67     private final ASTRewriteFormatter formatter;
68     private final NodeInfoStore nodeInfos;
69     private final TargetSourceRangeComputer extendedSourceRangeComputer;
70     private final LineCommentEndOffsets lineCommentEndOffsets;
71     
72     /**
73      * Constructor for ASTRewriteAnalyzer.
74      * @param content the content of the compilation unit to rewrite.
75      * @param lineInfo line information for the content of the compilation unit to rewrite.
76      * @param rootEdit the edit to add all generated edits to
77      * @param eventStore the event store containing the description of changes
78      * @param nodeInfos annotations to nodes, such as if a node is a string placeholder or a copy target
79      * @param comments list of comments of the compilation unit to rewrite (elements of type <code>Comment</code>) or <code>null</code>.
80      * @param options the current jdt.core options (formatting/compliance) or <code>null</code>.
81      * @param extendedSourceRangeComputer the source range computer to use
82      */

83     public ASTRewriteAnalyzer(char[] content, LineInformation lineInfo, String JavaDoc lineDelim, TextEdit rootEdit, RewriteEventStore eventStore, NodeInfoStore nodeInfos, List JavaDoc comments, Map JavaDoc options, TargetSourceRangeComputer extendedSourceRangeComputer) {
84         this.eventStore= eventStore;
85         this.content= content;
86         this.lineInfo= lineInfo;
87         this.nodeInfos= nodeInfos;
88         this.tokenScanner= null;
89         this.currentEdit= rootEdit;
90         this.sourceCopyInfoToEdit= new IdentityHashMap JavaDoc();
91         this.sourceCopyEndNodes= new Stack JavaDoc();
92         
93         this.formatter= new ASTRewriteFormatter(nodeInfos, eventStore, options, lineDelim);
94         
95         this.extendedSourceRangeComputer = extendedSourceRangeComputer;
96         this.lineCommentEndOffsets= new LineCommentEndOffsets(comments);
97     }
98         
99     final TokenScanner getScanner() {
100         if (this.tokenScanner == null) {
101             IScanner scanner= ToolFactory.createScanner(true, false, false, false);
102             scanner.setSource(this.content);
103             this.tokenScanner= new TokenScanner(scanner);
104         }
105         return this.tokenScanner;
106     }
107     
108     final char[] getContent() {
109         return this.content;
110     }
111     
112     final LineInformation getLineInformation() {
113         return this.lineInfo;
114     }
115     
116     /**
117      * Returns the extended source range for a node.
118      *
119      * @return an extended source range (never null)
120      * @since 3.1
121      */

122     final SourceRange getExtendedRange(ASTNode node) {
123         if (this.eventStore.isRangeCopyPlaceholder(node)) {
124             return new SourceRange(node.getStartPosition(), node.getLength());
125         }
126         return this.extendedSourceRangeComputer.computeSourceRange(node);
127     }
128     
129     final int getExtendedOffset(ASTNode node) {
130         return getExtendedRange(node).getStartPosition();
131     }
132     
133     final int getExtendedEnd(ASTNode node) {
134         TargetSourceRangeComputer.SourceRange range= getExtendedRange(node);
135         return range.getStartPosition() + range.getLength();
136     }
137     
138     final TextEdit getCopySourceEdit(CopySourceInfo info) {
139         TextEdit edit= (TextEdit) this.sourceCopyInfoToEdit.get(info);
140         if (edit == null) {
141             SourceRange range= getExtendedRange(info.getNode());
142             int start= range.getStartPosition();
143             int end= start + range.getLength();
144             if (info.isMove) {
145                 MoveSourceEdit moveSourceEdit= new MoveSourceEdit(start, end - start);
146                 moveSourceEdit.setTargetEdit(new MoveTargetEdit(0));
147                 edit= moveSourceEdit;
148             } else {
149                 CopySourceEdit copySourceEdit= new CopySourceEdit(start, end - start);
150                 copySourceEdit.setTargetEdit(new CopyTargetEdit(0));
151                 edit= copySourceEdit;
152             }
153             this.sourceCopyInfoToEdit.put(info, edit);
154         }
155         return edit;
156     }
157     
158     private final int getChangeKind(ASTNode node, StructuralPropertyDescriptor property) {
159         RewriteEvent event= getEvent(node, property);
160         if (event != null) {
161             return event.getChangeKind();
162         }
163         return RewriteEvent.UNCHANGED;
164     }
165     
166     private final boolean hasChildrenChanges(ASTNode node) {
167         return this.eventStore.hasChangedProperties(node);
168     }
169     
170     private final boolean isChanged(ASTNode node, StructuralPropertyDescriptor property) {
171         RewriteEvent event= getEvent(node, property);
172         if (event != null) {
173             return event.getChangeKind() != RewriteEvent.UNCHANGED;
174         }
175         return false;
176     }
177     
178     private final boolean isCollapsed(ASTNode node) {
179         return this.nodeInfos.isCollapsed(node);
180     }
181     
182     final boolean isInsertBoundToPrevious(ASTNode node) {
183         return this.eventStore.isInsertBoundToPrevious(node);
184     }
185         
186     private final TextEditGroup getEditGroup(ASTNode parent, StructuralPropertyDescriptor property) {
187         RewriteEvent event= getEvent(parent, property);
188         if (event != null) {
189             return getEditGroup(event);
190         }
191         return null;
192     }
193     
194     final RewriteEvent getEvent(ASTNode parent, StructuralPropertyDescriptor property) {
195         return this.eventStore.getEvent(parent, property);
196     }
197     
198     final TextEditGroup getEditGroup(RewriteEvent change) {
199         return this.eventStore.getEventEditGroup(change);
200     }
201     
202     private final Object JavaDoc getOriginalValue(ASTNode parent, StructuralPropertyDescriptor property) {
203         return this.eventStore.getOriginalValue(parent, property);
204     }
205     
206     private final Object JavaDoc getNewValue(ASTNode parent, StructuralPropertyDescriptor property) {
207         return this.eventStore.getNewValue(parent, property);
208     }
209     
210     final void addEdit(TextEdit edit) {
211         this.currentEdit.addChild(edit);
212     }
213     
214     final String JavaDoc getLineDelimiter() {
215         return this.formatter.getLineDelimiter();
216     }
217     
218     final String JavaDoc createIndentString(int indent) {
219         return this.formatter.createIndentString(indent);
220     }
221     
222     final private String JavaDoc getIndentOfLine(int pos) {
223         int line= getLineInformation().getLineOfOffset(pos);
224         if (line >= 0) {
225             char[] cont= getContent();
226             int lineStart= getLineInformation().getLineOffset(line);
227             int i= lineStart;
228             while (i < cont.length && IndentManipulation.isIndentChar(content[i])) {
229                 i++;
230             }
231             return new String JavaDoc(cont, lineStart, i - lineStart);
232         }
233         return new String JavaDoc();
234     }
235     
236         
237     final String JavaDoc getIndentAtOffset(int pos) {
238         return this.formatter.getIndentString(getIndentOfLine(pos));
239     }
240     
241     final void doTextInsert(int offset, String JavaDoc insertString, TextEditGroup editGroup) {
242         if (insertString.length() > 0) {
243             // bug fix for 95839: problem with inserting at the end of a line comment
244
if (this.lineCommentEndOffsets.isEndOfLineComment(offset, this.content)) {
245                 if (!insertString.startsWith(getLineDelimiter())) {
246                     TextEdit edit= new InsertEdit(offset, getLineDelimiter()); // add a line delimiter
247
addEdit(edit);
248                     if (editGroup != null) {
249                         addEditGroup(editGroup, edit);
250                     }
251                 }
252                 this.lineCommentEndOffsets.remove(offset); // only one line delimiter per line comment required
253
}
254             TextEdit edit= new InsertEdit(offset, insertString);
255             addEdit(edit);
256             if (editGroup != null) {
257                 addEditGroup(editGroup, edit);
258             }
259         }
260     }
261     
262     final void addEditGroup(TextEditGroup editGroup, TextEdit edit) {
263         editGroup.addTextEdit(edit);
264     }
265     
266     final TextEdit doTextRemove(int offset, int len, TextEditGroup editGroup) {
267         if (len == 0) {
268             return null;
269         }
270         TextEdit edit= new DeleteEdit(offset, len);
271         addEdit(edit);
272         if (editGroup != null) {
273             addEditGroup(editGroup, edit);
274         }
275         return edit;
276     }
277     
278     final void doTextRemoveAndVisit(int offset, int len, ASTNode node, TextEditGroup editGroup) {
279         TextEdit edit= doTextRemove(offset, len, editGroup);
280         if (edit != null) {
281             this.currentEdit= edit;
282             voidVisit(node);
283             this.currentEdit= edit.getParent();
284         } else {
285             voidVisit(node);
286         }
287     }
288     
289     final int doVisit(ASTNode node) {
290         node.accept(this);
291         return getExtendedEnd(node);
292     }
293     
294     private final int doVisit(ASTNode parent, StructuralPropertyDescriptor property, int offset) {
295         Object JavaDoc node= getOriginalValue(parent, property);
296         if (property.isChildProperty() && node != null) {
297             return doVisit((ASTNode) node);
298         } else if (property.isChildListProperty()) {
299             return doVisitList((List JavaDoc) node, offset);
300         }
301         return offset;
302     }
303     
304     private int doVisitList(List JavaDoc list, int offset) {
305         int endPos= offset;
306         for (Iterator JavaDoc iter= list.iterator(); iter.hasNext();) {
307             ASTNode curr= ((ASTNode) iter.next());
308             endPos= doVisit(curr);
309         }
310         return endPos;
311     }
312     
313     final void voidVisit(ASTNode node) {
314         node.accept(this);
315     }
316     
317     private final void voidVisit(ASTNode parent, StructuralPropertyDescriptor property) {
318         Object JavaDoc node= getOriginalValue(parent, property);
319         if (property.isChildProperty() && node != null) {
320             voidVisit((ASTNode) node);
321         } else if (property.isChildListProperty()) {
322             voidVisitList((List JavaDoc) node);
323         }
324     }
325     
326     private void voidVisitList(List JavaDoc list) {
327         for (Iterator JavaDoc iter= list.iterator(); iter.hasNext();) {
328             doVisit(((ASTNode) iter.next()));
329         }
330     }
331     
332     private final boolean doVisitUnchangedChildren(ASTNode parent) {
333         List JavaDoc properties= parent.structuralPropertiesForType();
334         for (int i= 0; i < properties.size(); i++) {
335             voidVisit(parent, (StructuralPropertyDescriptor) properties.get(i));
336         }
337         return false;
338     }
339     
340     
341     private final void doTextReplace(int offset, int len, String JavaDoc insertString, TextEditGroup editGroup) {
342         if (len > 0 || insertString.length() > 0) {
343             TextEdit edit= new ReplaceEdit(offset, len, insertString);
344             addEdit(edit);
345             if (editGroup != null) {
346                 addEditGroup(editGroup, edit);
347             }
348         }
349     }
350         
351     private final TextEdit doTextCopy(TextEdit sourceEdit, int destOffset, int sourceIndentLevel, String JavaDoc destIndentString, TextEditGroup editGroup) {
352         TextEdit targetEdit;
353         SourceModifier modifier= new SourceModifier(sourceIndentLevel, destIndentString, this.formatter.getTabWidth(), this.formatter.getIndentWidth());
354         
355         if (sourceEdit instanceof MoveSourceEdit) {
356             MoveSourceEdit moveEdit= (MoveSourceEdit) sourceEdit;
357             moveEdit.setSourceModifier(modifier);
358             
359             targetEdit= new MoveTargetEdit(destOffset, moveEdit);
360             addEdit(targetEdit);
361         } else {
362             CopySourceEdit copyEdit= (CopySourceEdit) sourceEdit;
363             copyEdit.setSourceModifier(modifier);
364             
365             targetEdit= new CopyTargetEdit(destOffset, copyEdit);
366             addEdit(targetEdit);
367         }
368         
369         if (editGroup != null) {
370             addEditGroup(editGroup, sourceEdit);
371             addEditGroup(editGroup, targetEdit);
372         }
373         return targetEdit;
374
375     }
376             
377     private void changeNotSupported(ASTNode node) {
378         Assert.isTrue(false, "Change not supported in " + node.getClass().getName()); //$NON-NLS-1$
379
}
380     
381     
382     class ListRewriter {
383         protected String JavaDoc contantSeparator;
384         protected int startPos;
385         
386         protected RewriteEvent[] list;
387         
388         protected final ASTNode getOriginalNode(int index) {
389             return (ASTNode) this.list[index].getOriginalValue();
390         }
391         
392         protected final ASTNode getNewNode(int index) {
393             return (ASTNode) this.list[index].getNewValue();
394         }
395         
396         protected String JavaDoc getSeparatorString(int nodeIndex) {
397             return this.contantSeparator;
398         }
399         
400         protected int getInitialIndent() {
401             return getIndent(this.startPos);
402         }
403                 
404         protected int getNodeIndent(int nodeIndex) {
405             ASTNode node= getOriginalNode(nodeIndex);
406             if (node == null) {
407                 for (int i= nodeIndex - 1; i>= 0; i--) {
408                     ASTNode curr= getOriginalNode(i);
409                     if (curr != null) {
410                         return getIndent(curr.getStartPosition());
411                     }
412                 }
413                 return getInitialIndent();
414             }
415             return getIndent(node.getStartPosition());
416         }
417         
418         protected int getStartOfNextNode(int nextIndex, int defaultPos) {
419             for (int i= nextIndex; i < this.list.length; i++) {
420                 RewriteEvent elem= this.list[i];
421                 if (elem.getChangeKind() != RewriteEvent.INSERTED) {
422                     ASTNode node= (ASTNode) elem.getOriginalValue();
423                     return getExtendedOffset(node);
424                 }
425             }
426             return defaultPos;
427         }
428         
429         protected int getEndOfNode(ASTNode node) {
430             return getExtendedEnd(node);
431         }
432         
433         public final int rewriteList(ASTNode parent, StructuralPropertyDescriptor property, int offset, String JavaDoc keyword, String JavaDoc separator) {
434             this.contantSeparator= separator;
435             return rewriteList(parent, property, offset, keyword);
436         }
437         
438         private boolean insertAfterSeparator(ASTNode node) {
439             return !isInsertBoundToPrevious(node);
440         }
441         
442         public final int rewriteList(ASTNode parent, StructuralPropertyDescriptor property, int offset, String JavaDoc keyword) {
443             this.startPos= offset;
444             this.list= getEvent(parent, property).getChildren();
445             
446             int total= this.list.length;
447             if (total == 0) {
448                 return this.startPos;
449             }
450         
451             int currPos= -1;
452             
453             int lastNonInsert= -1;
454             int lastNonDelete= -1;
455                     
456             for (int i= 0; i < total; i++) {
457                 int currMark= this.list[i].getChangeKind();
458                 
459                 if (currMark != RewriteEvent.INSERTED) {
460                     lastNonInsert= i;
461                     if (currPos == -1) {
462                         ASTNode elem= (ASTNode) this.list[i].getOriginalValue();
463                         currPos= getExtendedOffset(elem);
464                     }
465                 }
466                 if (currMark != RewriteEvent.REMOVED) {
467                     lastNonDelete= i;
468                 }
469             }
470         
471             if (currPos == -1) { // only inserts
472
if (keyword.length() > 0) { // creating a new list -> insert keyword first (e.g. " throws ")
473
TextEditGroup editGroup= getEditGroup(this.list[0]); // first node is insert
474
doTextInsert(offset, keyword, editGroup);
475                 }
476                 currPos= offset;
477             }
478             if (lastNonDelete == -1) { // all removed, set back to start so the keyword is removed as well
479
currPos= offset;
480             }
481             
482             int prevEnd= currPos;
483             
484             final int NONE= 0, NEW= 1, EXISTING= 2;
485             int separatorState= NEW;
486
487             for (int i= 0; i < total; i++) {
488                 RewriteEvent currEvent= this.list[i];
489                 int currMark= currEvent.getChangeKind();
490                 int nextIndex= i + 1;
491
492                 if (currMark == RewriteEvent.INSERTED) {
493                     TextEditGroup editGroup= getEditGroup(currEvent);
494                     ASTNode node= (ASTNode) currEvent.getNewValue();
495                     
496                     if (separatorState == NONE) { // element after last existing element (but not first)
497
doTextInsert(currPos, getSeparatorString(i - 1), editGroup); // insert separator
498
separatorState= NEW;
499                     }
500                     if (separatorState == NEW || insertAfterSeparator(node)) {
501                         doTextInsert(currPos, node, getNodeIndent(i), true, editGroup); // insert node
502

503                         separatorState= NEW;
504                         if (i != lastNonDelete) {
505                             if (this.list[nextIndex].getChangeKind() != RewriteEvent.INSERTED) {
506                                 doTextInsert(currPos, getSeparatorString(i), editGroup); // insert separator
507
} else {
508                                 separatorState= NONE;
509                             }
510                         }
511                     } else { // EXISTING && insert before separator
512
doTextInsert(prevEnd, getSeparatorString(i - 1), editGroup);
513                         doTextInsert(prevEnd, node, getNodeIndent(i), true, editGroup);
514                     }
515                 } else if (currMark == RewriteEvent.REMOVED) {
516                     ASTNode node= (ASTNode) currEvent.getOriginalValue();
517                     TextEditGroup editGroup= getEditGroup(currEvent);
518                     int currEnd= getEndOfNode(node);
519                     if (i > lastNonDelete && separatorState == EXISTING) {
520                         // is last, remove previous separator: split delete to allow range copies
521
doTextRemove(prevEnd, currPos - prevEnd, editGroup); // remove separator
522
doTextRemoveAndVisit(currPos, currEnd - currPos, node, editGroup); // remove node
523
currPos= currEnd;
524                         prevEnd= currEnd;
525                     } else {
526                         // remove element and next separator
527
int end= getStartOfNextNode(nextIndex, currEnd); // start of next
528
doTextRemoveAndVisit(currPos, currEnd - currPos, node, getEditGroup(currEvent)); // remove node
529
doTextRemove(currEnd, end - currEnd, editGroup); // remove separator
530
currPos= end;
531                         prevEnd= currEnd;
532                         separatorState= NEW;
533                     }
534                 } else { // replaced or unchanged
535
if (currMark == RewriteEvent.REPLACED) {
536                         ASTNode node= (ASTNode) currEvent.getOriginalValue();
537                         int currEnd= getEndOfNode(node);
538                         
539                         TextEditGroup editGroup= getEditGroup(currEvent);
540                         ASTNode changed= (ASTNode) currEvent.getNewValue();
541                         doTextRemoveAndVisit(currPos, currEnd - currPos, node, editGroup);
542                         doTextInsert(currPos, changed, getNodeIndent(i), true, editGroup);
543                         
544                         prevEnd= currEnd;
545                     } else { // is unchanged
546
ASTNode node= (ASTNode) currEvent.getOriginalValue();
547                         voidVisit(node);
548                     }
549                     if (i == lastNonInsert) { // last node or next nodes are all inserts
550
separatorState= NONE;
551                         if (currMark == RewriteEvent.UNCHANGED) {
552                             ASTNode node= (ASTNode) currEvent.getOriginalValue();
553                             prevEnd= getEndOfNode(node);
554                         }
555                         currPos= prevEnd;
556                     } else if (this.list[nextIndex].getChangeKind() != RewriteEvent.UNCHANGED) {
557                         // no updates needed while nodes are unchanged
558
if (currMark == RewriteEvent.UNCHANGED) {
559                             ASTNode node= (ASTNode) currEvent.getOriginalValue();
560                             prevEnd= getEndOfNode(node);
561                         }
562                         currPos= getStartOfNextNode(nextIndex, prevEnd); // start of next
563
separatorState= EXISTING;
564                     }
565                 }
566
567             }
568             return currPos;
569         }
570         
571     }
572                 
573     private int rewriteRequiredNode(ASTNode parent, StructuralPropertyDescriptor property) {
574         RewriteEvent event= getEvent(parent, property);
575         if (event != null && event.getChangeKind() == RewriteEvent.REPLACED) {
576             ASTNode node= (ASTNode) event.getOriginalValue();
577             TextEditGroup editGroup= getEditGroup(event);
578             SourceRange range= getExtendedRange(node);
579             int offset= range.getStartPosition();
580             int length= range.getLength();
581             doTextRemoveAndVisit(offset, length, node, editGroup);
582             doTextInsert(offset, (ASTNode) event.getNewValue(), getIndent(offset), true, editGroup);
583             return offset + length;
584         }
585         return doVisit(parent, property, 0);
586     }
587         
588     private int rewriteNode(ASTNode parent, StructuralPropertyDescriptor property, int offset, Prefix prefix) {
589         RewriteEvent event= getEvent(parent, property);
590         if (event != null) {
591             switch (event.getChangeKind()) {
592                 case RewriteEvent.INSERTED: {
593                     ASTNode node= (ASTNode) event.getNewValue();
594                     TextEditGroup editGroup= getEditGroup(event);
595                     int indent= getIndent(offset);
596                     doTextInsert(offset, prefix.getPrefix(indent), editGroup);
597                     doTextInsert(offset, node, indent, true, editGroup);
598                     return offset;
599                 }
600                 case RewriteEvent.REMOVED: {
601                     ASTNode node= (ASTNode) event.getOriginalValue();
602                     TextEditGroup editGroup= getEditGroup(event);
603                     
604                     int nodeEnd= getExtendedEnd(node);
605                     // if there is a prefix, remove the prefix as well
606
int len= nodeEnd - offset;
607                     doTextRemoveAndVisit(offset, len, node, editGroup);
608                     return nodeEnd;
609                 }
610                 case RewriteEvent.REPLACED: {
611                     ASTNode node= (ASTNode) event.getOriginalValue();
612                     TextEditGroup editGroup= getEditGroup(event);
613                     SourceRange range= getExtendedRange(node);
614                     int nodeOffset= range.getStartPosition();
615                     int nodeLen= range.getLength();
616                     doTextRemoveAndVisit(nodeOffset, nodeLen, node, editGroup);
617                     doTextInsert(nodeOffset, (ASTNode) event.getNewValue(), getIndent(offset), true, editGroup);
618                     return nodeOffset + nodeLen;
619                 }
620             }
621         }
622         return doVisit(parent, property, offset);
623     }
624     
625     private int rewriteJavadoc(ASTNode node, StructuralPropertyDescriptor property) {
626         int pos= rewriteNode(node, property, node.getStartPosition(), ASTRewriteFormatter.NONE);
627         int changeKind= getChangeKind(node, property);
628         if (changeKind == RewriteEvent.INSERTED) {
629             String JavaDoc indent= getLineDelimiter() + getIndentAtOffset(pos);
630             doTextInsert(pos, indent, getEditGroup(node, property));
631         } else if (changeKind == RewriteEvent.REMOVED) {
632             try {
633                 getScanner().readNext(pos, false);
634                 doTextRemove(pos, getScanner().getCurrentStartOffset() - pos, getEditGroup(node, property));
635                 pos= getScanner().getCurrentStartOffset();
636             } catch (CoreException e) {
637                 handleException(e);
638             }
639         }
640         return pos;
641     }
642     
643     
644     /*
645      * endpos can be -1 -> use the end pos of the body
646      */

647     private int rewriteBodyNode(ASTNode parent, StructuralPropertyDescriptor property, int offset, int endPos, int indent, BlockContext context) {
648         RewriteEvent event= getEvent(parent, property);
649         if (event != null) {
650             switch (event.getChangeKind()) {
651                 case RewriteEvent.INSERTED: {
652                     ASTNode node= (ASTNode) event.getNewValue();
653                     TextEditGroup editGroup= getEditGroup(event);
654                     
655                     String JavaDoc[] strings= context.getPrefixAndSuffix(indent, node, this.eventStore);
656                     
657                     doTextInsert(offset, strings[0], editGroup);
658                     doTextInsert(offset, node, indent, true, editGroup);
659                     doTextInsert(offset, strings[1], editGroup);
660                     return offset;
661                 }
662                 case RewriteEvent.REMOVED: {
663                     ASTNode node= (ASTNode) event.getOriginalValue();
664                     if (endPos == -1) {
665                         endPos= getExtendedEnd(node);
666                     }
667                     
668                     TextEditGroup editGroup= getEditGroup(event);
669                     // if there is a prefix, remove the prefix as well
670
int len= endPos - offset;
671                     doTextRemoveAndVisit(offset, len, node, editGroup);
672                     return endPos;
673                 }
674                 case RewriteEvent.REPLACED: {
675                     ASTNode node= (ASTNode) event.getOriginalValue();
676                     if (endPos == -1) {
677                         endPos= getExtendedEnd(node);
678                     }
679                     TextEditGroup editGroup= getEditGroup(event);
680                     int nodeLen= endPos - offset;
681                     
682                     ASTNode replacingNode= (ASTNode) event.getNewValue();
683                     String JavaDoc[] strings= context.getPrefixAndSuffix(indent, replacingNode, this.eventStore);
684                     doTextRemoveAndVisit(offset, nodeLen, node, editGroup);
685                     
686                     String JavaDoc prefix= strings[0];
687                     doTextInsert(offset, prefix, editGroup);
688                     String JavaDoc lineInPrefix= getCurrentLine(prefix, prefix.length());
689                     if (prefix.length() != lineInPrefix.length()) {
690                         // prefix contains a new line: update the indent to the one used in the prefix
691
indent= this.formatter.computeIndentUnits(lineInPrefix);
692                     }
693                     doTextInsert(offset, replacingNode, indent, true, editGroup);
694                     doTextInsert(offset, strings[1], editGroup);
695                     return endPos;
696                 }
697             }
698         }
699         int pos= doVisit(parent, property, offset);
700         if (endPos != -1) {
701             return endPos;
702         }
703         return pos;
704     }
705     
706     private int rewriteOptionalQualifier(ASTNode parent, StructuralPropertyDescriptor property, int startPos) {
707         RewriteEvent event= getEvent(parent, property);
708         if (event != null) {
709             switch (event.getChangeKind()) {
710                 case RewriteEvent.INSERTED: {
711                     ASTNode node= (ASTNode) event.getNewValue();
712                     TextEditGroup editGroup= getEditGroup(event);
713                     doTextInsert(startPos, node, getIndent(startPos), true, editGroup);
714                     doTextInsert(startPos, ".", editGroup); //$NON-NLS-1$
715
return startPos;
716                 }
717                 case RewriteEvent.REMOVED: {
718                     try {
719                         ASTNode node= (ASTNode) event.getOriginalValue();
720                         TextEditGroup editGroup= getEditGroup(event);
721 <