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