KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ltk > core > refactoring > MultiStateTextFileChange


1 /*******************************************************************************
2  * Copyright (c) 2005, 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.ltk.core.refactoring;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.LinkedList JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.eclipse.text.edits.InsertEdit;
24 import org.eclipse.text.edits.MalformedTreeException;
25 import org.eclipse.text.edits.MultiTextEdit;
26 import org.eclipse.text.edits.ReplaceEdit;
27 import org.eclipse.text.edits.TextEdit;
28 import org.eclipse.text.edits.TextEditCopier;
29 import org.eclipse.text.edits.TextEditGroup;
30 import org.eclipse.text.edits.TextEditProcessor;
31 import org.eclipse.text.edits.UndoEdit;
32
33 import org.eclipse.core.runtime.Assert;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IPath;
36 import org.eclipse.core.runtime.IProgressMonitor;
37 import org.eclipse.core.runtime.NullProgressMonitor;
38 import org.eclipse.core.runtime.OperationCanceledException;
39 import org.eclipse.core.runtime.SubProgressMonitor;
40
41 import org.eclipse.core.filebuffers.FileBuffers;
42 import org.eclipse.core.filebuffers.ITextFileBuffer;
43 import org.eclipse.core.filebuffers.ITextFileBufferManager;
44 import org.eclipse.core.filebuffers.LocationKind;
45
46 import org.eclipse.core.resources.IFile;
47
48 import org.eclipse.jface.text.BadLocationException;
49 import org.eclipse.jface.text.BadPositionCategoryException;
50 import org.eclipse.jface.text.Document;
51 import org.eclipse.jface.text.DocumentEvent;
52 import org.eclipse.jface.text.DocumentRewriteSession;
53 import org.eclipse.jface.text.DocumentRewriteSessionType;
54 import org.eclipse.jface.text.IDocument;
55 import org.eclipse.jface.text.IDocumentExtension4;
56 import org.eclipse.jface.text.IDocumentListener;
57 import org.eclipse.jface.text.IPositionUpdater;
58 import org.eclipse.jface.text.IRegion;
59 import org.eclipse.jface.text.Position;
60 import org.eclipse.jface.text.Region;
61
62 import org.eclipse.ltk.internal.core.refactoring.BufferValidationState;
63 import org.eclipse.ltk.internal.core.refactoring.Changes;
64 import org.eclipse.ltk.internal.core.refactoring.ContentStamps;
65 import org.eclipse.ltk.internal.core.refactoring.MultiStateUndoChange;
66 import org.eclipse.ltk.internal.core.refactoring.NonDeletingPositionUpdater;
67 import org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin;
68
69 /**
70  * A multi state text file change is a special change object that applies a sequence of {@link TextEdit
71  * text edit trees} to a document. The multi state text file change manages the text edit trees.
72  * <p>
73  * A multi state text file change offers the ability to access the original content of
74  * the document as well as creating a preview of the change. The edit
75  * trees get copied when creating any kind of preview. Therefore no region
76  * updating on the original edit trees takes place when requesting a preview
77  * (for more information on region updating see class {@link TextEdit TextEdit}.
78  * If region tracking is required for a preview it can be enabled via a call
79  * to the method {@link #setKeepPreviewEdits(boolean) setKeepPreviewEdits}.
80  * If enabled the multi state text file change keeps the copied edit trees executed for the
81  * preview allowing clients to map an original edit to an executed edit. The
82  * executed edit can then be used to determine its position in the preview.
83  * </p>
84  *
85  * @since 3.2
86  */

