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 }