1 package no.uio.ifi.refaktor.changers;
3 import java.lang.reflect.Modifier;
4 import java.util.Random;
6 import no.uio.ifi.refaktor.changers.RefaktorChangerException.Reason;
7 import no.uio.ifi.refaktor.extractors.ExtractAndMoveMethodPrefixesExtractor;
8 import no.uio.ifi.refaktor.utils.CompilationUnitTextSelection;
9 import no.uio.ifi.refaktor.utils.RefaktorDebug;
10 import no.uio.ifi.refaktor.utils.RefaktorHandleUtils;
12 import org.eclipse.core.resources.IProject;
13 import org.eclipse.core.runtime.Assert;
14 import org.eclipse.core.runtime.CoreException;
15 import org.eclipse.core.runtime.IProgressMonitor;
16 import org.eclipse.jdt.core.IMethod;
17 import org.eclipse.jdt.core.JavaModelException;
18 import org.eclipse.jdt.core.dom.IVariableBinding;
19 import org.eclipse.jdt.internal.corext.refactoring.code.ExtractMethodRefactoring;
20 import org.eclipse.jdt.internal.corext.refactoring.structure.MoveInstanceMethodProcessor;
21 import org.eclipse.ltk.core.refactoring.participants.MoveRefactoring;
24 * This class composes the refactorings known as
25 * Extract Method and Move Method.
27 * Before extracting, it finds the possible targets for the move
28 * with the help of a PropertyExtractor (see {@link ExtractAndMoveMethodPrefixesExtractor}),
29 * that extracts, from the AST, as set of safe prefixes that forms the base
30 * for calculating the target for the Move Method.
32 * The changer then tries to analyze which of the safe prefixes
33 * that is the best candidate. The best candidate is used to find the
34 * destination of the composed refactoring.
36 * When the destination is calculated, the changer
37 * first extracts a new method and then moves it to the
40 @SuppressWarnings("restriction")
41 public class ExtractAndMoveMethodExecutor implements Executor {
42 private CompilationUnitTextSelection compilationUnitTextSelection;
43 private ExtractAndMoveMethodPrefixesExtractor extractor;
44 private String newMethodName;
45 private String newMethodSignature;
46 private MoveMethodRefactoringTargetFinder targetFinder;
47 private RefactoringPerformer refactoringExecutor;
49 public ExtractAndMoveMethodExecutor(CompilationUnitTextSelection compilationUnitTextSelection) {
50 this(compilationUnitTextSelection, generateName());
53 public ExtractAndMoveMethodExecutor(CompilationUnitTextSelection compilationUnitTextSelection, String newMethodName) {
54 this.compilationUnitTextSelection = compilationUnitTextSelection;
55 setNewMethodName(newMethodName);
56 extractor = new ExtractAndMoveMethodPrefixesExtractor(compilationUnitTextSelection);
59 public void setNewMethodName(String newMethodName) {
60 this.newMethodName = newMethodName;
63 public static String generateName() {
64 return "generated_" + Math.abs(new Random().nextLong());
68 public void checkPreconditions() throws RefaktorChangerException {
69 checkIfSelectionIsValid();
71 checkIfUsefulTargetFound();
74 private void checkIfSelectionIsValid() {
75 if (!extractor.selectionIsValid())
76 throw new RefaktorChangerException(Reason.SELECTION_INVALID);
79 private void extractProperty() {
80 extractor.extractProperty();
83 private void checkIfUsefulTargetFound() {
84 if (!extractor.hasUsefulResults())
85 throw new RefaktorChangerException(Reason.NO_TARGET_FOUND);
89 public void executeChange(IProgressMonitor monitor, UndoResources undoResources) throws CoreException {
90 createRefactoringExecutor(monitor, undoResources);
91 checkConditionsAndPerformExtractMethodRefactoring();
92 checkConditionsAndPerformMoveMethodRefactoring();
95 private void checkConditionsAndPerformExtractMethodRefactoring() throws CoreException {
96 ExtractMethodRefactoring extractMethodRefactoring = createExtractMethodRefactoring();
97 refactoringExecutor.performRefactoring(extractMethodRefactoring);
98 assertThatNewMethodNameWasUsedWhenPerformingRefactoring(extractMethodRefactoring);
99 setNewMethodSignature(extractMethodRefactoring);
100 createMoveMethodRefactoringTargetFinder(extractMethodRefactoring);
103 private void checkConditionsAndPerformMoveMethodRefactoring() throws JavaModelException, CoreException {
104 // Here, the method we'd like to move must already exist since we need to look up an IMethod handle:
105 refactoringExecutor.performRefactoring(createMoveRefactoring());
108 private ExtractMethodRefactoring createExtractMethodRefactoring() throws CoreException {
109 ExtractMethodRefactoring extractMethodRefactoring = new ExtractMethodRefactoring(
110 compilationUnitTextSelection.getCompilationUnit(),
111 compilationUnitTextSelection.getOffset(),
112 compilationUnitTextSelection.getLength()
115 extractMethodRefactoring.setMethodName(newMethodName);
116 extractMethodRefactoring.setValidationContext(null);
117 extractMethodRefactoring.setReplaceDuplicates(true);
118 extractMethodRefactoring.setVisibility(Modifier.PUBLIC);
119 extractMethodRefactoring.setLinkedProposalModel(null);
120 return extractMethodRefactoring;
123 private MoveRefactoring createMoveRefactoring()
124 throws JavaModelException {
125 IMethod method = getHandleOfExtractedMethod();
126 MoveInstanceMethodProcessor refactoringProcessor = new MoveInstanceMethodProcessor(method, null);
127 if (refactoringProcessor.canEnableDelegateUpdating())
128 refactoringProcessor.setDelegateUpdating(false);
129 refactoringProcessor.setDeprecateDelegates(true);
130 refactoringProcessor.setUseGetters(true);
131 refactoringProcessor.setUseSetters(true);
132 refactoringProcessor.setInlineDelegator(true);
133 refactoringProcessor.setRemoveDelegator(true);
135 String typeName = compilationUnitTextSelection.getNameOfSurroundingType();
136 // Sanity check, removes a dependency on 'method' [vs]
137 assert method.getDeclaringType().getElementName().equals(typeName) : method.getDeclaringType().getElementName() +" / " +typeName;
138 // Set the name of the parameter that takes an object of
139 // the type of the class the method is moved from.
140 refactoringProcessor.setTargetName(typeName.toLowerCase());
142 IVariableBinding target = targetFinder.findTarget();
143 refactoringProcessor.setTarget(target);
145 RefaktorDebug.println("MoveMethod:\nTrying to move method " + method.getElementName() + " to " + target.getName());
147 return new MoveRefactoring(refactoringProcessor);
150 private IVariableBinding getOriginalTargetFromPrefix() {
151 return extractor.getMostFrequentPrefix().getVariableBindingOfFirstExpression();
154 private void assertThatNewMethodNameWasUsedWhenPerformingRefactoring(ExtractMethodRefactoring extractMethodRefactoring) {
155 assert extractMethodRefactoring.getMethodName().equals(newMethodName);
158 private void setNewMethodSignature(ExtractMethodRefactoring extractMethodRefactoring) {
159 newMethodSignature = extractMethodRefactoring.getSignature();
162 private void createMoveMethodRefactoringTargetFinder(ExtractMethodRefactoring extractMethodRefactoring)
163 throws JavaModelException {
164 targetFinder = new MoveMethodRefactoringTargetFinder(getHandleOfExtractedMethod(), getOriginalTargetFromPrefix(), extractMethodRefactoring.getParameterInfos());
167 private IMethod getHandleOfExtractedMethod() throws JavaModelException {
168 String packageName = compilationUnitTextSelection.getPackageName();
169 String typeName = compilationUnitTextSelection.getNameOfSurroundingType();
170 IProject project = compilationUnitTextSelection.getProject();
171 IMethod method = RefaktorHandleUtils.findMethodHandle(project, packageName, typeName, newMethodSignature);
172 Assert.isNotNull(method, "Can't find "+packageName+"."+typeName+"#"+newMethodSignature);
176 private void createRefactoringExecutor(IProgressMonitor monitor, UndoResources undoResources) {
177 refactoringExecutor = new SimpleRefactoringPerformer(monitor, undoResources);