87 public class MultiStateTextFileChange extends TextEditBasedChange {
88
89     private static final class ComposableBufferChange {
90
91         private TextEdit fEdit;
92
93         private List JavaDoc fGroups;
94
95         private final TextEdit getEdit() {
96             return fEdit;
97         }
98
99         private final List JavaDoc getGroups() {
100             return fGroups;
101         }
102
103         private final void setEdit(final TextEdit edit) {
104             Assert.isNotNull(edit);
105
106             fEdit= edit;
107         }
108
109         private final void setGroups(final List JavaDoc groups) {
110             Assert.isNotNull(groups);
111
112             fGroups= groups;
113         }
114     }
115
116     private static final class ComposableBufferChangeGroup extends TextEditBasedChangeGroup {
117
118         private final Set JavaDoc fEdits= new HashSet JavaDoc();
119
120         private ComposableBufferChangeGroup(final MultiStateTextFileChange change, final TextEditGroup group) {
121             super(change, group);
122
123             final TextEdit[] edits= group.getTextEdits();
124             for (int index= 0; index < edits.length; index++)
125                 cacheEdit(edits[index]);
126         }
127
128         private final void cacheEdit(final TextEdit edit) {
129             fEdits.add(edit);
130
131             final TextEdit[] edits= edit.getChildren();
132             for (int index= 0; index < edits.length; index++)
133                 cacheEdit(edits[index]);
134         }
135
136         private final boolean containsEdit(final TextEdit edit) {
137             return fEdits.contains(edit);
138         }
139
140         private final Set JavaDoc getCachedEdits() {
141             return fEdits;
142         }
143     }
144
145     private static final class ComposableEditPosition extends Position {
146
147         private String JavaDoc fText;
148
149         private final String JavaDoc getText() {
150             return fText;
151         }
152
153         private final void setText(final String JavaDoc text) {
154             Assert.isNotNull(text);
155
156             fText= text;
157         }
158     }
159
160     private static final class ComposableUndoEdit {
161
162         private ComposableBufferChangeGroup fGroup;
163
164         private TextEdit fOriginal;
165
166         private ReplaceEdit fUndo;
167
168         private final ComposableBufferChangeGroup getGroup() {
169             return fGroup;
170         }
171
172         private final TextEdit getOriginal() {
173             return fOriginal;
174         }
175
176         private final String JavaDoc getOriginalText() {
177             if (fOriginal instanceof ReplaceEdit) {
178                 return ((ReplaceEdit) getOriginal()).getText();
179             } else if (fOriginal instanceof InsertEdit) {
180                 return ((InsertEdit) getOriginal()).getText();
181             }
182             return ""; //$NON-NLS-1$
183
}
184
185         private final ReplaceEdit getUndo() {
186             return fUndo;
187         }
188
189         private final void setGroup(final ComposableBufferChangeGroup group) {
190             Assert.isNotNull(group);
191
192             fGroup= group;
193         }
194
195         private final void setOriginal(final TextEdit edit) {
196             fOriginal= edit;
197         }
198
199         private final void setUndo(final ReplaceEdit undo) {
200             Assert.isNotNull(undo);
201
202             fUndo= undo;
203         }
204     }
205
206     /** The position category for the resulting edit positions */
207     private static final String JavaDoc COMPOSABLE_POSITION_CATEGORY= "ComposableEditPositionCategory_" + System.currentTimeMillis(); //$NON-NLS-1$
208

209     /** The position category for the preview region range marker */
210     private static final String JavaDoc MARKER_POSITION_CATEGORY= "MarkerPositionCategory_" + System.currentTimeMillis(); //$NON-NLS-1$
211

212     /** The text file buffer */
213     private ITextFileBuffer fBuffer;
214
215     /** The last string obtained from a document */
216     private String JavaDoc fCachedString;
217
218     /**
219      * The internal change objects (element type:
220      * <code>ComposableBufferChange</code>)
221      */

222     private final ArrayList JavaDoc fChanges= new ArrayList JavaDoc(4);
223
224     /** The content stamp */
225     private ContentStamp fContentStamp;
226
227     /** The text edit copier */
228     private TextEditCopier fCopier;
229
230     /** The text file buffer reference count */
231     private int fCount;
232
233     /** The dirty flag */
234     private boolean fDirty;
235
236     /** The affected file */
237     private IFile fFile;
238
239     /** The save mode */
240     private int fSaveMode= TextFileChange.KEEP_SAVE_STATE;
241
242     /** The validation state */
243     private BufferValidationState fValidationState;
244
245     /**
246      * Creates a new composite text file change.
247      * <p>
248      * The default text type is <code>txt</code>.
249      * </p>
250      *
251      * @param name
252      * the name of the composite text file change
253      * @param file
254      * the text file to apply the change to
255      */

256     public MultiStateTextFileChange(final String JavaDoc name, final IFile file) {
257         super(name);
258
259         Assert.isNotNull(file);
260         fFile= file;
261
262         setTextType("txt"); //$NON-NLS-1$
263
}
264
265     /**
266      * Acquires a document from the file buffer manager.
267      *
268      * @param monitor
269      * the progress monitor to use
270      * @return the document
271      * @throws CoreException
272      */

273     private IDocument acquireDocument(final IProgressMonitor monitor) throws CoreException {
274         if (fCount > 0)
275             return fBuffer.getDocument();
276
277         final ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
278         final IPath path= fFile.getFullPath();
279
280         manager.connect(path, LocationKind.IFILE, monitor);
281         fCount++;
282
283         fBuffer= manager.getTextFileBuffer(path, LocationKind.IFILE);
284
285         final IDocument document= fBuffer.getDocument();
286         fContentStamp= ContentStamps.get(fFile, document);
287
288         return document;
289     }
290
291     /**
292      * Adds a new text change to this composite change.
293      * <p>
294      * The text change which is added is not changed in any way. Rather
295      * the contents of the text change are retrieved and stored internally
296      * in this composite text change.
297      * </p>
298      *
299      * @param change
300      * the text change to add
301      */

302     public final void addChange(final TextChange change) {
303         Assert.isNotNull(change);
304
305         final ComposableBufferChange result= new ComposableBufferChange();
306         result.setEdit(change.getEdit());
307
308         final TextEditBasedChangeGroup[] groups= change.getChangeGroups();
309         final List JavaDoc list= new ArrayList JavaDoc(groups.length);
310
311         for (int index= 0; index < groups.length; index++) {
312
313             final TextEditBasedChangeGroup group= new ComposableBufferChangeGroup(this, groups[index].getTextEditGroup());
314             list.add(group);
315
316             addChangeGroup(group);
317         }
318         result.setGroups(list);
319         fChanges.add(result);
320     }
321
322     // Copied from TextChange
323
private TextEditProcessor createTextEditProcessor(ComposableBufferChange change, IDocument document, int flags, boolean preview) {
324         List JavaDoc excludes= new ArrayList JavaDoc(0);
325         for (final Iterator JavaDoc iterator= change.getGroups().iterator(); iterator.hasNext();) {
326             TextEditBasedChangeGroup group= (TextEditBasedChangeGroup) iterator.next();
327             if (!group.isEnabled())
328                 excludes.addAll(Arrays.asList(group.getTextEdits()));
329         }
330
331         if (preview) {
332             fCopier= new TextEditCopier(change.getEdit());
333             TextEdit copiedEdit= fCopier.perform();
334             boolean keep= getKeepPreviewEdits();
335             if (keep)
336                 flags= flags | TextEdit.UPDATE_REGIONS;
337             LocalTextEditProcessor result= new LocalTextEditProcessor(document, copiedEdit, flags);
338             result.setExcludes(mapEdits((TextEdit[]) excludes.toArray(new TextEdit[excludes.size()]), fCopier));
339             if (!keep)
340                 fCopier= null;
341             return result;
342         } else {
343             LocalTextEditProcessor result= new LocalTextEditProcessor(document, change.getEdit(), flags | TextEdit.UPDATE_REGIONS);
344             result.setExcludes((TextEdit[]) excludes.toArray(new TextEdit[excludes.size()]));
345             return result;
346         }
347     }
348
349     /**
350      * Creates the corresponding text edit to the event.
351      *
352      * @param document
353      * the document
354      * @param offset
355      * the offset of the event
356      * @param length
357      * the length of the event
358      * @param text
359      * the text of the event
360      * @return the undo edit
361      */

362     private ReplaceEdit createUndoEdit(final IDocument document, final int offset, final int length, final String JavaDoc text) {
363         String JavaDoc currentText= null;
364         try {
365             currentText= document.get(offset, length);
366         } catch (BadLocationException cannotHappen) {
367             // Cannot happen
368
}
369
370         if (fCachedString != null && fCachedString.equals(currentText))
371             currentText= fCachedString;
372         else
373             fCachedString= currentText;
374
375         return new ReplaceEdit(offset, text != null ? text.length() : 0, currentText);
376     }
377
378     /*
379      * @see org.eclipse.ltk.core.refactoring.Change#dispose()
380      */

381     public final void dispose() {
382         fValidationState.dispose();
383     }
384
385     /*
386      * @see org.eclipse.ltk.core.refactoring.TextEditBasedChange#getCurrentContent(org.eclipse.core.runtime.IProgressMonitor)
387      */

388     public final String JavaDoc getCurrentContent(final IProgressMonitor monitor) throws CoreException {
389         return getCurrentDocument(monitor).get();
390     }
391
392     /*
393      * @see org.eclipse.ltk.core.refactoring.TextEditBasedChange#getCurrentContent(org.eclipse.jface.text.IRegion,boolean,int,org.eclipse.core.runtime.IProgressMonitor)
394      */

395     public final String JavaDoc getCurrentContent(final IRegion region, final boolean expand, final int surround, final IProgressMonitor monitor) throws CoreException {
396         Assert.isNotNull(region);
397         Assert.isTrue(surround >= 0);
398         final IDocument document= getCurrentDocument(monitor);
399         Assert.isTrue(document.getLength() >= region.getOffset() + region.getLength());
400         return getContent(document, region, expand, surround);
401     }
402
403     /**
404      * Returns a document representing the current state of the buffer,
405      * prior to the application of the change.
406      * <p>
407      * The returned document should not be modified.
408      * </p>
409      *
410      * @param monitor
411      * the progress monitor to use, or <code>null</code>
412      * @return the current document, or the empty document
413      * @throws CoreException
414      * if no document could be acquired
415      */

416     public final IDocument getCurrentDocument(IProgressMonitor monitor) throws CoreException {
417         if (monitor == null)
418             monitor= new NullProgressMonitor();
419         IDocument result= null;
420         monitor.beginTask("", 2); //$NON-NLS-1$
421
try {
422             result= acquireDocument(new SubProgressMonitor(monitor, 1));
423         } finally {
424             if (result != null)
425                 releaseDocument(result, new SubProgressMonitor(monitor, 1));
426         }
427         monitor.done();
428         if (result == null)
429             result= new Document();
430         return result;
431     }
432
433     /*
434      * @see org.eclipse.ltk.core.refactoring.Change#getModifiedElement()
435      */

436     public final Object JavaDoc getModifiedElement() {
437         return fFile;
438     }
439
440     /*
441      * @see org.eclipse.ltk.core.refactoring.TextEditBasedChange#getPreviewContent(org.eclipse.ltk.core.refactoring.TextEditBasedChangeGroup[],org.eclipse.jface.text.IRegion,boolean,int,org.eclipse.core.runtime.IProgressMonitor)
442      */

443     public final String JavaDoc getPreviewContent(final TextEditBasedChangeGroup[] groups, final IRegion region, final boolean expand, final int surround, final IProgressMonitor monitor) throws CoreException {
444
445         final Set JavaDoc cachedGroups= new HashSet JavaDoc(Arrays.asList(groups));
446         final IDocument document= new Document(getCurrentDocument(monitor).get());
447
448         // Marks the region in the document to be previewed
449
final Position range= new Position(region.getOffset(), region.getLength());
450         try {
451
452             ComposableBufferChange change= null;
453
454             final TextEditBasedChangeGroup[] changedGroups= getChangeGroups();
455
456             LinkedList JavaDoc compositeUndo= new LinkedList JavaDoc();
457             for (int index= 0; index < fChanges.size(); index++) {
458                 change= (ComposableBufferChange) fChanges.get(index);
459
460                 TextEdit copy= null;
461                 try {
462                     // Have to use a copy
463
fCopier= new TextEditCopier(change.getEdit());
464                     copy= fCopier.perform();
465
466                     // Create a mapping from the copied edits to its originals
467
final Map JavaDoc originalMap= new HashMap JavaDoc();
468                     for (final Iterator JavaDoc outer= change.getGroups().iterator(); outer.hasNext();) {
469
470                         final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) outer.next();
471                         for (final Iterator JavaDoc inner= group.getCachedEdits().iterator(); inner.hasNext();) {
472
473                             final TextEdit originalEdit= (TextEdit) inner.next();
474                             final TextEdit copiedEdit= fCopier.getCopy(originalEdit);
475
476                             if (copiedEdit != null)
477                                 originalMap.put(copiedEdit, originalEdit);
478                             else
479                                 RefactoringCorePlugin.logErrorMessage("Could not find a copy for the indexed text edit " + originalEdit.toString()); //$NON-NLS-1$
480
}
481                     }
482
483                     final ComposableBufferChangeGroup[] currentGroup= { null};
484                     final TextEdit[] currentEdit= { null};
485
486                     // Text edit processor which sets the change group and
487
// current edit when processing
488
final TextEditProcessor processor= new TextEditProcessor(document, copy, TextEdit.NONE) {
489
490                         protected final boolean considerEdit(final TextEdit edit) {
491
492                             final TextEdit originalEdit= (TextEdit) originalMap.get(edit);
493                             if (originalEdit != null) {
494
495                                 currentEdit[0]= originalEdit;
496
497                                 boolean found= false;
498                                 for (int offset= 0; offset < changedGroups.length && !found; offset++) {
499
500                                     final ComposableBufferChangeGroup group= (ComposableBufferChangeGroup) changedGroups[offset];
501                                     if (group.containsEdit(originalEdit)) {
502
503                                         currentGroup[0]= group;
504                                         found= true;
505                                     }
506                                 }
507                                 if (!found)
508                                     currentGroup[0]= null;
509
510                             } else if (!(edit instanceof MultiTextEdit)) {
511                                 RefactoringCorePlugin.logErrorMessage("Could not find the original of the copied text edit " + edit.toString()); //$NON-NLS-1$
512
}
513                             return true;
514                         }
515                     };
516
517                     final LinkedList JavaDoc eventUndos= new LinkedList JavaDoc();
518
519                     // Listener to record the undos on the document (offsets
520
// relative to the document event)
521
final IDocumentListener listener= new IDocumentListener() {
522
523                         public final void documentAboutToBeChanged(final DocumentEvent event) {
524                             final ComposableUndoEdit edit= new ComposableUndoEdit();
525
526                             edit.setGroup(currentGroup[0]);
527                             edit.setOriginal(currentEdit[0]);
528                             edit.setUndo(createUndoEdit(document, event.getOffset(), event.getLength(), event.getText()));
529
530                             eventUndos.addFirst(edit);
531                         }
532
533                         public final void documentChanged(final DocumentEvent event) {
534                             // Do nothing
535
}
536                     };
537
538                     try {
539                         // Record undos in LIFO order
540
document.addDocumentListener(listener);
541                         processor.performEdits();
542                     } finally {
543                         document.removeDocumentListener(listener);
544                     }
545
546                     compositeUndo.addFirst(eventUndos);
547
548                 } finally {
549                     fCopier= null;
550                 }
551             }
552
553             final IPositionUpdater positionUpdater= new IPositionUpdater() {
554
555                 public final void update(final DocumentEvent event) {
556
557                     final int eventOffset= event.getOffset();
558                     final int eventLength= event.getLength();
559                     final int eventOldEndOffset= eventOffset + eventLength;
560                     final String JavaDoc eventText= event.getText();
561                     final int eventNewLength= eventText == null ? 0 : eventText.length();
562                     final int eventNewEndOffset= eventOffset + eventNewLength;
563                     final int deltaLength= eventNewLength - eventLength;
564
565                     try {
566
567                         final Position[] positions= event.getDocument().getPositions(COMPOSABLE_POSITION_CATEGORY);
568                         for (int index= 0; index < positions.length; index++) {
569
570                             final Position position= positions[index];
571                             if (position.isDeleted())
572                                 continue;
573
574                             final int offset= position.getOffset();
575                             final int length= position.getLength();
576                             final int end= offset + length;
577
578                             if (offset > eventOldEndOffset) {
579                                 // position comes way after change - shift
580
position.setOffset(offset + deltaLength);
581                             } else if (end < eventOffset) {
582                                 // position comes way before change - leave
583
// alone
584
} else if (offset == eventOffset) {
585                                 // leave alone, since the edits are overlapping
586
} else if (offset <= eventOffset && end >= eventOldEndOffset) {
587                                 // event completely internal to the position
588
// -
589
// adjust length
590
position.setLength(length + deltaLength);
591                             } else if (offset < eventOffset) {
592                                 // event extends over end of position - include
593
// the
594
// replacement text into the position
595
position.setLength(eventNewEndOffset - offset);
596                             } else if (end > eventOldEndOffset) {
597                                 // event extends from before position into it -
598
// adjust
599
// offset and length, including the replacement
600
// text into
601
// the position
602
position.setOffset(eventOffset);
603                                 int deleted= eventOldEndOffset - offset;
604                                 position.setLength(length - deleted + eventNewLength);
605                             } else {
606                                 // event comprises the position - keep it at the
607
// same
608
// position, but always inside the replacement
609
// text
610
int newOffset= Math.min(offset, eventNewEndOffset);
611                                 int newEndOffset= Math.min(end, eventNewEndOffset);
612                                 position.setOffset(newOffset);
613                                 position.setLength(newEndOffset - newOffset);
614                             }
615                         }
616                     } catch (BadPositionCategoryException exception) {
617                         // ignore and return
618
}
619                 }
620             };
621
622             try {
623
624                 document.addPositionCategory(COMPOSABLE_POSITION_CATEGORY);
625                 document.addPositionUpdater(positionUpdater);
626
627                 // Apply undos in LIFO order to get to the original document
628
// Track the undos of edits which are in change groups to be
629
// previewed and insert
630
// Undo edits for them (corresponding to the linearized net
631
// effect on the original document)
632
final LinkedList JavaDoc undoQueue= new LinkedList JavaDoc();
633                 for (final Iterator JavaDoc outer= compositeUndo.iterator(); outer.hasNext();) {
634                     for (final Iterator JavaDoc inner= ((List JavaDoc) outer.next()).iterator(); inner.hasNext();) {
635
636                         final ComposableUndoEdit edit= (ComposableUndoEdit) inner.next();
637                         final ReplaceEdit undo= edit.getUndo();
638
639                         final int offset= undo.getOffset();
640                         final int length= undo.getLength();
641                         final String JavaDoc text= undo.getText();
642
643                         ComposableEditPosition position= new ComposableEditPosition();
644                         if (cachedGroups.contains(edit.getGroup())) {
645
646                             if (text == null || text.equals("")) { //$NON-NLS-1$
647
position.offset= offset;
648                                 if (length != 0) {
649                                     // Undo is a delete, create final insert
650
// edit
651
position.length= 0;
652                                     position.setText(edit.getOriginalText());
653                                 } else
654                                     RefactoringCorePlugin.logErrorMessage("Dubious undo edit found: " + undo.toString()); //$NON-NLS-1$
655

656                             } else if (length == 0) {
657                                 position.offset= offset;
658                                 // Undo is an insert, create final delete
659
// edit
660
position.setText(""); //$NON-NLS-1$
661
position.length= text.length();
662                             } else {
663                                 // Undo is a replace, create final replace edit
664
position.offset= offset;
665                                 position.length= length;
666                                 position.setText(edit.getOriginalText());
667                             }
668
669                             document.addPosition(COMPOSABLE_POSITION_CATEGORY, position);
670                         }
671
672                         position= new ComposableEditPosition();
673                         position.offset= undo.getOffset();
674                         position.length= undo.getLength();
675                         position.setText(undo.getText());
676
677                         undoQueue.add(position);
678                     }
679
680                     for (final Iterator JavaDoc iterator= undoQueue.iterator(); iterator.hasNext();) {
681                         final ComposableEditPosition position= (ComposableEditPosition) iterator.next();
682
683                         document.replace(position.offset, position.length, position.getText());
684                         iterator.remove();
685                     }
686                 }
687
688                 // Use a simple non deleting position updater for the range
689
final IPositionUpdater markerUpdater= new NonDeletingPositionUpdater(MARKER_POSITION_CATEGORY);
690
691                 try {
692
693                     final Position[] positions= document.getPositions(COMPOSABLE_POSITION_CATEGORY);
694                     document.addPositionCategory(MARKER_POSITION_CATEGORY);
695                     document.addPositionUpdater(markerUpdater);
696                     document.addPosition(MARKER_POSITION_CATEGORY, range);
697
698                     for (int index= 0; index < positions.length; index++) {
699                         final ComposableEditPosition position= (ComposableEditPosition) positions[index];
700
701                         document.replace(position.offset, position.length, position.getText() != null ? position.getText() : ""); //$NON-NLS-1$
702
}
703
704                 } catch (BadPositionCategoryException exception) {
705                     RefactoringCorePlugin.log(exception);
706                 } finally {
707                     document.removePositionUpdater(markerUpdater);
708                     try {
709                         document.removePosition(MARKER_POSITION_CATEGORY, range);
710                         document.removePositionCategory(MARKER_POSITION_CATEGORY);
711                     } catch (BadPositionCategoryException exception) {
712                         // Cannot happen
713
}
714                 }
715             } catch (BadPositionCategoryException exception) {
716                 RefactoringCorePlugin.log(exception);
717             } finally {
718                 document.removePositionUpdater(positionUpdater);
719                 try {
720                     document.removePositionCategory(COMPOSABLE_POSITION_CATEGORY);
721                 } catch (BadPositionCategoryException exception) {
722                     RefactoringCorePlugin.log(exception);
723                 }
724             }
725
726             return getContent(document, new Region(range.offset, range.length), expand, surround);
727
728         } catch (MalformedTreeException exception) {
729             RefactoringCorePlugin.log(exception);
730         } catch (BadLocationException exception) {
731             RefactoringCorePlugin.log(exception);
732         }
733         return getPreviewDocument(monitor).get();
734     }
735
736     /*
737      * @see org.eclipse.ltk.core.refactoring.TextEditBasedChange#getPreviewContent(org.eclipse.core.runtime.IProgressMonitor)
738      */

