KickJava   Java API By Example, From Geeks To Geeks.

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


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.ltk.core.refactoring;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.ISafeRunnable;
23 import org.eclipse.core.runtime.OperationCanceledException;
24 import org.eclipse.core.runtime.SafeRunner;
25 import org.eclipse.core.runtime.SubProgressMonitor;
26
27 import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
28 import org.eclipse.ltk.internal.core.refactoring.RefactoringCorePlugin;
29
30 /**
31  * Represents a composite change. Composite changes can be marked
32  * as synthetic. A synthetic composite changes might not be rendered
33  * in the refactoring preview tree to save display real-estate.
34  * <p>
35  * Clients may subclass this class.
36  * </p>
37  *
38  * @see Change
39  *
40  * @since 3.0
41  */

42 public class CompositeChange extends Change {
43
44     private String JavaDoc fName;
45     private List JavaDoc fChanges;
46     private boolean fIsSynthetic;
47     private Change fUndoUntilException;
48
49     /**
50      * Creates a new composite change with the given name.
51      *
52      * @param name the human readable name of the change. Will
53      * be used to display the change in the user interface
54      */

55     public CompositeChange(String JavaDoc name) {
56         this(name, new ArrayList JavaDoc(2));
57     }
58     
59     /**
60      * Creates a new composite change with the given name and array
61      * of children.
62      *
63      * @param name the human readable name of the change. Will
64      * be used to display the change in the user interface
65      * @param children the initial array of children
66      */

67     public CompositeChange(String JavaDoc name, Change[] children) {
68         this(name, new ArrayList JavaDoc(children.length));
69         addAll(children);
70     }
71             
72     private CompositeChange(String JavaDoc name, List JavaDoc changes) {
73         Assert.isNotNull(name);
74         Assert.isNotNull(changes);
75         fName= name;
76         fChanges= changes;
77     }
78     
79     /**
80      * Returns whether this change is synthetic or not.
81      *
82      * @return <code>true</code>if this change is synthetic; otherwise
83      * <code>false</code>
84      */

85     public boolean isSynthetic() {
86         return fIsSynthetic;
87     }
88
89     /**
90      * Marks this change as synthetic.
91      */

92     public void markAsSynthetic() {
93         fIsSynthetic= true;
94     }
95     
96     /**
97      * {@inheritDoc}
98      */

99     public String JavaDoc getName() {
100         return fName;
101     }
102     
103     /**
104      * Adds the given change to the list of children. The change to be added
105      * can be <code>null</code>. Adding a "null" change does nothing.
106      *
107      * @param change the change to add
108      */

109     public void add(Change change) {
110         if (change != null) {
111             Assert.isTrue(change.getParent() == null);
112             fChanges.add(change);
113             change.setParent(this);
114         }
115     }
116     
117     /**
118      * Adds all changes in the given array to the list of children.
119      *
120      * @param changes the changes to add
121      */

122     public void addAll(Change[] changes) {
123         for (int i= 0; i < changes.length; i++) {
124             add(changes[i]);
125         }
126     }
127     
128     /**
129      * Merges the children of the given composite change into this
130      * change. This means the changes are removed from the given
131      * composite change and added to this change.
132      *
133      * @param change the change to merge
134      */

135     public void merge(CompositeChange change) {
136         Change[] others= change.getChildren();
137         for (int i= 0; i < others.length; i++) {
138             Change other= others[i];
139             change.remove(other);
140             add(other);
141         }
142     }
143     
144     /**
145      * Removes the given change from the list of children.
146      *
147      * @param change the change to remove
148      *
149      * @return <code>true</code> if the change contained the given
150      * child; otherwise <code>false</code> is returned
151      */

152     public boolean remove(Change change) {
153         Assert.isNotNull(change);
154         boolean result= fChanges.remove(change);
155         if (result) {
156             change.setParent(null);
157         }
158         return result;
159         
160     }
161     
162     /**
163      * Removes all changes from this composite change.
164      *
165      * @return the list of changes removed from this composite
166      * change
167      *
168      * @since 3.1
169      */

170     public Change[] clear() {
171         Change[] result= (Change[])fChanges.toArray(new Change[fChanges.size()]);
172         fChanges.clear();
173         return result;
174     }
175     
176     /**
177      * Returns the children managed by this composite change.
178      *
179      * @return the children of this change or an empty array if no
180      * children exist
181      */

182     public Change[] getChildren() {
183         return (Change[])fChanges.toArray(new Change[fChanges.size()]);
184     }
185     
186     /**
187      * {@inheritDoc}
188      * <p>
189      * The composite change sends <code>setEnabled</code> to all its children.
190      * </p>
191      * <p>
192      * Client are allowed to extend this method.
193      * </p>
194      */

195     public void setEnabled(boolean enabled) {
196         super.setEnabled(enabled);
197         for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext(); ) {
198             ((Change)iter.next()).setEnabled(enabled);
199         }
200     }
201     
202     /**
203      * {@inheritDoc}
204      * <p>
205      * The composite change sends <code>initializeValidationData</code> to all its
206      * children.
207      * </p>
208      * <p>
209      * Client are allowed to extend this method.
210      * </p>
211      */

