Source code: org/eclipse/ltk/core/refactoring/Change.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.ltk.core.refactoring;
12
13 import org.eclipse.core.runtime.CoreException;
14 import org.eclipse.core.runtime.IAdaptable;
15 import org.eclipse.core.runtime.IProgressMonitor;
16 import org.eclipse.core.runtime.OperationCanceledException;
17
18 import org.eclipse.ltk.internal.core.refactoring.Assert;
19
20 /**
21 * An abstract base implementation for object representing a generic change
22 * to the workbench. A <code>Change</code> object is typically created by
23 * calling <code>Refactoring.createChange()</code>. This class should be
24 * subclassed by clients wishing to provide new changes.
25 * <p>
26 * Changes are best executed by using a {@link PerformChangeOperation}. If clients
27 * execute a change directly then the following life cycle has to be honored:
28 * <ul>
29 * <li>after a single change or a tree of changes has been created, the
30 * method <code>initializeValidationState</code> has to be called.</li>
31 * <li>the method <code>isValid</code> can be used to determine if a change
32 * can still be applied to the workspace. If the method returns a {@link
33 * RefactoringStatus} with a severity of FATAL then the change has to be
34 * treated as invalid. Performing an invalid change isn't allowed and
35 * results in an unspecified result. This method can be called multiple
36 * times.
37 * <li>then the method perform can be called. An disabled change must not
38 * be executed. The perform method can only be called once. After a change
39 * as been executed only the method <code>dispose</code> must be called.</li>
40 * <li>the method dispose has to be called either after the perform method
41 * has been called or if a change is no longer needed. The second case
42 * for example occurs when the undo stack gets flushed and all change
43 * objects managed by the undo stack are no longer needed. The method
44 * dispose is typically implemented to unregister listeners register during the
45 * method <code>initializeValidationState</code>. There is no guarantee
46 * that <code>initializeValidationState</code>, <code>isValid</code>
47 * or <code>perform</code> has been called, before <code>dispose</code>
48 * is called.
49 * </ul>
50 * Below a code snippet that can be used to execute a change:
51 * <pre>
52 * Change change= createChange();
53 * try {
54 * change.initializeValidationState(pm);
55 *
56 * ....
57 *
58 * if (!change.isEnabled())
59 * return;
60 * RefactoringStatus valid= change.isValid(new SubProgressMonitor(pm, 1));
61 * if (valid.hasFatalError())
62 * return;
63 * Change undo= change.perform(new SubProgressMonitor(pm, 1));
64 * if (undo != null) {
65 * undo.initializeValidationState(new SubProgressMonitor(pm, 1));
66 * // do something with the undo object
67 * }
68 * } finally {
69 * change.dispose();
70 * }
71 * </pre>
72 * </p>
73 * <p>
74 * It is important that implementors of this abstract class provide an adequate
75 * implementation of <code>isValid</code> and that they provide an undo change
76 * via the return value of the method <code>perform</code>. If no undo can be
77 * provided then the perform method is allowed to return <code>null</code>. But
78 * implementors should be aware that not providing an undo object for a change
79 * object that is part of a larger change tree will result in the fact that for
80 * the whole change tree no undo object will be present.
81 * </p>
82 * <p>
83 * Clients may subclass this class.
84 * </p>
85 *
86 * @since 3.0
87 */
88 public abstract class Change implements IAdaptable {
89
90 private Change fParent;
91 private boolean fIsEnabled= true;
92
93 /**
94 * Constructs a new change object.
95 */
96 protected Change() {
97 }
98
99 /**
100 * Returns the human readable name of this change. The
101 * name <em>MUST</em> not be <code>null</code>.
102 *
103 * @return the human readable name of this change
104 */
105 public abstract String getName();
106
107 /**
108 * Returns whether this change is enabled or not. Disabled changes
109 * must not be executed.
110 *
111 * @return <code>true</code> if the change is enabled; <code>false</code>
112 * otherwise.
113 */
114 public boolean isEnabled() {
115 return fIsEnabled;
116 }
117
118 /**
119 * Sets whether this change is enabled or not.
120 *
121 * @param enabled <code>true</code> to enable this change; <code>
122 * false</code> otherwise
123 */
124 public void setEnabled(boolean enabled) {
125 fIsEnabled= enabled;
126 }
127
128 /**
129 * Returns the parent change. Returns <code>null</code> if no
130 * parent exists.
131 *
132 * @return the parent change
133 */
134 public Change getParent() {
135 return fParent;
136 }
137
138 /**
139 * Sets the parent of this change. Requires that this change isn't already
140 * connected to a parent. The parent can be <code>null</code> to disconnect
141 * this change from a parent.
142 *
143 * @param parent the parent of this change or <code>null</code>
144 */
145 /* package */ void setParent(Change parent) {
146 if (parent != null)
147 Assert.isTrue(fParent == null);
148 fParent= parent;
149 }
150
151 /**
152 * Hook method to initialize some internal state to provide an adequate answer
153 * for the <code>isValid</code> method. This method gets called after a change
154 * or a whole change tree has been created.
155 * <p>
156 * Typically this method is implemented in one of the following ways:
157 * <ul>
158 * <li>the change hooks up a listener on some delta notification mechanism
159 * and marks itself as invalid if it receives a certain delta. Is this
160 * the case the implementor must take care of unhooking the listener
161 * in <code>dispose</code>.</li>
162 * <li>the change remembers some information allowing to decide if a change
163 * object is still valid when <code>isValid</code> is called.</li>
164 * </ul>
165 * <p>
166 * For example, a change object that manipulates the content of an <code>IFile</code>
167 * could either listen to resource changes and detect that the file got changed or
168 * it could remember some content stamp and compare it with the actual content stamp
169 * when <code>isValid</code> is called.
170 * </p>
171 *
172 * @param pm a progress monitor
173 */
174 public abstract void initializeValidationData(IProgressMonitor pm);
175
176 /**
177 * Verifies that this change object is still valid and can be executed by calling
178 * <code>perform</code>. If a refactoring status with a severity of {@link
179 * RefactoringStatus#FATAL} is returned then the change has to be treated as invalid
180 * and can no longer be executed. Performing such a change produces an unspecified
181 * result and will very likely throw an exception.
182 * <p>
183 * This method is also called by the {@link IUndoManager UndoManager} to decide if
184 * an undo or redo change is still valid and therefore can be executed.
185 * </p>
186 *
187 * @param pm a progress monitor.
188 *
189 * @return a refactoring status describing the outcome of the validation check
190 *
191 * @throws CoreException if an error occurred during validation check. The change
192 * is to be treated as invalid if an exception occurs
193 *
194 * @throws OperationCanceledException if the validation check got cancelled
195 */
196 public abstract RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException;
197
198 /**
199 * Performs this change. If this method is call on an invalid or disabled change
200 * object the result is unspecified. Changes should in general not respond to
201 * {@link IProgressMonitor#isCanceled()} since canceling a change tree in the
202 * middle of its execution leaves the workspace in a half changed state.
203 *
204 * @param pm a progress monitor
205 *
206 * @return the undo change for this change object or <code>null</code> if no
207 * undo is provided
208 *
209 * @throws CoreException if an error occurred during change execution
210 */
211 public abstract Change perform(IProgressMonitor pm) throws CoreException;
212
213 /**
214 * Disposes this change. Subclasses that override this method typically
215 * unregister listeners which got registered during the call to <code>
216 * initializeValidationState</code>.
217 * <p>
218 * Subclasses may override this method.
219 * </p>
220 */
221 public void dispose() {
222 // empty default implementation
223 }
224
225 /**
226 * Returns the element modified by this <code>Change</code>. The method may return
227 * <code>null</code> if the change isn't related to an element.
228 *
229 * @return the element modified by this change
230 */
231 public abstract Object getModifiedElement();
232
233 /**
234 * {@inheritDoc}
235 */
236 public Object getAdapter(Class adapter) {
237 if (fParent == null)
238 return null;
239 return fParent.getAdapter(adapter);
240 }
241 }