739     public final String JavaDoc getPreviewContent(final IProgressMonitor monitor) throws CoreException {
740         return getPreviewDocument(monitor).get();
741     }
742
743     /**
744      * Returns a document representing the preview of the refactored buffer,
745      * after the application of the change object.
746      *
747      * @param monitor
748      * the progress monitor to use, or <code>null</code>
749      * @return the preview document, or an empty document
750      * @throws CoreException
751      * if no document could be acquired
752      */

753     public final IDocument getPreviewDocument(IProgressMonitor monitor) throws CoreException {
754         if (monitor == null)
755             monitor= new NullProgressMonitor();
756
757         IDocument result= null;
758         IDocument document= null;
759         DocumentRewriteSession session= null;
760
761         try {
762
763             document= acquireDocument(new SubProgressMonitor(monitor, 1));
764             if (document != null) {
765                 result= new Document(document.get());
766
767                 if (result instanceof IDocumentExtension4)
768                     session= ((IDocumentExtension4) result).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
769
770                 performChanges(result, null, true);
771             }
772
773         } catch (BadLocationException exception) {
774             throw Changes.asCoreException(exception);
775         } finally {
776             if (document != null) {
777                 try {
778                     if (session != null && result != null)
779                         ((IDocumentExtension4) result).stopRewriteSession(session);
780                 } finally {
781                     releaseDocument(document, new SubProgressMonitor(monitor, 1));
782                 }
783             }
784             monitor.done();
785         }
786         if (result == null)
787             result= new Document();
788         return result;
789     }
790
791     /**
792      * Returns the save mode of this change.
793      *
794      * @return the save mode
795      */