212     public void initializeValidationData(IProgressMonitor pm) {
213         pm.beginTask("", fChanges.size()); //$NON-NLS-1$
214
for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext();) {
215             Change change= (Change)iter.next();
216             change.initializeValidationData(new SubProgressMonitor(pm, 1));
217             pm.worked(1);
218         }
219     }
220     
221     /**
222      * {@inheritDoc}
223      * <p>
224      * The composite change sends <code>isValid</code> to all its children
225      * until the first one returns a status with a severity of <code>FATAL
226      * </code>. If one of the children throws an exception the remaining children
227      * will not receive the <code>isValid</code> call.
228      * </p>
229      * <p>
230      * Client are allowed to extend this method.
231      * </p>
232      */

233     public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException {
234         RefactoringStatus result= new RefactoringStatus();
235         pm.beginTask("", fChanges.size()); //$NON-NLS-1$
236
for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext() && !result.hasFatalError();) {
237             Change change= (Change)iter.next();
238             if (change.isEnabled())
239                 result.merge(change.isValid(new SubProgressMonitor(pm, 1)));
240             else
241                 pm.worked(1);
242             if (pm.isCanceled())
243                 throw new OperationCanceledException();
244         }
245         pm.done();
246         return result;
247     }
248     
249     /**
250      * {@inheritDoc}
251      * <p>
252      * The composite change sends <code>perform</code> to all its <em>enabled</em>
253      * children. If one of the children throws an exception the remaining children
254      * will not receive the <code>perform</code> call. In this case the method <code>
255      * getUndoUntilException</code> can be used to get an undo object containing the
256      * undo objects of all executed children.
257      * </p>
258      * <p>
259      * Client are allowed to extend this method.
260      * </p>
261      */

262     public Change perform(IProgressMonitor pm) throws CoreException {
263         fUndoUntilException= null;
264         List JavaDoc undos= new ArrayList JavaDoc(fChanges.size());
265         pm.beginTask("", fChanges.size()); //$NON-NLS-1$
266
pm.setTaskName(RefactoringCoreMessages.CompositeChange_performingChangesTask_name);
267         Change change= null;
268         boolean canceled= false;
269         try {
270             for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext();) {
271                 change= (Change)iter.next();
272                 if (canceled && !internalProcessOnCancel(change))
273                     continue;
274                 
275                 if (change.isEnabled()) {
276                     Change undoChange= null;
277                     try {
278                         undoChange= change.perform(new SubProgressMonitor(pm, 1));
279                     } catch(OperationCanceledException e) {
280                         canceled= true;
281                         if (!internalContinueOnCancel())
282                             throw e;
283                         undos= null;
284                     }
285                     if (undos != null) {
286                         if (undoChange == null) {
287                             undos= null;
288                         } else {
289                             undos.add(undoChange);
290                         }
291                     }
292                 }
293                 // remove the change from the list of children to give
294
// the garbage collector the change to collect the change. This
295
// ensures that the memory consumption doesn't go up when
296
// producing the undo change tree.
297
iter.remove();
298                 // Make sure we dispose the change since it will now longer be
299
// in the list of children when call CompositeChange#dispose()
300
final Change changeToDispose= change;
301                 SafeRunner.run(new ISafeRunnable() {
302                     public void run() throws Exception JavaDoc {
303                         changeToDispose.dispose();
304                     }
305                     public void handleException(Throwable JavaDoc exception) {
306                         RefactoringCorePlugin.log(exception);
307                     }
308                 });
309             }
310             if (canceled)
311                 throw new OperationCanceledException();
312             if (undos != null) {
313                 Collections.reverse(undos);
314                 return createUndoChange((Change[]) undos.toArray(new Change[undos.size()]));
315             } else {
316                 return null;
317             }
318         } catch (CoreException e) {
319             handleUndos(change, undos);
320             internalHandleException(change, e);
321             throw e;
322         } catch (RuntimeException JavaDoc e) {
323             handleUndos(change, undos);
324             internalHandleException(change, e);
325             throw e;
326         }
327     }
328     
329     private void handleUndos(Change failedChange, List JavaDoc undos) {
330         if (undos == null) {
331             fUndoUntilException= null;
332             return;
333         }
334         if (failedChange instanceof CompositeChange) {
335             Change partUndoChange= ((CompositeChange)failedChange).getUndoUntilException();
336             if (partUndoChange != null) {
337                 undos.add(partUndoChange);
338             }
339         }
340         if (undos.size() == 0) {
341             fUndoUntilException= new NullChange(getName());
342             return;
343         }
344         Collections.reverse(undos);
345         fUndoUntilException= createUndoChange((Change[]) undos.toArray(new Change[undos.size()]));
346     }
347     
348     /**
349      * Note: this is an internal method and should not be overridden outside of
350      * the refactoring framework.
351      * <p>
352      * The method gets called if one of the changes managed by this
353      * composite change generates an exception when performed.
354      * </p>
355      *
356      * @param change the change that caused the exception
357      * @param t the exception itself
358      */

