1 /*******************************************************************************
2  * Copyright (c) 2000, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.ltk.core.refactoring;
15 
16 import org.eclipse.core.runtime.CoreException;
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.OperationCanceledException;
19 import org.eclipse.core.runtime.PlatformObject;
20 import org.eclipse.core.runtime.SubProgressMonitor;
21 
22 /**
23  * Abstract super class for all refactorings. Refactorings are used to perform
24  * behavior-preserving workspace transformations. A refactoring offers two
25  * different kind of methods:
26  * <ol>
27  *   <li>methods to check conditions to determine if the refactoring can be carried out
28  *       in general and if transformation will be behavior-preserving.
29  *       </li>
30  *   <li>a method to create a {@link org.eclipse.ltk.core.refactoring.Change} object
31  *       that represents the actual work space modifications.
32  *       </li>
33  * </ol>
34  * The life cycle of a refactoring is as follows:
35  * <ol>
36  *   <li>the refactoring gets created</li>
37  *   <li>the refactoring is initialized with the elements to be refactored. It is
38  *       up to a concrete refactoring implementation to provide corresponding API.</li>
39  *   <li>{@link #checkInitialConditions(IProgressMonitor)} is called. The method
40  *       can be called more than once.</li>
41  *   <li>additional arguments are provided to perform the refactoring (for example
42  *       the new name of a element in the case of a rename refactoring). It is up
43  *       to a concrete implementation to provide corresponding API.</li>
44  *   <li>{@link #checkFinalConditions(IProgressMonitor)} is called. The method
45  *       can be called more than once. The method must not be called if
46  *       {@link #checkInitialConditions(IProgressMonitor)} returns a refactoring
47  *       status of severity {@link RefactoringStatus#FATAL}.</li>
48  *   <li>{@link #createChange(IProgressMonitor)} is called. The method must only be
49  *       called once after each call to {@link #checkFinalConditions(IProgressMonitor)}
50  *       and should not be called if one of the condition checking methods
51  *       returns a refactoring status of severity {@link RefactoringStatus#FATAL}.
52  *   </li>
53  *   <li>steps 4 to 6 can be executed repeatedly (for example when the user goes
54  *       back from the preview page).
55  *   </li>
56  * </ol>
57  *
58  * <p>
59  * A refactoring can not assume that all resources are saved before any methods
60  * are called on it. Therefore a refactoring must be able to deal with unsaved
61  * resources.
62  * </p>
63  * <p>
64  * The class should be subclassed by clients wishing to implement new refactorings.
65  * </p>
66  *
67  * @see RefactoringContext
68  *
69  * @since 3.0
70  */
71 public abstract class Refactoring extends PlatformObject {
72 
73 	private Object fValidationContext;
74 
75 	/**
76 	 * Sets the validation context used when calling
77 	 * {@link org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)}.
78 	 *
79 	 * @param context the <code>org.eclipse.swt.widgets.Shell</code> that is
80 	 * to be used to parent any dialogs with the user, or <code>null</code> if
81 	 * there is no UI context (declared as an <code>Object</code> to avoid any
82 	 * direct references on the SWT component)
83 	 */
setValidationContext(Object context)84 	public final void setValidationContext(Object context) {
85 		fValidationContext= context;
86 	}
87 
88 	/**
89 	 * Returns the validation context
90 	 *
91 	 * @return the validation context or <code>null</code> if no validation
92 	 *  context has been set.
93 	 */
getValidationContext()94 	public final Object getValidationContext() {
95 		return fValidationContext;
96 	}
97 
98 	/**
99 	 * Returns the refactoring's name.
100 	 *
101 	 * @return the refactoring's human readable name. Must not be
102 	 *  <code>null</code>
103 	 */
getName()104 	public abstract String getName();
105 
106 	//---- Conditions ------------------------------------------------------------
107 
108 	/**
109 	 * Returns the tick provider used for progress reporting for this
110 	 * refactoring.
111 	 *
112 	 * @return the refactoring tick provider used for progress reporting
113 	 *
114 	 * @since 3.2
115 	 */
getRefactoringTickProvider()116 	public final RefactoringTickProvider getRefactoringTickProvider() {
117 		RefactoringTickProvider result= doGetRefactoringTickProvider();
118 		if (result == null) {
119 			result= RefactoringTickProvider.DEFAULT;
120 		}
121 		return result;
122 	}
123 
124 	/**
125 	 * Hook method to provide the tick provider used for progress reporting.
126 	 * <p>
127 	 * Subclasses may override this method
128 	 * </p>
129 	 * @return the refactoring tick provider used for progress reporting
130 	 *
131 	 * @since 3.2
132 	 */
doGetRefactoringTickProvider()133 	protected RefactoringTickProvider doGetRefactoringTickProvider() {
134 		return RefactoringTickProvider.DEFAULT;
135 	}
136 
137 	/**
138 	 * Checks all conditions. This implementation calls <code>checkInitialConditions</code>
139 	 * and <code>checkFinalConditions</code>.
140 	 * <p>
141 	 * Subclasses may extend this method to provide additional condition checks.
142 	 * </p>
143 	 *
144 	 * @param pm a progress monitor to report progress
145 	 *
146 	 * @return a refactoring status. If the status is <code>RefactoringStatus#FATAL</code>
147 	 *  the refactoring has to be considered as not being executable.
148 	 *
149 	 * @throws CoreException if an exception occurred during condition checking.
150 	 *  If this happens then the condition checking has to be interpreted as failed
151 	 *
152 	 * @throws OperationCanceledException if the condition checking got canceled
153 	 *
154 	 * @see #checkInitialConditions(IProgressMonitor)
155 	 * @see #checkFinalConditions(IProgressMonitor)
156 	 */
checkAllConditions(IProgressMonitor pm)157 	public RefactoringStatus checkAllConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
158 		RefactoringTickProvider refactoringTickProvider= getRefactoringTickProvider();
159 		pm.beginTask("", refactoringTickProvider.getCheckAllConditionsTicks()); //$NON-NLS-1$
160 		RefactoringStatus result= new RefactoringStatus();
161 		result.merge(checkInitialConditions(new SubProgressMonitor(pm, refactoringTickProvider.getCheckInitialConditionsTicks())));
162 		if (!result.hasFatalError()) {
163 			if (pm.isCanceled())
164 				throw new OperationCanceledException();
165 			result.merge(checkFinalConditions(new SubProgressMonitor(pm, refactoringTickProvider.getCheckFinalConditionsTicks())));
166 		}
167 		pm.done();
168 		return result;
169 	}
170 
171 	/**
172 	 * Checks some initial conditions based on the element to be refactored. The
173 	 * method is typically called by the UI to perform an initial checks after an
174 	 * action has been executed.
175 	 * <p>
176 	 * The refactoring has to be considered as not being executable if the returned status
177 	 * has the severity of <code>RefactoringStatus#FATAL</code>.
178 	 * </p>
179 	 * <p>
180 	 * This method can be called more than once.
181 	 * </p>
182 	 *
183 	 * @param pm a progress monitor to report progress. Although initial checks
184 	 *  are supposed to execute fast, there can be certain situations where progress
185 	 *  reporting is necessary. For example rebuilding a corrupted index may report
186 	 *  progress.
187 	 *
188 	 * @return a refactoring status. If the status is <code>RefactoringStatus#FATAL</code>
189 	 *  the refactoring has to be considered as not being executable.
190 	 *
191 	 * @throws CoreException if an exception occurred during initial condition checking.
192 	 *  If this happens then the initial condition checking has to be interpreted as failed
193 	 *
194 	 * @throws OperationCanceledException if the condition checking got canceled
195 	 *
196 	 * @see #checkFinalConditions(IProgressMonitor)
197 	 * @see RefactoringStatus#FATAL
198 	 */
checkInitialConditions(IProgressMonitor pm)199 	public abstract RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException;
200 
201 	/**
202 	 * After <code>checkInitialConditions</code> has been performed and the user has
203 	 * provided all input necessary to perform the refactoring this method is called
204 	 * to check the remaining preconditions.
205 	 * <p>
206 	 * The refactoring has to be considered as not being executable if the returned status
207 	 * has the severity of <code>RefactoringStatus#FATAL</code>.
208 	 * </p>
209 	 * <p>
210 	 * This method can be called more than once.
211 	 * </p>
212 	 *
213 	 * @param pm a progress monitor to report progress
214 	 *
215 	 * @return a refactoring status. If the status is <code>RefactoringStatus#FATAL</code>
216 	 *  the refactoring is considered as not being executable.
217 	 *
218 	 * @throws CoreException if an exception occurred during final condition checking
219 	 *  If this happens then the final condition checking is interpreted as failed
220 	 *
221 	 * @throws OperationCanceledException if the condition checking got canceled
222 	 *
223 	 * @see #checkInitialConditions(IProgressMonitor)
224 	 * @see RefactoringStatus#FATAL
225 	 */
checkFinalConditions(IProgressMonitor pm)226 	public abstract RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException;
227 
228 	//---- change creation ------------------------------------------------------
229 
230 	/**
231 	 * Creates a {@link Change} object that performs the actual workspace
232 	 * transformation.
233 	 *
234 	 * @param pm a progress monitor to report progress
235 	 *
236 	 * @return the change representing the workspace modifications of the
237 	 *  refactoring
238 	 *
239 	 * @throws CoreException if an error occurred while creating the change
240 	 *
241 	 * @throws OperationCanceledException if the condition checking got canceled
242 	 */
createChange(IProgressMonitor pm)243 	public abstract Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException;
244 
245 	@Override
getAdapter(Class<T> adapter)246 	public <T> T getAdapter(Class<T> adapter) {
247 		if (adapter.isInstance(this)) {
248 			@SuppressWarnings("unchecked")
249 			T t= (T) this;
250 			return t;
251 		}
252 		return super.getAdapter(adapter);
253 	}
254 
255 	@Override
toString()256 	public String toString() {
257 		return getName();
258 	}
259 }
260