796     public final int getSaveMode() {
797         return fSaveMode;
798     }
799
800     /*
801      * @see org.eclipse.ltk.core.refactoring.Change#initializeValidationData(org.eclipse.core.runtime.IProgressMonitor)
802      */

803     public final void initializeValidationData(final IProgressMonitor monitor) {
804         monitor.beginTask("", 1); //$NON-NLS-1$
805
fValidationState= BufferValidationState.create(fFile);
806         monitor.worked(1);
807     }
808
809     /*
810      * @see org.eclipse.ltk.core.refactoring.Change#isValid(org.eclipse.core.runtime.IProgressMonitor)
811      */

812     public final RefactoringStatus isValid(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
813         monitor.beginTask("", 1); //$NON-NLS-1$
814

815         final ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(fFile.getFullPath(), LocationKind.IFILE);
816         fDirty= buffer != null && buffer.isDirty();
817
818         final RefactoringStatus status= fValidationState.isValid(needsSaving());
819         if (needsSaving()) {
820             status.merge(Changes.validateModifiesFiles(new IFile[] { fFile}));
821         } else {
822             // we are reading the file. So it should be at least in sync
823
status.merge(Changes.checkInSync(new IFile[] { fFile}));
824         }
825         monitor.worked(1);
826         return status;
827     }
828
829     /**
830      * Does the change need saving?
831      *
832      * @return <code>true</code> if it needs saving, <code>false</code>
833      * otherwise
834      */

