1 /*******************************************************************************
2  * Copyright (c) 2006, 2018 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.jdt.internal.corext.refactoring.structure;
15 
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22 
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.NullProgressMonitor;
27 import org.eclipse.core.runtime.SubProgressMonitor;
28 
29 import org.eclipse.text.edits.MalformedTreeException;
30 import org.eclipse.text.edits.TextEdit;
31 
32 import org.eclipse.jface.text.BadLocationException;
33 import org.eclipse.jface.text.Document;
34 import org.eclipse.jface.text.IDocument;
35 
36 import org.eclipse.ltk.core.refactoring.GroupCategorySet;
37 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
38 import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
39 import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
40 import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
41 
42 import org.eclipse.jdt.core.Flags;
43 import org.eclipse.jdt.core.ICompilationUnit;
44 import org.eclipse.jdt.core.IField;
45 import org.eclipse.jdt.core.IInitializer;
46 import org.eclipse.jdt.core.IJavaElement;
47 import org.eclipse.jdt.core.IMember;
48 import org.eclipse.jdt.core.IMethod;
49 import org.eclipse.jdt.core.ISourceRange;
50 import org.eclipse.jdt.core.IType;
51 import org.eclipse.jdt.core.ITypeHierarchy;
52 import org.eclipse.jdt.core.JavaModelException;
53 import org.eclipse.jdt.core.dom.AST;
54 import org.eclipse.jdt.core.dom.ASTNode;
55 import org.eclipse.jdt.core.dom.ASTVisitor;
56 import org.eclipse.jdt.core.dom.Annotation;
57 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
58 import org.eclipse.jdt.core.dom.BodyDeclaration;
59 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
60 import org.eclipse.jdt.core.dom.CompilationUnit;
61 import org.eclipse.jdt.core.dom.Dimension;
62 import org.eclipse.jdt.core.dom.EnumDeclaration;
63 import org.eclipse.jdt.core.dom.Expression;
64 import org.eclipse.jdt.core.dom.FieldDeclaration;
65 import org.eclipse.jdt.core.dom.IExtendedModifier;
66 import org.eclipse.jdt.core.dom.IMethodBinding;
67 import org.eclipse.jdt.core.dom.ITypeBinding;
68 import org.eclipse.jdt.core.dom.Javadoc;
69 import org.eclipse.jdt.core.dom.MethodDeclaration;
70 import org.eclipse.jdt.core.dom.Modifier;
71 import org.eclipse.jdt.core.dom.SimpleName;
72 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
73 import org.eclipse.jdt.core.dom.Type;
74 import org.eclipse.jdt.core.dom.TypeDeclaration;
75 import org.eclipse.jdt.core.dom.TypeParameter;
76 import org.eclipse.jdt.core.dom.VariableDeclaration;
77 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
78 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
79 import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
80 import org.eclipse.jdt.core.search.IJavaSearchConstants;
81 import org.eclipse.jdt.core.search.SearchMatch;
82 import org.eclipse.jdt.core.search.SearchPattern;
83 
84 import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
85 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
86 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
87 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
88 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
89 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
90 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
91 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
92 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
93 import org.eclipse.jdt.internal.corext.refactoring.reorg.SourceReferenceUtil;
94 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsModel;
95 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsSolver;
96 import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeRefactoringProcessor;
97 import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
98 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
99 import org.eclipse.jdt.internal.corext.refactoring.util.TextEditBasedChangeManager;
100 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
101 import org.eclipse.jdt.internal.corext.util.JdtFlags;
102 import org.eclipse.jdt.internal.corext.util.Messages;
103 import org.eclipse.jdt.internal.corext.util.SearchUtils;
104 import org.eclipse.jdt.internal.core.manipulation.StubUtility;
105 import org.eclipse.jdt.internal.core.manipulation.util.Strings;
106 
107 import org.eclipse.jdt.ui.JavaElementLabels;
108 
109 import org.eclipse.jdt.internal.ui.JavaPlugin;
110 
111 /**
112  * Partial implementation of a hierarchy refactoring processor used in pull up,
113  * push down and extract supertype refactorings.
114  * <p>
115  * This processor provides common functionality to move members in a type
116  * hierarchy, and to perform a "Use Supertype" refactoring afterwards.
117  * </p>
118  *
119  * @since 3.2
120  */
121 public abstract class HierarchyProcessor extends SuperTypeRefactoringProcessor {
122 
123 	/**
124 	 * AST node visitor which performs the actual mapping.
125 	 */
126 	public static class TypeVariableMapper extends ASTVisitor {
127 
128 		/** The type variable mapping to use */
129 		protected final TypeVariableMaplet[] fMapping;
130 
131 		/** The AST rewrite to use */
132 		protected final ASTRewrite fRewrite;
133 
134 		/**
135 		 * Creates a new type variable mapper.
136 		 *
137 		 * @param rewrite
138 		 *            The AST rewrite to use
139 		 * @param mapping
140 		 *            The type variable mapping to use
141 		 */
TypeVariableMapper(final ASTRewrite rewrite, final TypeVariableMaplet[] mapping)142 		public TypeVariableMapper(final ASTRewrite rewrite, final TypeVariableMaplet[] mapping) {
143 			Assert.isNotNull(rewrite);
144 			Assert.isNotNull(mapping);
145 			fRewrite= rewrite;
146 			fMapping= mapping;
147 		}
148 
149 		@Override
visit(final SimpleName node)150 		public final boolean visit(final SimpleName node) {
151 			final ITypeBinding binding= node.resolveTypeBinding();
152 			if (binding != null && binding.isTypeVariable()) {
153 				String name= null;
154 				for (TypeVariableMaplet mapping : fMapping) {
155 					name= binding.getName();
156 					if (mapping.getSourceName().equals(name) && node.getIdentifier().equals(name)) {
157 						final MethodDeclaration declaration= ASTNodes.getParent(node, MethodDeclaration.class);
158 						if (declaration != null) {
159 							final IMethodBinding method= declaration.resolveBinding();
160 							if (method != null) {
161 								for (ITypeBinding b : method.getTypeParameters()) {
162 									if (b.isEqualTo(binding)) {
163 										return true;
164 									}
165 								}
166 							}
167 						}
168 						fRewrite.set(node, SimpleName.IDENTIFIER_PROPERTY, mapping.getTargetName(), null);
169 					}
170 				}
171 			}
172 			return true;
173 		}
174 	}
175 
areAllFragmentsDeleted(final FieldDeclaration declaration, final List<ASTNode> declarationNodes)176 	protected static boolean areAllFragmentsDeleted(final FieldDeclaration declaration, final List<ASTNode> declarationNodes) {
177 		return declarationNodes.containsAll(declaration.fragments());
178 	}
179 
checkProjectCompliance(CompilationUnitRewrite sourceRewriter, IType destination, IMember[] members)180 	protected static RefactoringStatus checkProjectCompliance(CompilationUnitRewrite sourceRewriter, IType destination, IMember[] members) {
181 		RefactoringStatus status= new RefactoringStatus();
182 		if (!JavaModelUtil.is50OrHigher(destination.getJavaProject())) {
183 			for (IMember member : members) {
184 				try {
185 					BodyDeclaration decl= ASTNodeSearchUtil.getBodyDeclarationNode(member, sourceRewriter.getRoot());
186 					if (decl != null) {
187 						for (final Iterator<IExtendedModifier> iterator= decl.modifiers().iterator(); iterator.hasNext();) {
188 							boolean reported= false;
189 							final IExtendedModifier modifier= iterator.next();
190 							if (!reported && modifier.isAnnotation()) {
191 								status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.PullUpRefactoring_incompatible_langauge_constructs, new String[]{JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_DEFAULT)}), JavaStatusContext.create(member)));
192 								reported= true;
193 							}
194 						}
195 					}
196 				} catch (JavaModelException exception) {
197 					JavaPlugin.log(exception);
198 				}
199 				if (member instanceof IMethod) {
200 					final IMethod method= (IMethod) member;
201 					try {
202 						if (Flags.isVarargs(method.getFlags())) {
203 							status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.PullUpRefactoring_incompatible_language_constructs1, new String[]{JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_DEFAULT)}), JavaStatusContext.create(member)));
204 						}
205 					} catch (JavaModelException exception) {
206 						JavaPlugin.log(exception);
207 					}
208 				}
209 			}
210 		}
211 		return status;
212 	}
213 
copyExtraDimensions(final VariableDeclaration oldVarDeclaration, final VariableDeclaration newVarDeclaration)214 	protected static void copyExtraDimensions(final VariableDeclaration oldVarDeclaration, final VariableDeclaration newVarDeclaration) {
215 		final AST ast= newVarDeclaration.getAST();
216 		for (Object extraDimension : oldVarDeclaration.extraDimensions()) {
217 			newVarDeclaration.extraDimensions().add(ASTNode.copySubtree(ast, (Dimension) extraDimension));
218 		}
219 	}
220 
copyExtraDimensions(final MethodDeclaration oldMethod, final MethodDeclaration newMethod)221 	protected static void copyExtraDimensions(final MethodDeclaration oldMethod, final MethodDeclaration newMethod) {
222 		final AST ast= newMethod.getAST();
223 		for (Object extraDimension : oldMethod.extraDimensions()) {
224 			newMethod.extraDimensions().add(ASTNode.copySubtree(ast, (Dimension) extraDimension));
225 		}
226 	}
227 
copyAnnotations(final FieldDeclaration oldField, final FieldDeclaration newField)228 	protected static void copyAnnotations(final FieldDeclaration oldField, final FieldDeclaration newField) {
229 		final AST ast= newField.getAST();
230 		for (Object element : oldField.modifiers()) {
231 			final IExtendedModifier modifier= (IExtendedModifier) element;
232 			final List<IExtendedModifier> modifiers= newField.modifiers();
233 			if (modifier.isAnnotation() && !modifiers.contains(modifier))
234 				modifiers.add((IExtendedModifier) ASTNode.copySubtree(ast, (Annotation) modifier));
235 		}
236 	}
237 
copyAnnotations(final MethodDeclaration oldMethod, final MethodDeclaration newMethod)238 	protected static void copyAnnotations(final MethodDeclaration oldMethod, final MethodDeclaration newMethod) {
239 		final AST ast= newMethod.getAST();
240 		for (Object element : oldMethod.modifiers()) {
241 			final IExtendedModifier modifier= (IExtendedModifier) element;
242 			final List<IExtendedModifier> modifiers= newMethod.modifiers();
243 			if (modifier.isAnnotation() && !modifiers.contains(modifier))
244 				modifiers.add((IExtendedModifier) ASTNode.copySubtree(ast, (Annotation) modifier));
245 		}
246 	}
247 
copyJavadocNode(final ASTRewrite rewrite, final BodyDeclaration oldDeclaration, final BodyDeclaration newDeclaration)248 	protected static void copyJavadocNode(final ASTRewrite rewrite, final BodyDeclaration oldDeclaration, final BodyDeclaration newDeclaration) {
249 		final Javadoc predecessor= oldDeclaration.getJavadoc();
250 		if (predecessor != null) {
251 			String newString= ASTNodes.getNodeSource(predecessor, false, true);
252 			if (newString != null) {
253 				newDeclaration.setJavadoc((Javadoc) rewrite.createStringPlaceholder(newString, ASTNode.JAVADOC));
254 			}
255 		}
256 	}
257 
copyThrownExceptions(final MethodDeclaration oldMethod, final MethodDeclaration newMethod)258 	protected static void copyThrownExceptions(final MethodDeclaration oldMethod, final MethodDeclaration newMethod) {
259 		final AST ast= newMethod.getAST();
260 		for (Object thrownExceptionType : oldMethod.thrownExceptionTypes()) {
261 			newMethod.thrownExceptionTypes().add(ASTNode.copySubtree(ast, (Type) thrownExceptionType));
262 		}
263 	}
264 
copyTypeParameters(final MethodDeclaration oldMethod, final MethodDeclaration newMethod)265 	protected static void copyTypeParameters(final MethodDeclaration oldMethod, final MethodDeclaration newMethod) {
266 		final AST ast= newMethod.getAST();
267 		for (Object typeParameter : oldMethod.typeParameters()) {
268 			newMethod.typeParameters().add(ASTNode.copySubtree(ast, (TypeParameter) typeParameter));
269 		}
270 	}
271 
createLabel(final IMember member)272 	protected static String createLabel(final IMember member) {
273 		if (member instanceof IType)
274 			return JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED);
275 		else if (member instanceof IMethod)
276 			return JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED);
277 		else if (member instanceof IField)
278 			return JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED);
279 		else if (member instanceof IInitializer)
280 			return RefactoringCoreMessages.HierarchyRefactoring_initializer;
281 		Assert.isTrue(false);
282 		return null;
283 	}
284 
createNewFieldDeclarationNode(final ASTRewrite rewrite, final CompilationUnit unit, final IField field, final VariableDeclarationFragment oldFieldFragment, final TypeVariableMaplet[] mapping, final IProgressMonitor monitor, final RefactoringStatus status, final int modifiers)285 	protected static FieldDeclaration createNewFieldDeclarationNode(final ASTRewrite rewrite, final CompilationUnit unit, final IField field, final VariableDeclarationFragment oldFieldFragment, final TypeVariableMaplet[] mapping, final IProgressMonitor monitor, final RefactoringStatus status, final int modifiers) throws JavaModelException {
286 		final VariableDeclarationFragment newFragment= rewrite.getAST().newVariableDeclarationFragment();
287 		copyExtraDimensions(oldFieldFragment, newFragment);
288 		if (oldFieldFragment.getInitializer() != null) {
289 			Expression newInitializer= null;
290 			if (mapping.length > 0)
291 				newInitializer= createPlaceholderForExpression(oldFieldFragment.getInitializer(), field.getCompilationUnit(), mapping, rewrite);
292 			else
293 				newInitializer= createPlaceholderForExpression(oldFieldFragment.getInitializer(), field.getCompilationUnit(), rewrite);
294 			newFragment.setInitializer(newInitializer);
295 		}
296 		newFragment.setName(((SimpleName) ASTNode.copySubtree(rewrite.getAST(), oldFieldFragment.getName())));
297 		final FieldDeclaration newField= rewrite.getAST().newFieldDeclaration(newFragment);
298 		final FieldDeclaration oldField= ASTNodeSearchUtil.getFieldDeclarationNode(field, unit);
299 		copyJavadocNode(rewrite, oldField, newField);
300 		copyAnnotations(oldField, newField);
301 		newField.modifiers().addAll(ASTNodeFactory.newModifiers(rewrite.getAST(), modifiers));
302 		final Type oldType= oldField.getType();
303 		Type newType= null;
304 		if (mapping.length > 0) {
305 			newType= createPlaceholderForType(oldType, field.getCompilationUnit(), mapping, rewrite);
306 		} else
307 			newType= createPlaceholderForType(oldType, field.getCompilationUnit(), rewrite);
308 		newField.setType(newType);
309 		return newField;
310 	}
311 
createPlaceholderForExpression(final Expression expression, final ICompilationUnit declaringCu, final ASTRewrite rewrite)312 	protected static Expression createPlaceholderForExpression(final Expression expression, final ICompilationUnit declaringCu, final ASTRewrite rewrite) throws JavaModelException {
313 		return (Expression) rewrite.createStringPlaceholder(declaringCu.getBuffer().getText(expression.getStartPosition(), expression.getLength()), ASTNode.METHOD_INVOCATION);
314 	}
315 
createPlaceholderForExpression(final Expression expression, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite)316 	protected static Expression createPlaceholderForExpression(final Expression expression, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite) throws JavaModelException {
317 		Expression result= null;
318 		try {
319 			final IDocument document= new Document(declaringCu.getBuffer().getContents());
320 			final ASTRewrite rewriter= ASTRewrite.create(expression.getAST());
321 			final ITrackedNodePosition position= rewriter.track(expression);
322 			expression.accept(new TypeVariableMapper(rewriter, mapping));
323 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
324 			result= (Expression) rewrite.createStringPlaceholder(document.get(position.getStartPosition(), position.getLength()), ASTNode.METHOD_INVOCATION);
325 		} catch (MalformedTreeException exception) {
326 			JavaPlugin.log(exception);
327 		} catch (BadLocationException exception) {
328 			JavaPlugin.log(exception);
329 		}
330 		return result;
331 	}
332 
createPlaceholderForProtectedTypeDeclaration(final BodyDeclaration bodyDeclaration, final CompilationUnit declaringCuNode, final ICompilationUnit declaringCu, final ASTRewrite rewrite, final boolean removeIndentation)333 	protected static BodyDeclaration createPlaceholderForProtectedTypeDeclaration(final BodyDeclaration bodyDeclaration, final CompilationUnit declaringCuNode, final ICompilationUnit declaringCu, final ASTRewrite rewrite, final boolean removeIndentation) throws JavaModelException {
334 		String text= null;
335 		try {
336 			final ASTRewrite rewriter= ASTRewrite.create(bodyDeclaration.getAST());
337 			ModifierRewrite.create(rewriter, bodyDeclaration).setVisibility(Modifier.PROTECTED, null);
338 			final ITrackedNodePosition position= rewriter.track(bodyDeclaration);
339 			final IDocument document= new Document(declaringCu.getBuffer().getText(declaringCuNode.getStartPosition(), declaringCuNode.getLength()));
340 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.UPDATE_REGIONS);
341 			text= document.get(position.getStartPosition(), position.getLength());
342 		} catch (BadLocationException exception) {
343 			text= getNewText(bodyDeclaration, declaringCu, removeIndentation);
344 		}
345 		return (BodyDeclaration) rewrite.createStringPlaceholder(text, ASTNode.TYPE_DECLARATION);
346 	}
347 
createPlaceholderForProtectedTypeDeclaration(final BodyDeclaration bodyDeclaration, final CompilationUnit declaringCuNode, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite, final boolean removeIndentation)348 	protected static BodyDeclaration createPlaceholderForProtectedTypeDeclaration(final BodyDeclaration bodyDeclaration, final CompilationUnit declaringCuNode, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite, final boolean removeIndentation) throws JavaModelException {
349 		BodyDeclaration result= null;
350 		try {
351 			final IDocument document= new Document(declaringCu.getBuffer().getContents());
352 			final ASTRewrite rewriter= ASTRewrite.create(bodyDeclaration.getAST());
353 			final ITrackedNodePosition position= rewriter.track(bodyDeclaration);
354 			bodyDeclaration.accept(new TypeVariableMapper(rewriter, mapping) {
355 
356 				@Override
357 				public final boolean visit(final AnnotationTypeDeclaration node) {
358 					ModifierRewrite.create(fRewrite, bodyDeclaration).setVisibility(Modifier.PROTECTED, null);
359 					return true;
360 				}
361 
362 				@Override
363 				public final boolean visit(final EnumDeclaration node) {
364 					ModifierRewrite.create(fRewrite, bodyDeclaration).setVisibility(Modifier.PROTECTED, null);
365 					return true;
366 				}
367 
368 				@Override
369 				public final boolean visit(final TypeDeclaration node) {
370 					ModifierRewrite.create(fRewrite, bodyDeclaration).setVisibility(Modifier.PROTECTED, null);
371 					return true;
372 				}
373 			});
374 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
375 			result= (BodyDeclaration) rewrite.createStringPlaceholder(document.get(position.getStartPosition(), position.getLength()), ASTNode.TYPE_DECLARATION);
376 		} catch (MalformedTreeException exception) {
377 			JavaPlugin.log(exception);
378 		} catch (BadLocationException exception) {
379 			JavaPlugin.log(exception);
380 		}
381 		return result;
382 	}
383 
createPlaceholderForSingleVariableDeclaration(final SingleVariableDeclaration declaration, final ICompilationUnit declaringCu, final ASTRewrite rewrite)384 	protected static SingleVariableDeclaration createPlaceholderForSingleVariableDeclaration(final SingleVariableDeclaration declaration, final ICompilationUnit declaringCu, final ASTRewrite rewrite) throws JavaModelException {
385 		return (SingleVariableDeclaration) rewrite.createStringPlaceholder(declaringCu.getBuffer().getText(declaration.getStartPosition(), declaration.getLength()), ASTNode.SINGLE_VARIABLE_DECLARATION);
386 	}
387 
createPlaceholderForSingleVariableDeclaration(final SingleVariableDeclaration declaration, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite)388 	protected static SingleVariableDeclaration createPlaceholderForSingleVariableDeclaration(final SingleVariableDeclaration declaration, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite) throws JavaModelException {
389 		SingleVariableDeclaration result= null;
390 		try {
391 			final IDocument document= new Document(declaringCu.getBuffer().getContents());
392 			final ASTRewrite rewriter= ASTRewrite.create(declaration.getAST());
393 			final ITrackedNodePosition position= rewriter.track(declaration);
394 			declaration.accept(new TypeVariableMapper(rewriter, mapping));
395 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
396 			result= (SingleVariableDeclaration) rewrite.createStringPlaceholder(document.get(position.getStartPosition(), position.getLength()), ASTNode.SINGLE_VARIABLE_DECLARATION);
397 		} catch (MalformedTreeException exception) {
398 			JavaPlugin.log(exception);
399 		} catch (BadLocationException exception) {
400 			JavaPlugin.log(exception);
401 		}
402 		return result;
403 	}
404 
createPlaceholderForType(final Type type, final ICompilationUnit declaringCu, final ASTRewrite rewrite)405 	protected static Type createPlaceholderForType(final Type type, final ICompilationUnit declaringCu, final ASTRewrite rewrite) throws JavaModelException {
406 		return (Type) rewrite.createStringPlaceholder(declaringCu.getBuffer().getText(type.getStartPosition(), type.getLength()), ASTNode.SIMPLE_TYPE);
407 	}
408 
createPlaceholderForType(final Type type, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite)409 	protected static Type createPlaceholderForType(final Type type, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite) throws JavaModelException {
410 		Type result= null;
411 		try {
412 			final IDocument document= new Document(declaringCu.getBuffer().getContents());
413 			final ASTRewrite rewriter= ASTRewrite.create(type.getAST());
414 			final ITrackedNodePosition position= rewriter.track(type);
415 			type.accept(new TypeVariableMapper(rewriter, mapping));
416 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
417 			result= (Type) rewrite.createStringPlaceholder(document.get(position.getStartPosition(), position.getLength()), ASTNode.SIMPLE_TYPE);
418 		} catch (MalformedTreeException exception) {
419 			JavaPlugin.log(exception);
420 		} catch (BadLocationException exception) {
421 			JavaPlugin.log(exception);
422 		}
423 		return result;
424 	}
425 
createPlaceholderForTypeDeclaration(final BodyDeclaration bodyDeclaration, final ICompilationUnit declaringCu, final ASTRewrite rewrite, final boolean removeIndentation)426 	protected static BodyDeclaration createPlaceholderForTypeDeclaration(final BodyDeclaration bodyDeclaration, final ICompilationUnit declaringCu, final ASTRewrite rewrite, final boolean removeIndentation) throws JavaModelException {
427 		return (BodyDeclaration) rewrite.createStringPlaceholder(getNewText(bodyDeclaration, declaringCu, removeIndentation), ASTNode.TYPE_DECLARATION);
428 	}
429 
createPlaceholderForTypeDeclaration(final BodyDeclaration bodyDeclaration, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite, final boolean removeIndentation)430 	protected static BodyDeclaration createPlaceholderForTypeDeclaration(final BodyDeclaration bodyDeclaration, final ICompilationUnit declaringCu, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite, final boolean removeIndentation) throws JavaModelException {
431 		BodyDeclaration result= null;
432 		try {
433 			final IDocument document= new Document(declaringCu.getBuffer().getContents());
434 			final ASTRewrite rewriter= ASTRewrite.create(bodyDeclaration.getAST());
435 			final ITrackedNodePosition position= rewriter.track(bodyDeclaration);
436 			bodyDeclaration.accept(new TypeVariableMapper(rewriter, mapping));
437 			rewriter.rewriteAST(document, declaringCu.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
438 			result= (BodyDeclaration) rewrite.createStringPlaceholder(document.get(position.getStartPosition(), position.getLength()), ASTNode.TYPE_DECLARATION);
439 		} catch (MalformedTreeException exception) {
440 			JavaPlugin.log(exception);
441 		} catch (BadLocationException exception) {
442 			JavaPlugin.log(exception);
443 		}
444 		return result;
445 	}
446 
deleteDeclarationNodes(final CompilationUnitRewrite sourceRewriter, final boolean sameCu, final CompilationUnitRewrite unitRewriter, final List<IMember> members, final GroupCategorySet set)447 	protected static void deleteDeclarationNodes(final CompilationUnitRewrite sourceRewriter, final boolean sameCu, final CompilationUnitRewrite unitRewriter, final List<IMember> members, final GroupCategorySet set) throws JavaModelException {
448 		final List<ASTNode> declarationNodes= getDeclarationNodes(unitRewriter.getRoot(), members);
449 		for (ASTNode node : declarationNodes) {
450 			final ASTRewrite rewriter= unitRewriter.getASTRewrite();
451 			final ImportRemover remover= unitRewriter.getImportRemover();
452 			if (node instanceof VariableDeclarationFragment) {
453 				if (node.getParent() instanceof FieldDeclaration) {
454 					final FieldDeclaration declaration= (FieldDeclaration) node.getParent();
455 					if (areAllFragmentsDeleted(declaration, declarationNodes)) {
456 						rewriter.remove(declaration, unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_remove_member, set));
457 						if (!sameCu)
458 							remover.registerRemovedNode(declaration);
459 					} else {
460 						rewriter.remove(node, unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_remove_member, set));
461 						if (!sameCu)
462 							remover.registerRemovedNode(node);
463 					}
464 				}
465 			} else {
466 				rewriter.remove(node, unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_remove_member, set));
467 				if (!sameCu)
468 					remover.registerRemovedNode(node);
469 			}
470 		}
471 	}
472 
getDeclarationNodes(final CompilationUnit cuNode, final List<IMember> members)473 	protected static List<ASTNode> getDeclarationNodes(final CompilationUnit cuNode, final List<IMember> members) throws JavaModelException {
474 		final List<ASTNode> result= new ArrayList<>(members.size());
475 		for (IMember member : members) {
476 			ASTNode node= null;
477 			if (member instanceof IField) {
478 				if (Flags.isEnum(member.getFlags()))
479 					node= ASTNodeSearchUtil.getEnumConstantDeclaration((IField) member, cuNode);
480 				else
481 					node= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField) member, cuNode);
482 			} else if (member instanceof IType)
483 				node= ASTNodeSearchUtil.getAbstractTypeDeclarationNode((IType) member, cuNode);
484 			else if (member instanceof IMethod)
485 				node= ASTNodeSearchUtil.getMethodDeclarationNode((IMethod) member, cuNode);
486 			if (node != null)
487 				result.add(node);
488 		}
489 		return result;
490 	}
491 
getNewText(final ASTNode node, final ICompilationUnit declaringCu, final boolean removeIndentation)492 	protected static String getNewText(final ASTNode node, final ICompilationUnit declaringCu, final boolean removeIndentation) throws JavaModelException {
493 		final String result= declaringCu.getBuffer().getText(node.getStartPosition(), node.getLength());
494 		if (removeIndentation)
495 			return getUnindentedText(result, declaringCu);
496 
497 		return result;
498 	}
499 
getUnindentedText(final String text, final ICompilationUnit declaringCu)500 	protected static String getUnindentedText(final String text, final ICompilationUnit declaringCu) throws JavaModelException {
501 		final String[] lines= Strings.convertIntoLines(text);
502 		Strings.trimIndentation(lines, declaringCu.getJavaProject(), false);
503 		return Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(declaringCu));
504 	}
505 
506 	/** The cached declaring type */
507 	protected IType fCachedDeclaringType;
508 
509 	/** The cached member references */
510 	protected final Map<IMember, Object[]> fCachedMembersReferences= new HashMap<>(2);
511 
512 	/** The cached type references */
513 	protected IType[] fCachedReferencedTypes;
514 
515 	/** The text edit based change manager */
516 	protected TextEditBasedChangeManager fChangeManager;
517 
518 	/** Does the refactoring use a working copy layer? */
519 	protected final boolean fLayer;
520 
521 	/** The members to move (may be in working copies) */
522 	protected IMember[] fMembersToMove;
523 
524 	/**
525 	 * Creates a new hierarchy processor.
526 	 *
527 	 * @param members
528 	 *            the members, or <code>null</code> if invoked by scripting
529 	 * @param settings
530 	 *            the code generation settings to use
531 	 * @param layer
532 	 *            <code>true</code> to create a working copy layer,
533 	 *            <code>false</code> otherwise
534 	 */
HierarchyProcessor(final IMember[] members, final CodeGenerationSettings settings, boolean layer)535 	protected HierarchyProcessor(final IMember[] members, final CodeGenerationSettings settings, boolean layer) {
536 		super(settings);
537 		fLayer= layer;
538 		if (members != null) {
539 			fMembersToMove= (IMember[]) SourceReferenceUtil.sortByOffset(members);
540 			if (layer && fMembersToMove.length > 0) {
541 				final ICompilationUnit original= fMembersToMove[0].getCompilationUnit();
542 				if (original != null) {
543 					try {
544 						final ICompilationUnit copy= getSharedWorkingCopy(original.getPrimary(), new NullProgressMonitor());
545 						if (copy != null) {
546 							for (int index= 0; index < fMembersToMove.length; index++) {
547 								final IJavaElement[] elements= copy.findElements(fMembersToMove[index]);
548 								if (elements != null && elements.length > 0 && elements[0] instanceof IMember) {
549 									fMembersToMove[index]= (IMember) elements[0];
550 								}
551 							}
552 						}
553 					} catch (JavaModelException exception) {
554 						JavaPlugin.log(exception);
555 					}
556 				}
557 			}
558 		}
559 	}
560 
canBeAccessedFrom(final IMember member, final IType target, final ITypeHierarchy hierarchy)561 	protected boolean canBeAccessedFrom(final IMember member, final IType target, final ITypeHierarchy hierarchy) throws JavaModelException {
562 		Assert.isTrue(!(member instanceof IInitializer));
563 		return member.exists();
564 	}
565 
checkConstructorCalls(final IType type, final IProgressMonitor monitor)566 	protected RefactoringStatus checkConstructorCalls(final IType type, final IProgressMonitor monitor) throws JavaModelException {
567 		try {
568 			monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 2);
569 			final RefactoringStatus result= new RefactoringStatus();
570 			final String message= Messages.format(RefactoringCoreMessages.HierarchyRefactoring_gets_instantiated, new Object[] { JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)});
571 
572 			for (SearchResultGroup group : ConstructorReferenceFinder.getConstructorReferences(type, fOwner, new SubProgressMonitor(monitor, 1), result)) {
573 				ICompilationUnit unit= group.getCompilationUnit();
574 				if (unit != null) {
575 					final CompilationUnit cuNode= RefactoringASTParser.parseWithASTProvider(unit, false, new SubProgressMonitor(monitor, 1));
576 					for (ASTNode node : ASTNodeSearchUtil.getAstNodes(group.getSearchResults(), cuNode)) {
577 						if ((node instanceof ClassInstanceCreation) || ConstructorReferenceFinder.isImplicitConstructorReferenceNodeInClassCreations(node)) {
578 							final RefactoringStatusContext context= JavaStatusContext.create(unit, node);
579 							result.addError(message, context);
580 						}
581 					}
582 				}
583 			}
584 			return result;
585 		} finally {
586 			monitor.done();
587 		}
588 	}
589 
checkDeclaringType(final IProgressMonitor monitor)590 	protected RefactoringStatus checkDeclaringType(final IProgressMonitor monitor) throws JavaModelException {
591 		try {
592 			final IType type= getDeclaringType();
593 			if (type.isEnum())
594 				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.HierarchyRefactoring_enum_members);
595 			if (type.isAnnotation())
596 				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.HierarchyRefactoring_annotation_members);
597 			if (type.isInterface())
598 				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.HierarchyRefactoring_interface_members);
599 			if (type.isBinary())
600 				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.HierarchyRefactoring_members_of_binary);
601 			if (type.isReadOnly())
602 				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.HierarchyRefactoring_members_of_read_only);
603 			return new RefactoringStatus();
604 		} finally {
605 			if (monitor != null)
606 				monitor.done();
607 		}
608 	}
609 
checkIfMembersExist()610 	protected RefactoringStatus checkIfMembersExist() {
611 		final RefactoringStatus result= new RefactoringStatus();
612 		for (IMember member : fMembersToMove) {
613 			if (member == null || !member.exists())
614 				result.addFatalError(RefactoringCoreMessages.HierarchyRefactoring_does_not_exist);
615 		}
616 		return result;
617 	}
618 
clearCaches()619 	protected void clearCaches() {
620 		fCachedReferencedTypes= null;
621 	}
622 
copyParameters(final ASTRewrite rewrite, final ICompilationUnit unit, final MethodDeclaration oldMethod, final MethodDeclaration newMethod, final TypeVariableMaplet[] mapping)623 	protected void copyParameters(final ASTRewrite rewrite, final ICompilationUnit unit, final MethodDeclaration oldMethod, final MethodDeclaration newMethod, final TypeVariableMaplet[] mapping) throws JavaModelException {
624 		SingleVariableDeclaration newDeclaration= null;
625 		for (Object parameter : oldMethod.parameters()) {
626 			final SingleVariableDeclaration oldDeclaration= (SingleVariableDeclaration) parameter;
627 			if (mapping.length > 0)
628 				newDeclaration= createPlaceholderForSingleVariableDeclaration(oldDeclaration, unit, mapping, rewrite);
629 			else
630 				newDeclaration= createPlaceholderForSingleVariableDeclaration(oldDeclaration, unit, rewrite);
631 			newMethod.parameters().add(newDeclaration);
632 		}
633 	}
634 
copyReturnType(final ASTRewrite rewrite, final ICompilationUnit unit, final MethodDeclaration oldMethod, final MethodDeclaration newMethod, final TypeVariableMaplet[] mapping)635 	protected void copyReturnType(final ASTRewrite rewrite, final ICompilationUnit unit, final MethodDeclaration oldMethod, final MethodDeclaration newMethod, final TypeVariableMaplet[] mapping) throws JavaModelException {
636 		Type newReturnType= null;
637 		if (mapping.length > 0)
638 			newReturnType= createPlaceholderForType(oldMethod.getReturnType2(), unit, mapping, rewrite);
639 		else
640 			newReturnType= createPlaceholderForType(oldMethod.getReturnType2(), unit, rewrite);
641 		newMethod.setReturnType2(newReturnType);
642 	}
643 
644 	@Override
createContraintSolver(final SuperTypeConstraintsModel model)645 	protected SuperTypeConstraintsSolver createContraintSolver(final SuperTypeConstraintsModel model) {
646 		return new SuperTypeConstraintsSolver(model);
647 	}
648 
getDeclaringType()649 	public IType getDeclaringType() {
650 		if (fCachedDeclaringType != null)
651 			return fCachedDeclaringType;
652 		fCachedDeclaringType= RefactoringAvailabilityTester.getTopLevelType(fMembersToMove);
653 		if (fCachedDeclaringType == null)
654 			fCachedDeclaringType= fMembersToMove[0].getDeclaringType();
655 		return fCachedDeclaringType;
656 	}
657 
getMembersToMove()658 	public IMember[] getMembersToMove() {
659 		return fMembersToMove;
660 	}
661 
getTypesReferencedInMovedMembers(final IProgressMonitor monitor)662 	protected IType[] getTypesReferencedInMovedMembers(final IProgressMonitor monitor) throws JavaModelException {
663 		if (fCachedReferencedTypes == null) {
664 			final IType[] types= ReferenceFinderUtil.getTypesReferencedIn(fMembersToMove, fOwner, monitor);
665 			final List<IType> result= new ArrayList<>(types.length);
666 			final List<IMember> members= Arrays.asList(fMembersToMove);
667 			for (IType type : types) {
668 				if (!members.contains(type) && !type.equals(getDeclaringType())) {
669 					result.add(type);
670 				}
671 			}
672 			fCachedReferencedTypes= new IType[result.size()];
673 			result.toArray(fCachedReferencedTypes);
674 		}
675 		return fCachedReferencedTypes;
676 	}
677 
hasNonMovedReferences(final IMember member, final IProgressMonitor monitor, final RefactoringStatus status)678 	protected boolean hasNonMovedReferences(final IMember member, final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
679 		if (!fCachedMembersReferences.containsKey(member)) {
680 			final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(member, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE));
681 			engine.setFiltering(true, true);
682 			engine.setStatus(status);
683 			engine.setOwner(fOwner);
684 			engine.setScope(RefactoringScopeFactory.create(member));
685 			engine.searchPattern(new SubProgressMonitor(monitor, 1));
686 			fCachedMembersReferences.put(member, engine.getResults());
687 		}
688 		final SearchResultGroup[] groups= (SearchResultGroup[]) fCachedMembersReferences.get(member);
689 		if (groups.length == 0)
690 			return false;
691 		else if (groups.length > 1)
692 			return true;
693 		final ICompilationUnit unit= groups[0].getCompilationUnit();
694 		if (!getDeclaringType().getCompilationUnit().equals(unit))
695 			return true;
696 		for (SearchMatch match : groups[0].getSearchResults()) {
697 			if (!isMovedReference(match)) {
698 				return true;
699 			}
700 		}
701 		return false;
702 	}
703 
isMovedReference(final SearchMatch match)704 	protected boolean isMovedReference(final SearchMatch match) throws JavaModelException {
705 		for (IMember m : fMembersToMove) {
706 			ISourceRange range= m.getSourceRange();
707 			if (range.getOffset() <= match.getOffset() && range.getOffset() + range.getLength() >= match.getOffset())
708 				return true;
709 		}
710 		return false;
711 	}
712 
713 	@Override
loadParticipants(final RefactoringStatus status, final SharableParticipants sharedParticipants)714 	public RefactoringParticipant[] loadParticipants(final RefactoringStatus status, final SharableParticipants sharedParticipants) throws CoreException {
715 		return new RefactoringParticipant[0];
716 	}
717 
needsVisibilityAdjustment(final IMember member, final boolean references, final IProgressMonitor monitor, final RefactoringStatus status)718 	protected boolean needsVisibilityAdjustment(final IMember member, final boolean references, final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
719 		if (JdtFlags.isPublic(member) || JdtFlags.isProtected(member))
720 			return false;
721 		if (!references)
722 			return true;
723 		return hasNonMovedReferences(member, monitor, status);
724 	}
725 }
726