359     protected void internalHandleException(Change change, Throwable JavaDoc t) {
360         // do nothing
361
}
362
363     /**
364      * Note: this is an internal method and should not be overridden outside of
365      * the refactoring framework.
366      * <p>
367      * The method gets called if one of the changes managed by this
368      * composite change generates an operation canceled exception when
369      * performed.
370      * </p>
371      *
372      * @return <code>true</code> if performing the change should
373      * continue on cancel; otherwise <code>false</code>
374      *
375      * @since 3.1
376      */

377     protected boolean internalContinueOnCancel() {
378         return false;
379     }
380
381     /**
382      * Note: this is an internal method and should not be overridden outside of
383      * the refactoring framework.
384      * <p>
385      * The method gets called if the execution of this change got canceled,
386      * but <code>internalContinueOnCancel</code> returned true.
387      * </p>
388      *
389      * @param change the change to perform
390      *
391      * @return <code>true</code> if the given change should be performed although
392      * the execution got canceled; otherwise <code>false</code>
393      *
394      * @since 3.1
395      */

396     protected boolean internalProcessOnCancel(Change change) {
397         return false;
398     }
399
400     /**
401      * {@inheritDoc}
402      * <p>
403      * The composite change sends <code>dispose</code> to all its children. It is guaranteed
404      * that all children receive the <code>dispose</code> call.
405      * </p>
406      */

407     public void dispose() {
408         for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext(); ) {
409             final Change change= (Change)iter.next();
410             SafeRunner.run(new ISafeRunnable() {
411                 public void run() throws Exception JavaDoc {
412                     change.dispose();
413                 }
414                 public void handleException(Throwable JavaDoc exception) {
415                     RefactoringCorePlugin.log(exception);
416                 }
417             });
418         }
419     }
420     
421     /**
422      * Returns the undo object containing all undo changes of those children
423      * that got successfully executed while performing this change. Returns
424      * <code>null</code> if all changes were executed successfully.
425      * <p>
426      * This method is not intended to be overridden or extended.
427      * </p>
428      * @return the undo object containing all undo changes of those children
429      * that got successfully executed while performing this change
430      */

431     public Change getUndoUntilException() {
432         return fUndoUntilException;
433     }
434         
435     /**
436      * Hook to create an undo change. The method should be overridden
437      * by clients which provide their own composite change to create
438      * a corresponding undo change.
439      *
440      * @param childUndos the child undo. The undo edits appear in the
441      * list in the reverse order of their execution. So the first
442      * change in the array is the undo change of the last change
443      * that got executed.
444      *
445      * @return the undo change
446      */

447     protected Change createUndoChange(Change[] childUndos) {
448         return new CompositeChange(getName(), childUndos);
449     }
450     
451     /**
452      * {@inheritDoc}
453      */

454     public Object JavaDoc[] getAffectedObjects() {
455         if (fChanges.size() == 0)
456             return new Object JavaDoc[0];
457         List JavaDoc result= new ArrayList JavaDoc();
458         for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext();) {
459             Change change= (Change)iter.next();
460             Object JavaDoc[] affectedObjects= change.getAffectedObjects();
461             if (affectedObjects == null)
462                 return null;
463             result.addAll(Arrays.asList(affectedObjects));
464         }
465         return result.toArray();
466     }
467     
468     /**
469      * {@inheritDoc}
470      */

471     public Object JavaDoc getModifiedElement() {
472         return null;
473     }
474
475     /**
476      * {@inheritDoc}
477      *
478      * @since 3.2
479      */

480     public ChangeDescriptor getDescriptor() {
481         for (final Iterator JavaDoc iterator= fChanges.iterator(); iterator.hasNext();) {
482             final Change change= (Change) iterator.next();
483             final ChangeDescriptor descriptor= change.getDescriptor();
484             if (descriptor != null)
485                 return descriptor;
486         }
487         return null;
488     }
489
490     public String JavaDoc toString() {
491         StringBuffer JavaDoc buff= new StringBuffer JavaDoc();
492         buff.append(getName());
493         buff.append("\n"); //$NON-NLS-1$
494
for (Iterator JavaDoc iter= fChanges.iterator(); iter.hasNext();) {
495             buff.append("<").append(iter.next().toString()).append("/>\n"); //$NON-NLS-2$ //$NON-NLS-1$
496
}
497         return buff.toString();
498     }
499 }
500
Popular Tags