835     public final boolean needsSaving() {
836         return (fSaveMode & TextFileChange.FORCE_SAVE) != 0 || (!fDirty && (fSaveMode & TextFileChange.KEEP_SAVE_STATE) != 0);
837     }
838
839     /*
840      * @see org.eclipse.ltk.core.refactoring.Change#perform(org.eclipse.core.runtime.IProgressMonitor)
841      */

842     public final Change perform(final IProgressMonitor monitor) throws CoreException {
843         monitor.beginTask("", 3); //$NON-NLS-1$
844

845         IDocument document= null;
846         DocumentRewriteSession session= null;
847
848         try {
849             document= acquireDocument(new SubProgressMonitor(monitor, 1));
850             if (document instanceof IDocumentExtension4)
851                 session= ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
852
853             final LinkedList JavaDoc undoList= new LinkedList JavaDoc();
854             performChanges(document, undoList, false);
855
856             if (needsSaving())
857                 fBuffer.commit(new SubProgressMonitor(monitor, 1), false);
858
859             return new MultiStateUndoChange(getName(), fFile, (UndoEdit[]) undoList.toArray(new UndoEdit[undoList.size()]), fContentStamp, fSaveMode);
860
861         } catch (BadLocationException exception) {
862             throw Changes.asCoreException(exception);
863         } finally {
864             if (document != null) {
865                 try {
866                     if (session != null)
867                         ((IDocumentExtension4) document).stopRewriteSession(session);
868                 } finally {
869                     releaseDocument(document, new SubProgressMonitor(monitor, 1));
870                 }
871             }
872             monitor.done();
873         }
874     }
875
876     /**
877      * Performs the changes on the specified document.
878      *
879      * @param document
880      * the document to perform the changes on
881      * @param undoList
882      * the undo list, or <code>null</code> to discard the undos
883      * @param preview
884      * <code>true</code> if the changes are performed for preview,
885      * <code>false</code> otherwise
886      * @throws BadLocationException
887      * if the edit tree could not be applied
888      */

