1 /*******************************************************************************
2  * Copyright (c) 2006, 2020 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.Collection;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 
25 import org.eclipse.core.runtime.Assert;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.core.runtime.OperationCanceledException;
29 import org.eclipse.core.runtime.SubProgressMonitor;
30 
31 import org.eclipse.text.edits.MalformedTreeException;
32 import org.eclipse.text.edits.TextEdit;
33 
34 import org.eclipse.jface.text.BadLocationException;
35 import org.eclipse.jface.text.Document;
36 import org.eclipse.jface.text.IDocument;
37 
38 import org.eclipse.ltk.core.refactoring.Change;
39 import org.eclipse.ltk.core.refactoring.GroupCategory;
40 import org.eclipse.ltk.core.refactoring.GroupCategorySet;
41 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
42 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
43 import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
44 
45 import org.eclipse.jdt.core.Flags;
46 import org.eclipse.jdt.core.ICompilationUnit;
47 import org.eclipse.jdt.core.IField;
48 import org.eclipse.jdt.core.IJavaElement;
49 import org.eclipse.jdt.core.IJavaProject;
50 import org.eclipse.jdt.core.IMember;
51 import org.eclipse.jdt.core.IMethod;
52 import org.eclipse.jdt.core.IType;
53 import org.eclipse.jdt.core.ITypeHierarchy;
54 import org.eclipse.jdt.core.JavaModelException;
55 import org.eclipse.jdt.core.dom.AST;
56 import org.eclipse.jdt.core.dom.ASTNode;
57 import org.eclipse.jdt.core.dom.ASTRequestor;
58 import org.eclipse.jdt.core.dom.ASTVisitor;
59 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
60 import org.eclipse.jdt.core.dom.Block;
61 import org.eclipse.jdt.core.dom.CompilationUnit;
62 import org.eclipse.jdt.core.dom.Expression;
63 import org.eclipse.jdt.core.dom.FieldDeclaration;
64 import org.eclipse.jdt.core.dom.MarkerAnnotation;
65 import org.eclipse.jdt.core.dom.MethodDeclaration;
66 import org.eclipse.jdt.core.dom.Modifier;
67 import org.eclipse.jdt.core.dom.Name;
68 import org.eclipse.jdt.core.dom.ThisExpression;
69 import org.eclipse.jdt.core.dom.Type;
70 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
71 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
72 import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
73 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
74 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
75 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
76 import org.eclipse.jdt.core.refactoring.descriptors.PushDownDescriptor;
77 import org.eclipse.jdt.core.search.IJavaSearchConstants;
78 import org.eclipse.jdt.core.search.SearchEngine;
79 import org.eclipse.jdt.core.search.SearchMatch;
80 import org.eclipse.jdt.core.search.SearchPattern;
81 
82 import org.eclipse.jdt.internal.core.manipulation.StubUtility;
83 import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
84 import org.eclipse.jdt.internal.core.manipulation.util.Strings;
85 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
86 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
87 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
88 import org.eclipse.jdt.internal.corext.dom.BodyDeclarationRewrite;
89 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
90 import org.eclipse.jdt.internal.corext.refactoring.Checks;
91 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
92 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
93 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
94 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
95 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
96 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
97 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
98 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
99 import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
100 import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment;
101 import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext;
102 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
103 import org.eclipse.jdt.internal.corext.refactoring.util.TextEditBasedChangeManager;
104 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
105 import org.eclipse.jdt.internal.corext.util.JdtFlags;
106 import org.eclipse.jdt.internal.corext.util.Messages;
107 import org.eclipse.jdt.internal.corext.util.SearchUtils;
108 
109 import org.eclipse.jdt.ui.JavaElementLabels;
110 
111 import org.eclipse.jdt.internal.ui.JavaPlugin;
112 import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
113 
114 
115 /**
116  * Refactoring processor for the push down refactoring.
117  *
118  * @since 3.2
119  */
120 public final class PushDownRefactoringProcessor extends HierarchyProcessor {
121 
122 	public static class MemberActionInfo implements IMemberActionInfo {
123 
124 		public static final int NO_ACTION= 2;
125 
126 		public static final int PUSH_ABSTRACT_ACTION= 1;
127 
128 		public static final int PUSH_DOWN_ACTION= 0;
129 
assertValidAction(IMember member, int action)130 		private static void assertValidAction(IMember member, int action) {
131 			if (member instanceof IMethod)
132 				Assert.isTrue(action == PUSH_ABSTRACT_ACTION || action == NO_ACTION || action == PUSH_DOWN_ACTION);
133 			else if (member instanceof IField)
134 				Assert.isTrue(action == NO_ACTION || action == PUSH_DOWN_ACTION);
135 		}
136 
create(IMember member, int action)137 		public static MemberActionInfo create(IMember member, int action) {
138 			return new MemberActionInfo(member, action);
139 		}
140 
getMembers(MemberActionInfo[] infos)141 		static IMember[] getMembers(MemberActionInfo[] infos) {
142 			IMember[] result= new IMember[infos.length];
143 			for (int i= 0; i < result.length; i++) {
144 				result[i]= infos[i].getMember();
145 			}
146 			return result;
147 		}
148 
149 		private int fAction;
150 
151 		private final IMember fMember;
152 
MemberActionInfo(IMember member, int action)153 		private MemberActionInfo(IMember member, int action) {
154 			assertValidAction(member, action);
155 			Assert.isTrue(member instanceof IField || member instanceof IMethod);
156 			fMember= member;
157 			fAction= action;
158 		}
159 
copyJavadocToCopiesInSubclasses()160 		boolean copyJavadocToCopiesInSubclasses() {
161 			return isToBeDeletedFromDeclaringClass();
162 		}
163 
getAction()164 		public int getAction() {
165 			return fAction;
166 		}
167 
getAvailableActions()168 		public int[] getAvailableActions() {
169 			if (isFieldInfo())
170 				return new int[] { PUSH_DOWN_ACTION, NO_ACTION };
171 
172 			return new int[] { PUSH_DOWN_ACTION, PUSH_ABSTRACT_ACTION, NO_ACTION };
173 		}
174 
getMember()175 		public IMember getMember() {
176 			return fMember;
177 		}
178 
getNewModifiersForCopyInSubclass(int oldModifiers)179 		int getNewModifiersForCopyInSubclass(int oldModifiers) throws JavaModelException {
180 			if (isFieldInfo())
181 				return oldModifiers;
182 			if (isToBeDeletedFromDeclaringClass())
183 				return oldModifiers;
184 			int modifiers= oldModifiers;
185 			if (isNewMethodToBeDeclaredAbstract()) {
186 				if (!JdtFlags.isPublic(fMember))
187 					modifiers= Modifier.PROTECTED | JdtFlags.clearAccessModifiers(modifiers);
188 			}
189 			return modifiers;
190 		}
191 
getNewModifiersForOriginal(int oldModifiers)192 		int getNewModifiersForOriginal(int oldModifiers) throws JavaModelException {
193 			if (isFieldInfo())
194 				return oldModifiers;
195 			if (isToBeDeletedFromDeclaringClass())
196 				return oldModifiers;
197 			int modifiers= oldModifiers;
198 			if (isNewMethodToBeDeclaredAbstract()) {
199 				modifiers= JdtFlags.clearFlag(Modifier.FINAL | Modifier.NATIVE, oldModifiers);
200 				modifiers|= Modifier.ABSTRACT;
201 
202 				if (!JdtFlags.isPublic(fMember))
203 					modifiers= Modifier.PROTECTED | JdtFlags.clearAccessModifiers(modifiers);
204 			}
205 			return modifiers;
206 		}
207 
208 		@Override
isActive()209 		public boolean isActive() {
210 			return getAction() != NO_ACTION;
211 		}
212 
isEditable()213 		public boolean isEditable() {
214 			if (isFieldInfo())
215 				return false;
216 			if (getAction() == MemberActionInfo.NO_ACTION)
217 				return false;
218 			return true;
219 		}
220 
isFieldInfo()221 		boolean isFieldInfo() {
222 			return fMember instanceof IField;
223 		}
224 
isNewMethodToBeDeclaredAbstract()225 		boolean isNewMethodToBeDeclaredAbstract() throws JavaModelException {
226 			return !isFieldInfo() && !JdtFlags.isAbstract(fMember) && fAction == PUSH_ABSTRACT_ACTION;
227 		}
228 
isToBeCreatedInSubclassesOfDeclaringClass()229 		boolean isToBeCreatedInSubclassesOfDeclaringClass() {
230 			return fAction != NO_ACTION;
231 		}
232 
isToBeDeletedFromDeclaringClass()233 		boolean isToBeDeletedFromDeclaringClass() {
234 			return isToBePushedDown();
235 		}
236 
isToBePushedDown()237 		public boolean isToBePushedDown() {
238 			return fAction == PUSH_DOWN_ACTION;
239 		}
240 
setAction(int action)241 		public void setAction(int action) {
242 			assertValidAction(fMember, action);
243 			if (isFieldInfo())
244 				Assert.isTrue(action != PUSH_ABSTRACT_ACTION);
245 			fAction= action;
246 		}
247 
248 	}
249 
250 	private static final String ATTRIBUTE_ABSTRACT= "abstract"; //$NON-NLS-1$
251 
252 	private static final String ATTRIBUTE_PUSH= "push"; //$NON-NLS-1$
253 
254 	/** The identifier of this processor */
255 	public static final String IDENTIFIER= "org.eclipse.jdt.ui.pushDownProcessor"; //$NON-NLS-1$
256 
257 	/** The push down group category set */
258 	private static final GroupCategorySet SET_PUSH_DOWN= new GroupCategorySet(new GroupCategory("org.eclipse.jdt.internal.corext.pushDown", //$NON-NLS-1$
259 			RefactoringCoreMessages.PushDownRefactoring_category_name, RefactoringCoreMessages.PushDownRefactoring_category_description));
260 
createInfosForAllPushableFieldsAndMethods(IType type)261 	private static MemberActionInfo[] createInfosForAllPushableFieldsAndMethods(IType type) throws JavaModelException {
262 		List<MemberActionInfo> result= new ArrayList<>();
263 		for (IMember pushableMember : RefactoringAvailabilityTester.getPushDownMembers(type)) {
264 			result.add(MemberActionInfo.create(pushableMember, MemberActionInfo.NO_ACTION));
265 		}
266 		return result.toArray(new MemberActionInfo[result.size()]);
267 	}
268 
getAbstractMembers(IMember[] members)269 	private static IMember[] getAbstractMembers(IMember[] members) throws JavaModelException {
270 		List<IMember> result= new ArrayList<>(members.length);
271 		for (IMember member : members) {
272 			if (JdtFlags.isAbstract(member))
273 				result.add(member);
274 		}
275 		return result.toArray(new IMember[result.size()]);
276 	}
277 
getCompilationUnitRewrite(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final ICompilationUnit unit)278 	private static CompilationUnitRewrite getCompilationUnitRewrite(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final ICompilationUnit unit) {
279 		Assert.isNotNull(rewrites);
280 		Assert.isNotNull(unit);
281 		CompilationUnitRewrite rewrite= rewrites.get(unit);
282 		if (rewrite == null) {
283 			rewrite= new CompilationUnitRewrite(unit);
284 			rewrites.put(unit, rewrite);
285 		}
286 		return rewrite;
287 	}
288 
getReferencingElementsFromSameClass(IMember member, IProgressMonitor pm, RefactoringStatus status)289 	private static IJavaElement[] getReferencingElementsFromSameClass(IMember member, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
290 		Assert.isNotNull(member);
291 		final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(member, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE));
292 		engine.setFiltering(true, true);
293 		engine.setScope(SearchEngine.createJavaSearchScope(new IJavaElement[] { member.getDeclaringType() }));
294 		engine.setStatus(status);
295 		engine.searchPattern(new SubProgressMonitor(pm, 1));
296 		Set<IJavaElement> result= new HashSet<>(3);
297 		for (SearchResultGroup group : (SearchResultGroup[]) engine.getResults()) {
298 			for (SearchMatch searchResult : group.getSearchResults()) {
299 				result.add(SearchUtils.getEnclosingJavaElement(searchResult));
300 			}
301 		}
302 		return result.toArray(new IJavaElement[result.size()]);
303 	}
304 
305 	private ITypeHierarchy fCachedClassHierarchy;
306 
307 	private MemberActionInfo[] fMemberInfos;
308 
309 	/**
310 	 * Creates a new push down refactoring processor.
311 	 *
312 	 * @param members
313 	 *            the members to pull up
314 	 */
PushDownRefactoringProcessor(IMember[] members)315 	public PushDownRefactoringProcessor(IMember[] members) {
316 		super(members, null, false);
317 		if (members != null) {
318 			final IType type= RefactoringAvailabilityTester.getTopLevelType(members);
319 			try {
320 				if (type != null && RefactoringAvailabilityTester.getPushDownMembers(type).length != 0) {
321 					fMembersToMove= new IMember[0];
322 					fCachedDeclaringType= type;
323 				}
324 			} catch (JavaModelException exception) {
325 				JavaPlugin.log(exception);
326 			}
327 		}
328 	}
329 
330 	/**
331 	 * Creates a new push down refactoring processor from refactoring arguments.
332 	 *
333 	 * @param arguments
334 	 *            the refactoring arguments
335 	 * @param status
336 	 *            the resulting status
337 	 */
PushDownRefactoringProcessor(JavaRefactoringArguments arguments, RefactoringStatus status)338 	public PushDownRefactoringProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) {
339 		super(null, null, false);
340 		RefactoringStatus initializeStatus= initialize(arguments);
341 		status.merge(initializeStatus);
342 	}
343 
addAllRequiredPushableMembers(List<IMember> queue, IMember member, IProgressMonitor monitor)344 	private void addAllRequiredPushableMembers(List<IMember> queue, IMember member, IProgressMonitor monitor) throws JavaModelException {
345 		monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, 2);
346 		IProgressMonitor sub= new SubProgressMonitor(monitor, 1);
347 		sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, 2);
348 		IMethod[] requiredMethods= ReferenceFinderUtil.getMethodsReferencedIn(new IJavaElement[] { member }, new SubProgressMonitor(sub, 1));
349 		sub= new SubProgressMonitor(sub, 1);
350 		sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, requiredMethods.length);
351 		for (IMethod method : requiredMethods) {
352 			if (!MethodChecks.isVirtual(method) && (method.getDeclaringType().equals(getDeclaringType()) && !queue.contains(method) && RefactoringAvailabilityTester.isPushDownAvailable(method)))
353 				queue.add(method);
354 		}
355 		sub.done();
356 		for (IField field : ReferenceFinderUtil.getFieldsReferencedIn(new IJavaElement[] { member }, new SubProgressMonitor(monitor, 1))) {
357 			if (field.getDeclaringType().equals(getDeclaringType()) && !queue.contains(field) && RefactoringAvailabilityTester.isPushDownAvailable(field))
358 				queue.add(field);
359 		}
360 		monitor.done();
361 	}
362 
checkAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForAbstract)363 	private RefactoringStatus checkAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForAbstract) throws JavaModelException {
364 		RefactoringStatus result= new RefactoringStatus();
365 		IMember[] abstractMembersToPushDown= getAbstractMembers(membersToPushDown);
366 		for (IType type : destinationClassesForAbstract) {
367 			result.merge(MemberCheckUtil.checkMembersInDestinationType(abstractMembersToPushDown, type));
368 		}
369 		return result;
370 	}
371 
checkAccessedFields(IType[] subclasses, IProgressMonitor pm)372 	private RefactoringStatus checkAccessedFields(IType[] subclasses, IProgressMonitor pm) throws JavaModelException {
373 		RefactoringStatus result= new RefactoringStatus();
374 		IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass());
375 		List<IMember> pushedDownList= Arrays.asList(membersToPushDown);
376 		IField[] accessedFields= ReferenceFinderUtil.getFieldsReferencedIn(membersToPushDown, pm);
377 		for (IType targetClass : subclasses) {
378 			ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null);
379 			for (IField field : accessedFields) {
380 				boolean isAccessible= pushedDownList.contains(field) || canBeAccessedFrom(field, targetClass, targetSupertypes) || Flags.isEnum(field.getFlags());
381 				if (!isAccessible) {
382 					String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_field_not_accessible, new String[] { JavaElementLabels.getTextLabel(field, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(targetClass, JavaElementLabels.ALL_FULLY_QUALIFIED) });
383 					result.addError(message, JavaStatusContext.create(field));
384 				}
385 			}
386 		}
387 		pm.done();
388 		return result;
389 	}
390 
checkAccessedMethods(IType[] subclasses, IProgressMonitor pm)391 	private RefactoringStatus checkAccessedMethods(IType[] subclasses, IProgressMonitor pm) throws JavaModelException {
392 		RefactoringStatus result= new RefactoringStatus();
393 		IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass());
394 		List<IMember> pushedDownList= Arrays.asList(membersToPushDown);
395 		IMethod[] accessedMethods= ReferenceFinderUtil.getMethodsReferencedIn(membersToPushDown, pm);
396 		for (IType targetClass : subclasses) {
397 			ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null);
398 			for (IMethod method : accessedMethods) {
399 				boolean isAccessible= pushedDownList.contains(method) || canBeAccessedFrom(method, targetClass, targetSupertypes);
400 				if (!isAccessible) {
401 					String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_method_not_accessible, new String[] { JavaElementLabels.getTextLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(targetClass, JavaElementLabels.ALL_FULLY_QUALIFIED) });
402 					result.addError(message, JavaStatusContext.create(method));
403 				}
404 			}
405 		}
406 		pm.done();
407 		return result;
408 	}
409 
checkAccessedTypes(IType[] subclasses, IProgressMonitor pm)410 	private RefactoringStatus checkAccessedTypes(IType[] subclasses, IProgressMonitor pm) throws JavaModelException {
411 		RefactoringStatus result= new RefactoringStatus();
412 		IType[] accessedTypes= getTypesReferencedInMovedMembers(pm);
413 		for (IType targetClass : subclasses) {
414 			ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null);
415 			for (IType type : accessedTypes) {
416 				if (!canBeAccessedFrom(type, targetClass, targetSupertypes)) {
417 					String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_type_not_accessible, new String[] { JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(targetClass, JavaElementLabels.ALL_FULLY_QUALIFIED) });
418 					result.addError(message, JavaStatusContext.create(type));
419 				}
420 			}
421 		}
422 		pm.done();
423 		return result;
424 	}
425 
checkElementsAccessedByModifiedMembers(IProgressMonitor pm)426 	private RefactoringStatus checkElementsAccessedByModifiedMembers(IProgressMonitor pm) throws JavaModelException {
427 		RefactoringStatus result= new RefactoringStatus();
428 		pm.beginTask(RefactoringCoreMessages.PushDownRefactoring_check_references, 3);
429 		IType[] subclasses= getAbstractDestinations(new SubProgressMonitor(pm, 1));
430 		result.merge(checkAccessedTypes(subclasses, new SubProgressMonitor(pm, 1)));
431 		result.merge(checkAccessedFields(subclasses, new SubProgressMonitor(pm, 1)));
432 		result.merge(checkAccessedMethods(subclasses, new SubProgressMonitor(pm, 1)));
433 		pm.done();
434 		return result;
435 	}
436 
437 	@Override
checkFinalConditions(IProgressMonitor monitor, CheckConditionsContext context)438 	public RefactoringStatus checkFinalConditions(IProgressMonitor monitor, CheckConditionsContext context) throws CoreException, OperationCanceledException {
439 		try {
440 			monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 5);
441 			clearCaches();
442 			ICompilationUnit unit= getDeclaringType().getCompilationUnit();
443 			if (fLayer)
444 				unit= unit.findWorkingCopy(fOwner);
445 			resetWorkingCopies(unit);
446 			final RefactoringStatus result= new RefactoringStatus();
447 			result.merge(checkMembersInDestinationClasses(new SubProgressMonitor(monitor, 1)));
448 			result.merge(checkElementsAccessedByModifiedMembers(new SubProgressMonitor(monitor, 1)));
449 			result.merge(checkReferencesToPushedDownMembers(new SubProgressMonitor(monitor, 1)));
450 			if (!JdtFlags.isAbstract(getDeclaringType()) && getAbstractDeclarationInfos().length != 0)
451 				result.merge(checkConstructorCalls(getDeclaringType(), new SubProgressMonitor(monitor, 1)));
452 			else
453 				monitor.worked(1);
454 			if (result.hasFatalError())
455 				return result;
456 			List<IMember> members= new ArrayList<>(fMemberInfos.length);
457 			for (MemberActionInfo memberInfo : fMemberInfos) {
458 				if (memberInfo.getAction() != MemberActionInfo.NO_ACTION) {
459 					members.add(memberInfo.getMember());
460 				}
461 			}
462 			fMembersToMove= members.toArray(new IMember[members.size()]);
463 			fChangeManager= createChangeManager(new SubProgressMonitor(monitor, 1), result);
464 			if (result.hasFatalError())
465 				return result;
466 
467 			Checks.addModifiedFilesToChecker(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), context);
468 
469 			return result;
470 		} finally {
471 			monitor.done();
472 		}
473 	}
474 
475 	@Override
checkInitialConditions(IProgressMonitor monitor)476 	public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
477 		try {
478 			monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 1);
479 			RefactoringStatus status= new RefactoringStatus();
480 			status.merge(checkPossibleSubclasses(new SubProgressMonitor(monitor, 1)));
481 			if (status.hasFatalError())
482 				return status;
483 			status.merge(checkDeclaringType(new SubProgressMonitor(monitor, 1)));
484 			if (status.hasFatalError())
485 				return status;
486 			status.merge(checkIfMembersExist());
487 			if (status.hasFatalError())
488 				return status;
489 			fMemberInfos= createInfosForAllPushableFieldsAndMethods(getDeclaringType());
490 			List<IMember> list= Arrays.asList(fMembersToMove);
491 			for (MemberActionInfo info : fMemberInfos) {
492 				if (list.contains(info.getMember()))
493 					info.setAction(MemberActionInfo.PUSH_DOWN_ACTION);
494 			}
495 			return status;
496 		} finally {
497 			monitor.done();
498 		}
499 	}
500 
checkMembersInDestinationClasses(IProgressMonitor monitor)501 	private RefactoringStatus checkMembersInDestinationClasses(IProgressMonitor monitor) throws JavaModelException {
502 		monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 2);
503 		RefactoringStatus result= new RefactoringStatus();
504 		IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass());
505 
506 		IType[] destinationClassesForNonAbstract= getAbstractDestinations(new SubProgressMonitor(monitor, 1));
507 		result.merge(checkNonAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForNonAbstract));
508 		List<IMember> list= Arrays.asList(getAbstractMembers(getAbstractDestinations(new SubProgressMonitor(monitor, 1))));
509 
510 		IType[] destinationClassesForAbstract= list.toArray(new IType[list.size()]);
511 		result.merge(checkAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForAbstract));
512 		monitor.done();
513 		return result;
514 	}
515 
checkNonAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForNonAbstract)516 	private RefactoringStatus checkNonAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForNonAbstract) throws JavaModelException {
517 		RefactoringStatus result= new RefactoringStatus();
518 		List<IMember> list= new ArrayList<>(); // Arrays.asList does not support removing
519 		list.addAll(Arrays.asList(membersToPushDown));
520 		list.removeAll(Arrays.asList(getAbstractMembers(membersToPushDown)));
521 		IMember[] nonAbstractMembersToPushDown= list.toArray(new IMember[list.size()]);
522 		for (IType type : destinationClassesForNonAbstract) {
523 			result.merge(MemberCheckUtil.checkMembersInDestinationType(nonAbstractMembersToPushDown, type));
524 		}
525 		return result;
526 	}
527 
checkPossibleSubclasses(IProgressMonitor pm)528 	private RefactoringStatus checkPossibleSubclasses(IProgressMonitor pm) throws JavaModelException {
529 		IType[] modifiableSubclasses= getAbstractDestinations(pm);
530 		if (modifiableSubclasses.length == 0) {
531 			String msg= Messages.format(RefactoringCoreMessages.PushDownRefactoring_no_subclasses, new String[] { JavaElementLabels.getTextLabel(getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED) });
532 			return RefactoringStatus.createFatalErrorStatus(msg);
533 		}
534 		return new RefactoringStatus();
535 	}
536 
checkReferencesToPushedDownMembers(IProgressMonitor monitor)537 	private RefactoringStatus checkReferencesToPushedDownMembers(IProgressMonitor monitor) throws JavaModelException {
538 		List<IMember> fields= new ArrayList<>(fMemberInfos.length);
539 		for (MemberActionInfo info : fMemberInfos) {
540 			if (info.isToBePushedDown())
541 				fields.add(info.getMember());
542 		}
543 		IMember[] membersToPush= fields.toArray(new IMember[fields.size()]);
544 		RefactoringStatus result= new RefactoringStatus();
545 		List<IMember> movedMembers= Arrays.asList(MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()));
546 		monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_check_references, membersToPush.length);
547 		for (IMember member : membersToPush) {
548 			String label= createLabel(member);
549 			for (IJavaElement element : getReferencingElementsFromSameClass(member, new SubProgressMonitor(monitor, 1), result)) {
550 				if (movedMembers.contains(element))
551 					continue;
552 				if (!(element instanceof IMember))
553 					continue;
554 				IMember referencingMember= (IMember) element;
555 				Object[] keys= { label, createLabel(referencingMember) };
556 				String msg= Messages.format(RefactoringCoreMessages.PushDownRefactoring_referenced, keys);
557 				result.addError(msg, JavaStatusContext.create(referencingMember));
558 			}
559 		}
560 		monitor.done();
561 		return result;
562 	}
563 
computeAdditionalRequiredMembersToPushDown(IProgressMonitor monitor)564 	public void computeAdditionalRequiredMembersToPushDown(IProgressMonitor monitor) throws JavaModelException {
565 		List<IMember> list= Arrays.asList(getAdditionalRequiredMembers(monitor));
566 		for (MemberActionInfo info : fMemberInfos) {
567 			if (list.contains(info.getMember()))
568 				info.setAction(MemberActionInfo.PUSH_DOWN_ACTION);
569 		}
570 	}
571 
572 	/**
573 	 * AST node visitor which performs This check and removes qualifier if needed
574 	 */
575 	public static class ThisVisitor extends ASTVisitor {
576 
577 		private final IType fDeclaringType;
578 		private final ASTRewrite fRewrite;
579 
ThisVisitor(final ASTRewrite rewrite, final IType declaringType)580 		public ThisVisitor(final ASTRewrite rewrite, final IType declaringType) {
581 			Assert.isNotNull(rewrite);
582 			Assert.isNotNull(declaringType);
583 			fRewrite= rewrite;
584 			fDeclaringType= declaringType;
585 		}
586 
587 		@Override
visit(ThisExpression node)588 		public final boolean visit(ThisExpression node) {
589 			Name q= node.getQualifier();
590 			if (q == null) {
591 				return false;
592 			}
593 			String qName= q.getFullyQualifiedName();
594 			if (qName.equals(fDeclaringType.getElementName()) || qName.equals(fDeclaringType.getFullyQualifiedName())) {
595 				fRewrite.set(node, ThisExpression.QUALIFIER_PROPERTY, null, null);
596 			}
597 			return false;
598 		}
599 	}
600 
copyBodyOfPushedDownMethod(ASTRewrite targetRewrite, IMethod method, MethodDeclaration oldMethod, MethodDeclaration newMethod, TypeVariableMaplet[] mapping)601 	private void copyBodyOfPushedDownMethod(ASTRewrite targetRewrite, IMethod method, MethodDeclaration oldMethod, MethodDeclaration newMethod, TypeVariableMaplet[] mapping) throws JavaModelException {
602 		Block body= oldMethod.getBody();
603 		if (body == null) {
604 			newMethod.setBody(null);
605 			return;
606 		}
607 		try {
608 			final IDocument document= new Document(method.getCompilationUnit().getBuffer().getContents());
609 			final ASTRewrite rewriter= ASTRewrite.create(body.getAST());
610 			final ITrackedNodePosition position= rewriter.track(body);
611 			body.accept(new TypeVariableMapper(rewriter, mapping));
612 			body.accept(new ThisVisitor(rewriter, fCachedDeclaringType));
613 			rewriter.rewriteAST(document, getDeclaringType().getCompilationUnit().getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
614 			String content= document.get(position.getStartPosition(), position.getLength());
615 			String[] lines= Strings.convertIntoLines(content);
616 			Strings.trimIndentation(lines, method.getJavaProject(), false);
617 			content= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(method));
618 			newMethod.setBody((Block) targetRewrite.createStringPlaceholder(content, ASTNode.BLOCK));
619 		} catch (MalformedTreeException exception) {
620 			JavaPlugin.log(exception);
621 		} catch (BadLocationException exception) {
622 			JavaPlugin.log(exception);
623 		}
624 	}
625 
copyMembers(Collection<MemberVisibilityAdjustor> adjustors, Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, Map<ICompilationUnit, CompilationUnitRewrite> rewrites, RefactoringStatus status, MemberActionInfo[] infos, IType[] destinations, CompilationUnitRewrite sourceRewriter, CompilationUnitRewrite unitRewriter, IProgressMonitor monitor)626 	private void copyMembers(Collection<MemberVisibilityAdjustor> adjustors, Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, Map<ICompilationUnit, CompilationUnitRewrite> rewrites, RefactoringStatus status, MemberActionInfo[] infos, IType[] destinations, CompilationUnitRewrite sourceRewriter, CompilationUnitRewrite unitRewriter, IProgressMonitor monitor) throws JavaModelException {
627 		try {
628 			monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 1);
629 			IType type= null;
630 			TypeVariableMaplet[] mapping= null;
631 			for (IType destination : destinations) {
632 				type= destination;
633 				mapping= TypeVariableUtil.superTypeToInheritedType(getDeclaringType(), type);
634 				if (unitRewriter.getCu().equals(type.getCompilationUnit())) {
635 					IMember member= null;
636 					MemberVisibilityAdjustor adjustor= null;
637 					AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, unitRewriter.getRoot());
638 					ImportRewriteContext context= new ContextSensitiveImportRewriteContext(declaration, unitRewriter.getImportRewrite());
639 					for (int offset= infos.length - 1; offset >= 0; offset--) {
640 						member= infos[offset].getMember();
641 						adjustor= new MemberVisibilityAdjustor(type, member);
642 						if (infos[offset].isNewMethodToBeDeclaredAbstract())
643 							adjustor.setIncoming(false);
644 						adjustor.setRewrite(sourceRewriter.getASTRewrite(), sourceRewriter.getRoot());
645 						adjustor.setRewrites(rewrites);
646 
647 						// TW: set to error if bug 78387 is fixed
648 						adjustor.setFailureSeverity(RefactoringStatus.WARNING);
649 
650 						adjustor.setStatus(status);
651 						adjustor.setAdjustments(adjustments);
652 						adjustor.adjustVisibility(new SubProgressMonitor(monitor, 1));
653 						adjustments.remove(member);
654 						adjustors.add(adjustor);
655 						status.merge(checkProjectCompliance(getCompilationUnitRewrite(rewrites, getDeclaringType().getCompilationUnit()), type, new IMember[] {infos[offset].getMember()}));
656 						if (infos[offset].isFieldInfo()) {
657 							final VariableDeclarationFragment oldField= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField) infos[offset].getMember(), sourceRewriter.getRoot());
658 							if (oldField != null) {
659 								FieldDeclaration newField= createNewFieldDeclarationNode(infos[offset], sourceRewriter.getRoot(), mapping, unitRewriter.getASTRewrite(), oldField);
660 								unitRewriter.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newField, BodyDeclarationRewrite.getInsertionIndex(newField, declaration.bodyDeclarations()), unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PUSH_DOWN));
661 								ImportRewriteUtil.addImports(unitRewriter, context, oldField.getParent(), new HashMap<Name, String>(), new HashMap<Name, String>(), false);
662 							}
663 						} else {
664 							final MethodDeclaration oldMethod= ASTNodeSearchUtil.getMethodDeclarationNode((IMethod) infos[offset].getMember(), sourceRewriter.getRoot());
665 							if (oldMethod != null) {
666 								MethodDeclaration newMethod= createNewMethodDeclarationNode(infos[offset], mapping, unitRewriter, oldMethod);
667 								unitRewriter.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newMethod, BodyDeclarationRewrite.getInsertionIndex(newMethod, declaration.bodyDeclarations()), unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PUSH_DOWN));
668 								ImportRewriteUtil.addImports(unitRewriter, context, oldMethod, new HashMap<Name, String>(), new HashMap<Name, String>(), false);
669 							}
670 						}
671 					}
672 				}
673 			}
674 		} finally {
675 			monitor.done();
676 		}
677 	}
678 
679 	@Override
createChange(IProgressMonitor pm)680 	public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
681 		try {
682 			final Map<String, String> arguments= new HashMap<>();
683 			String project= null;
684 			final IType declaring= getDeclaringType();
685 			final IJavaProject javaProject= declaring.getJavaProject();
686 			if (javaProject != null)
687 				project= javaProject.getElementName();
688 			int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
689 			try {
690 				if (declaring.isLocal() || declaring.isAnonymous())
691 					flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
692 			} catch (JavaModelException exception) {
693 				JavaPlugin.log(exception);
694 			}
695 			final String description= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description_short_multi, BasicElementLabels.getJavaElementName(fMembersToMove[0].getElementName())) : RefactoringCoreMessages.PushDownRefactoring_descriptor_description_short;
696 			final String header= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description_full, new String[] { JavaElementLabels.getElementLabel(fMembersToMove[0], JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED) }) : Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description, new String[] { JavaElementLabels.getElementLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED) });
697 			final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
698 			final String[] settings= new String[fMembersToMove.length];
699 			for (int index= 0; index < settings.length; index++)
700 				settings[index]= JavaElementLabels.getElementLabel(fMembersToMove[index], JavaElementLabels.ALL_FULLY_QUALIFIED);
701 			comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.PushDownRefactoring_pushed_members_pattern, settings));
702 			addSuperTypeSettings(comment, true);
703 			final PushDownDescriptor descriptor= RefactoringSignatureDescriptorFactory.createPushDownDescriptor(project, description, comment.asString(), arguments, flags);
704 			if (fCachedDeclaringType != null)
705 				arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fCachedDeclaringType));
706 			for (int index= 0; index < fMembersToMove.length; index++) {
707 				arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (index + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, fMembersToMove[index]));
708 				for (MemberActionInfo memberInfo : fMemberInfos) {
709 					if (memberInfo.getMember().equals(fMembersToMove[index])) {
710 						switch (memberInfo.getAction()) {
711 							case MemberActionInfo.PUSH_ABSTRACT_ACTION:
712 								arguments.put(ATTRIBUTE_ABSTRACT + (index + 1), Boolean.TRUE.toString());
713 								break;
714 							case MemberActionInfo.PUSH_DOWN_ACTION:
715 								arguments.put(ATTRIBUTE_PUSH + (index + 1), Boolean.TRUE.toString());
716 								break;
717 						}
718 					}
719 				}
720 			}
721 			return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.PushDownRefactoring_change_name, fChangeManager.getAllChanges());
722 		} finally {
723 			pm.done();
724 			clearCaches();
725 		}
726 	}
727 
createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status)728 	private TextEditBasedChangeManager createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
729 		Assert.isNotNull(monitor);
730 		Assert.isNotNull(status);
731 		try {
732 			monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 7);
733 			final ICompilationUnit source= getDeclaringType().getCompilationUnit();
734 			final CompilationUnitRewrite sourceRewriter= new CompilationUnitRewrite(source);
735 			final Map<ICompilationUnit, CompilationUnitRewrite> rewrites= new HashMap<>(2);
736 			rewrites.put(source, sourceRewriter);
737 			IType[] types= getHierarchyOfDeclaringClass(new SubProgressMonitor(monitor, 1)).getSubclasses(getDeclaringType());
738 			final Set<ICompilationUnit> result= new HashSet<>(types.length + 1);
739 			for (IType type : types) {
740 				ICompilationUnit cu= type.getCompilationUnit();
741 				if (cu != null) { // subclasses can be in binaries
742 					result.add(cu);
743 				}
744 			}
745 			result.add(source);
746 			final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments= new HashMap<>();
747 			final List<MemberVisibilityAdjustor> adjustors= new ArrayList<>();
748 			CompilationUnitRewrite rewrite= null;
749 			final IProgressMonitor sub= new SubProgressMonitor(monitor, 4);
750 			try {
751 				sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, result.size() * 4);
752 				for (ICompilationUnit unit : result) {
753 					rewrite= getCompilationUnitRewrite(rewrites, unit);
754 					if (unit.equals(sourceRewriter.getCu())) {
755 						final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(getDeclaringType(), rewrite.getRoot());
756 						if (!JdtFlags.isAbstract(getDeclaringType()) && getAbstractDeclarationInfos().length != 0)
757 							ModifierRewrite.create(rewrite.getASTRewrite(), declaration).setModifiers((Modifier.ABSTRACT | declaration.getModifiers()), rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.PushDownRefactoring_make_abstract, SET_PUSH_DOWN));
758 						deleteDeclarationNodes(sourceRewriter, false, rewrite, Arrays.asList(getDeletableMembers()), SET_PUSH_DOWN);
759 						for (MemberActionInfo method : getAbstractDeclarationInfos()) {
760 							declareMethodAbstract(method, sourceRewriter, rewrite);
761 						}
762 					}
763 					final IMember[] members= getAbstractMembers(getAbstractDestinations(new SubProgressMonitor(monitor, 1)));
764 					final IType[] classes= new IType[members.length];
765 					for (int offset= 0; offset < members.length; offset++)
766 						classes[offset]= (IType) members[offset];
767 					copyMembers(adjustors, adjustments, rewrites, status, getAbstractMemberInfos(), classes, sourceRewriter, rewrite, sub);
768 					copyMembers(adjustors, adjustments, rewrites, status, getAffectedMemberInfos(), getAbstractDestinations(new SubProgressMonitor(monitor, 1)), sourceRewriter, rewrite, sub);
769 					if (monitor.isCanceled())
770 						throw new OperationCanceledException();
771 				}
772 			} finally {
773 				sub.done();
774 			}
775 			if (!adjustors.isEmpty() && !adjustments.isEmpty()) {
776 				final MemberVisibilityAdjustor adjustor= adjustors.get(0);
777 				adjustor.rewriteVisibility(new SubProgressMonitor(monitor, 1));
778 			}
779 			final TextEditBasedChangeManager manager= new TextEditBasedChangeManager();
780 			for (Map.Entry<ICompilationUnit, CompilationUnitRewrite> entry : rewrites.entrySet()) {
781 				ICompilationUnit unit= entry.getKey();
782 				rewrite= entry.getValue();
783 				if (rewrite != null)
784 					manager.manage(unit, rewrite.createChange(true));
785 			}
786 			return manager;
787 		} finally {
788 			monitor.done();
789 		}
790 	}
791 
createNewFieldDeclarationNode(MemberActionInfo info, CompilationUnit declaringCuNode, TypeVariableMaplet[] mapping, ASTRewrite rewrite, VariableDeclarationFragment oldFieldFragment)792 	private FieldDeclaration createNewFieldDeclarationNode(MemberActionInfo info, CompilationUnit declaringCuNode, TypeVariableMaplet[] mapping, ASTRewrite rewrite, VariableDeclarationFragment oldFieldFragment) throws JavaModelException {
793 		Assert.isTrue(info.isFieldInfo());
794 		IField field= (IField) info.getMember();
795 		AST ast= rewrite.getAST();
796 		VariableDeclarationFragment newFragment= ast.newVariableDeclarationFragment();
797 		copyExtraDimensions(oldFieldFragment, newFragment);
798 		Expression initializer= oldFieldFragment.getInitializer();
799 		if (initializer != null) {
800 			Expression newInitializer= null;
801 			if (mapping.length > 0)
802 				newInitializer= createPlaceholderForExpression(initializer, field.getCompilationUnit(), mapping, rewrite);
803 			else
804 				newInitializer= createPlaceholderForExpression(initializer, field.getCompilationUnit(), rewrite);
805 			newFragment.setInitializer(newInitializer);
806 		}
807 		newFragment.setName(ast.newSimpleName(oldFieldFragment.getName().getIdentifier()));
808 		FieldDeclaration newField= ast.newFieldDeclaration(newFragment);
809 		FieldDeclaration oldField= ASTNodeSearchUtil.getFieldDeclarationNode(field, declaringCuNode);
810 		if (info.copyJavadocToCopiesInSubclasses())
811 			copyJavadocNode(rewrite, oldField, newField);
812 		copyAnnotations(oldField, newField);
813 		newField.modifiers().addAll(ASTNodeFactory.newModifiers(ast, info.getNewModifiersForCopyInSubclass(oldField.getModifiers())));
814 		Type oldType= oldField.getType();
815 		ICompilationUnit cu= field.getCompilationUnit();
816 		Type newType= null;
817 		if (mapping.length > 0) {
818 			newType= createPlaceholderForType(oldType, cu, mapping, rewrite);
819 		} else
820 			newType= createPlaceholderForType(oldType, cu, rewrite);
821 		newField.setType(newType);
822 		return newField;
823 	}
824 
createNewMethodDeclarationNode(MemberActionInfo info, TypeVariableMaplet[] mapping, CompilationUnitRewrite rewriter, MethodDeclaration oldMethod)825 	private MethodDeclaration createNewMethodDeclarationNode(MemberActionInfo info, TypeVariableMaplet[] mapping, CompilationUnitRewrite rewriter, MethodDeclaration oldMethod) throws JavaModelException {
826 		Assert.isTrue(!info.isFieldInfo());
827 		IMethod method= (IMethod) info.getMember();
828 		ASTRewrite rewrite= rewriter.getASTRewrite();
829 		AST ast= rewrite.getAST();
830 		MethodDeclaration newMethod= ast.newMethodDeclaration();
831 		copyBodyOfPushedDownMethod(rewrite, method, oldMethod, newMethod, mapping);
832 		newMethod.setConstructor(oldMethod.isConstructor());
833 		copyExtraDimensions(oldMethod, newMethod);
834 		if (info.copyJavadocToCopiesInSubclasses())
835 			copyJavadocNode(rewrite, oldMethod, newMethod);
836 		final IJavaProject project= rewriter.getCu().getJavaProject();
837 		if (info.isNewMethodToBeDeclaredAbstract() && JavaModelUtil.is50OrHigher(project) && JavaPreferencesSettings.getCodeGenerationSettings(project).overrideAnnotation) {
838 			final MarkerAnnotation annotation= ast.newMarkerAnnotation();
839 			annotation.setTypeName(ast.newSimpleName("Override")); //$NON-NLS-1$
840 			newMethod.modifiers().add(annotation);
841 		}
842 		copyAnnotations(oldMethod, newMethod);
843 		newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, info.getNewModifiersForCopyInSubclass(oldMethod.getModifiers())));
844 		newMethod.setName(ast.newSimpleName(oldMethod.getName().getIdentifier()));
845 		copyReturnType(rewrite, method.getCompilationUnit(), oldMethod, newMethod, mapping);
846 		copyParameters(rewrite, method.getCompilationUnit(), oldMethod, newMethod, mapping);
847 		copyThrownExceptions(oldMethod, newMethod);
848 		copyTypeParameters(oldMethod, newMethod);
849 		return newMethod;
850 	}
851 
declareMethodAbstract(MemberActionInfo info, CompilationUnitRewrite sourceRewrite, CompilationUnitRewrite unitRewrite)852 	private void declareMethodAbstract(MemberActionInfo info, CompilationUnitRewrite sourceRewrite, CompilationUnitRewrite unitRewrite) throws JavaModelException {
853 		Assert.isTrue(!info.isFieldInfo());
854 		IMethod method= (IMethod) info.getMember();
855 		if (JdtFlags.isAbstract(method))
856 			return;
857 		final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(method, sourceRewrite.getRoot());
858 		unitRewrite.getASTRewrite().remove(declaration.getBody(), null);
859 		sourceRewrite.getImportRemover().registerRemovedNode(declaration.getBody());
860 		ModifierRewrite.create(unitRewrite.getASTRewrite(), declaration).setModifiers(info.getNewModifiersForOriginal(declaration.getModifiers()), null);
861 	}
862 
getAbstractDeclarationInfos()863 	private MemberActionInfo[] getAbstractDeclarationInfos() throws JavaModelException {
864 		List<MemberActionInfo> result= new ArrayList<>(fMemberInfos.length);
865 		for (MemberActionInfo info : fMemberInfos) {
866 			if (info.isNewMethodToBeDeclaredAbstract())
867 				result.add(info);
868 		}
869 		return result.toArray(new MemberActionInfo[result.size()]);
870 	}
871 
getAbstractDestinations(IProgressMonitor monitor)872 	private IType[] getAbstractDestinations(IProgressMonitor monitor) throws JavaModelException {
873 		IType[] allDirectSubclasses= getHierarchyOfDeclaringClass(monitor).getSubclasses(getDeclaringType());
874 		List<IType> result= new ArrayList<>(allDirectSubclasses.length);
875 		for (IType subclass : allDirectSubclasses) {
876 			if (subclass.exists() && !subclass.isBinary() && !subclass.isReadOnly() && subclass.getCompilationUnit() != null && subclass.isStructureKnown())
877 				result.add(subclass);
878 		}
879 		return result.toArray(new IType[result.size()]);
880 	}
881 
getAbstractMemberInfos()882 	private MemberActionInfo[] getAbstractMemberInfos() throws JavaModelException {
883 		List<MemberActionInfo> result= new ArrayList<>(fMemberInfos.length);
884 		for (MemberActionInfo info : fMemberInfos) {
885 			if (info.isToBeCreatedInSubclassesOfDeclaringClass() && JdtFlags.isAbstract(info.getMember()))
886 				result.add(info);
887 		}
888 		return result.toArray(new MemberActionInfo[result.size()]);
889 	}
890 
getAdditionalRequiredMembers(IProgressMonitor monitor)891 	public IMember[] getAdditionalRequiredMembers(IProgressMonitor monitor) throws JavaModelException {
892 		IMember[] members= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass());
893 		monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, members.length);// not
894 		// true,
895 		// but
896 		// not
897 		// easy
898 		// to
899 		// give
900 		// anything
901 		// better
902 		List<IMember> queue= new ArrayList<>(members.length);
903 		queue.addAll(Arrays.asList(members));
904 		if (queue.isEmpty())
905 			return new IMember[0];
906 		int i= 0;
907 		IMember current;
908 		do {
909 			current= queue.get(i);
910 			addAllRequiredPushableMembers(queue, current, new SubProgressMonitor(monitor, 1));
911 			i++;
912 			if (queue.size() == i)
913 				current= null;
914 		} while (current != null);
915 		queue.removeAll(Arrays.asList(members));// report only additional
916 		return queue.toArray(new IMember[queue.size()]);
917 	}
918 
getDeletableMembers()919 	private IMember[] getDeletableMembers() {
920 		List<IMember> result= new ArrayList<>(fMemberInfos.length);
921 		for (MemberActionInfo info : fMemberInfos) {
922 			if (info.isToBeDeletedFromDeclaringClass())
923 				result.add(info.getMember());
924 		}
925 		return result.toArray(new IMember[result.size()]);
926 	}
927 
getAffectedMemberInfos()928 	private MemberActionInfo[] getAffectedMemberInfos() throws JavaModelException {
929 		List<MemberActionInfo> result= new ArrayList<>(fMemberInfos.length);
930 		for (MemberActionInfo info : fMemberInfos) {
931 			if (info.isToBeCreatedInSubclassesOfDeclaringClass() && !JdtFlags.isAbstract(info.getMember()))
932 				result.add(info);
933 		}
934 		return result.toArray(new MemberActionInfo[result.size()]);
935 	}
936 
937 	@Override
getElements()938 	public Object[] getElements() {
939 		return fMembersToMove;
940 	}
941 
getHierarchyOfDeclaringClass(IProgressMonitor monitor)942 	private ITypeHierarchy getHierarchyOfDeclaringClass(IProgressMonitor monitor) throws JavaModelException {
943 		try {
944 			if (fCachedClassHierarchy != null)
945 				return fCachedClassHierarchy;
946 			fCachedClassHierarchy= getDeclaringType().newTypeHierarchy(monitor);
947 			return fCachedClassHierarchy;
948 		} finally {
949 			monitor.done();
950 		}
951 	}
952 
953 	@Override
getIdentifier()954 	public String getIdentifier() {
955 		return IDENTIFIER;
956 	}
957 
getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()958 	private MemberActionInfo[] getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass() throws JavaModelException {
959 		MemberActionInfo[] abs= getAbstractMemberInfos();
960 		MemberActionInfo[] nonabs= getAffectedMemberInfos();
961 		List<MemberActionInfo> result= new ArrayList<>(abs.length + nonabs.length);
962 		result.addAll(Arrays.asList(abs));
963 		result.addAll(Arrays.asList(nonabs));
964 		return result.toArray(new MemberActionInfo[result.size()]);
965 	}
966 
getMemberActionInfos()967 	public MemberActionInfo[] getMemberActionInfos() {
968 		return fMemberInfos;
969 	}
970 
971 	@Override
getProcessorName()972 	public String getProcessorName() {
973 		return RefactoringCoreMessages.PushDownRefactoring_name;
974 	}
975 
initialize(JavaRefactoringArguments extended)976 	private RefactoringStatus initialize(JavaRefactoringArguments extended) {
977 		String handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
978 		if (handle != null) {
979 			final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
980 			if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
981 				return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.PUSH_DOWN);
982 			else
983 				fCachedDeclaringType= (IType) element;
984 		}
985 		int count= 1;
986 		final List<IJavaElement> elements= new ArrayList<>();
987 		final List<MemberActionInfo> infos= new ArrayList<>();
988 		String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count;
989 		final RefactoringStatus status= new RefactoringStatus();
990 		while ((handle= extended.getAttribute(attribute)) != null) {
991 			final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
992 			if (element == null || !element.exists())
993 				status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(element, getProcessorName(), IJavaRefactorings.PUSH_DOWN));
994 			else
995 				elements.add(element);
996 			if (extended.getAttribute(ATTRIBUTE_ABSTRACT + count) != null)
997 				infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.PUSH_ABSTRACT_ACTION));
998 			else if (extended.getAttribute(ATTRIBUTE_PUSH + count) != null)
999 				infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.PUSH_DOWN_ACTION));
1000 			else
1001 				infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.NO_ACTION));
1002 			count++;
1003 			attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + count;
1004 		}
1005 		fMembersToMove= elements.toArray(new IMember[elements.size()]);
1006 		fMemberInfos= infos.toArray(new MemberActionInfo[infos.size()]);
1007 		if (!status.isOK())
1008 			return status;
1009 		return new RefactoringStatus();
1010 	}
1011 
1012 	@Override
isApplicable()1013 	public boolean isApplicable() throws CoreException {
1014 		return RefactoringAvailabilityTester.isPushDownAvailable(fMembersToMove);
1015 	}
1016 
1017 	@Override
rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set<String> replacements, final IProgressMonitor monitor)1018 	protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set<String> replacements, final IProgressMonitor monitor) throws CoreException {
1019 		// Not needed
1020 	}
1021 }