889     private void performChanges(final IDocument document, final LinkedList JavaDoc undoList, final boolean preview) throws BadLocationException {
890
891         for (final Iterator JavaDoc iterator= fChanges.iterator(); iterator.hasNext();) {
892             final ComposableBufferChange change= (ComposableBufferChange) iterator.next();
893
894             final UndoEdit edit= createTextEditProcessor(change, document, undoList != null ? TextEdit.CREATE_UNDO : TextEdit.NONE, preview).performEdits();
895             if (undoList != null)
896                 undoList.addFirst(edit);
897         }
898     }
899
900     /**
901      * Releases the document.
902      *
903      * @param document
904      * the document to release
905      * @param monitor
906      * the progress monitor
907      * @throws CoreException
908      */

909     private void releaseDocument(final IDocument document, final IProgressMonitor monitor) throws CoreException {
910         Assert.isTrue(fCount > 0);
911
912         if (fCount == 1)
913             FileBuffers.getTextFileBufferManager().disconnect(fFile.getFullPath(), LocationKind.IFILE, monitor);
914
915         fCount--;
916     }
917
918     /*
919      * @see org.eclipse.ltk.core.refactoring.TextEditBasedChange#setKeepPreviewEdits(boolean)
920      */

921     public final void setKeepPreviewEdits(final boolean keep) {
922         super.setKeepPreviewEdits(keep);
923
924         if (!keep)
925             fCopier= null;
926     }
927
928     /**
929      * Sets the save mode.
930      *
931      * @param mode
932      * The mode to set
933      */

934     public final void setSaveMode(final int mode) {
935         fSaveMode= mode;
936     }
937 }
938
Popular Tags