| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.refactoring.structure; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IInitializer; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.FieldDeclaration; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.ISearchPattern; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| |
| import org.eclipse.jdt.internal.corext.Assert; |
| import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; |
| import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.ASTRewrite; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.CompositeChange; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; |
| import org.eclipse.jdt.internal.corext.refactoring.SearchResult; |
| import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; |
| import org.eclipse.jdt.internal.corext.refactoring.base.Context; |
| import org.eclipse.jdt.internal.corext.refactoring.base.IChange; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaSourceContext; |
| import org.eclipse.jdt.internal.corext.refactoring.base.Refactoring; |
| import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatus; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.TextChange; |
| import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks; |
| import org.eclipse.jdt.internal.corext.refactoring.reorg.SourceReferenceUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; |
| import org.eclipse.jdt.internal.corext.textmanipulation.MultiTextEdit; |
| import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer; |
| import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Strings; |
| import org.eclipse.jdt.internal.corext.util.WorkingCopyUtil; |
| |
| public class PushDownRefactoring extends Refactoring { |
| |
| public static class MemberActionInfo implements IMemberActionInfo{ |
| |
| private final IMember fMember; |
| private int fAction; |
| public static final int PUSH_DOWN_ACTION= 0; |
| public static final int PUSH_ABSTRACT_ACTION= 1; |
| public static final int NO_ACTION= 2; |
| private MemberActionInfo(IMember member, int action){ |
| assertValidAction(member, action); |
| Assert.isTrue(member instanceof IField || member instanceof IMethod); |
| fMember= member; |
| fAction= action; |
| } |
| |
| public void setAction(int action){ |
| assertValidAction(fMember, action); |
| if (isFieldInfo()) |
| Assert.isTrue(action != PUSH_ABSTRACT_ACTION); |
| fAction= action; |
| } |
| |
| public int[] getAvailableActions(){ |
| if (isFieldInfo()) |
| return new int[]{PUSH_DOWN_ACTION, NO_ACTION}; |
| else |
| return new int[]{PUSH_DOWN_ACTION, PUSH_ABSTRACT_ACTION, NO_ACTION}; |
| } |
| |
| private static void assertValidAction(IMember member, int action) { |
| if (member instanceof IMethod) |
| Assert.isTrue( action == PUSH_ABSTRACT_ACTION || |
| action == NO_ACTION || |
| action == PUSH_DOWN_ACTION); |
| else if (member instanceof IField) |
| Assert.isTrue( action == NO_ACTION || |
| action == PUSH_DOWN_ACTION); |
| } |
| |
| public static MemberActionInfo create(IMember member) { |
| return new MemberActionInfo(member, NO_ACTION); |
| } |
| |
| public boolean isToBePushedDown() { |
| return fAction == PUSH_DOWN_ACTION; |
| } |
| |
| boolean isToBeDeletedFromDeclaringClass() { |
| return isToBePushedDown(); |
| } |
| |
| boolean isNewMethodToBeDeclaredAbstract() throws JavaModelException { |
| return |
| ! isFieldInfo() && |
| ! JdtFlags.isAbstract(fMember) && |
| fAction == PUSH_ABSTRACT_ACTION; |
| } |
| |
| boolean isToBeCreatedInSubclassesOfDeclaringClass() { |
| return fAction != NO_ACTION; |
| } |
| |
| public IMember getMember() { |
| return fMember; |
| } |
| |
| public int getAction(){ |
| return fAction; |
| } |
| |
| boolean isFieldInfo() { |
| return fMember instanceof IField; |
| } |
| |
| int getNewModifiersForCopyInSubclass(int oldModifiers) throws JavaModelException { |
| if (isFieldInfo()) |
| return oldModifiers; |
| if (isToBeDeletedFromDeclaringClass()) |
| return oldModifiers; |
| int modifiers= oldModifiers; |
| if (isNewMethodToBeDeclaredAbstract()){ |
| if (! JdtFlags.isPublic(fMember)) |
| modifiers= Modifier.PROTECTED | ASTNodes.clearAccessModifiers(modifiers); |
| } |
| return modifiers; |
| } |
| |
| int getNewModifiersForOriginal(int oldModifiers) throws JavaModelException{ |
| if (isFieldInfo()) |
| return oldModifiers; |
| if (isToBeDeletedFromDeclaringClass()) |
| return oldModifiers; |
| int modifiers= oldModifiers; |
| if (isNewMethodToBeDeclaredAbstract()){ |
| modifiers= ASTNodes.clearFlag(Modifier.FINAL, ASTNodes.clearFlag(Modifier.NATIVE, oldModifiers)); |
| modifiers |= Modifier.ABSTRACT; |
| |
| if (! JdtFlags.isPublic(fMember)) |
| modifiers= Modifier.PROTECTED | ASTNodes.clearAccessModifiers(modifiers); |
| } |
| return modifiers; |
| } |
| |
| //XXX incorrect to have a method like this in the model |
| public boolean isEditable(){ |
| if (isFieldInfo()) |
| return false; |
| if (getAction() == MemberActionInfo.NO_ACTION) |
| return false; |
| return true; |
| } |
| |
| boolean copyJavadocToCopiesInSubclasses() { |
| return isToBeDeletedFromDeclaringClass(); |
| } |
| |
| static IMember[] getMembers(MemberActionInfo[] infos){ |
| IMember[] result= new IMember[infos.length]; |
| for (int i= 0; i < result.length; i++) { |
| result[i]= infos[i].getMember(); |
| } |
| return result; |
| } |
| |
| public boolean isActive() { |
| return getAction() != NO_ACTION; |
| } |
| |
| } |
| |
| private MemberActionInfo[] fMemberInfos; |
| private IMember[] fSelectedMembers; |
| private TextChangeManager fChangeManager; |
| |
| private final ASTNodeMappingManager fAstManager; |
| private final ASTRewriteManager fRewriteManager; |
| private final ImportEditManager fImportEditManager; |
| private final CodeGenerationSettings fPreferenceSettings; |
| |
| //caches |
| private IType fCachedDeclaringClass; |
| private ITypeHierarchy fCachedClassHierarchy; |
| |
| private IType[] fTypesReferencedInPushedDownMembers; |
| public PushDownRefactoring(IMember[] members, CodeGenerationSettings preferenceSettings){ |
| Assert.isTrue(members.length > 0); |
| Assert.isNotNull(preferenceSettings); |
| fSelectedMembers= (IMember[])SourceReferenceUtil.sortByOffset(members); |
| fPreferenceSettings= preferenceSettings; |
| fAstManager= new ASTNodeMappingManager(); |
| fRewriteManager= new ASTRewriteManager(fAstManager); |
| fImportEditManager= new ImportEditManager(preferenceSettings); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName() |
| */ |
| public String getName() { |
| return RefactoringCoreMessages.getString("PushDownRefactoring.name"); //$NON-NLS-1$ |
| } |
| |
| public IType getDeclaringClass(){ |
| if (fCachedDeclaringClass != null) |
| return fCachedDeclaringClass; |
| //all members declared in same type - checked in precondition |
| fCachedDeclaringClass= (IType)WorkingCopyUtil.getOriginal(fSelectedMembers[0].getDeclaringType()); //index safe - checked in constructor |
| return fCachedDeclaringClass; |
| } |
| |
| |
| /* non java-doc |
| * @see Refactoring#checkPreconditions(IProgressMonitor) |
| */ |
| public RefactoringStatus checkPreconditions(IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= checkPreactivation(); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(super.checkPreconditions(pm)); |
| return result; |
| } |
| |
| public RefactoringStatus checkPreactivation() throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| result.merge(checkAllSelectedMembers()); |
| if (result.hasFatalError()) |
| return result; |
| |
| if (! haveCommonDeclaringType()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.no_common_type")); //$NON-NLS-1$ |
| |
| return new RefactoringStatus(); |
| } |
| |
| private boolean haveCommonDeclaringType() { |
| IType declaringType= fSelectedMembers[0].getDeclaringType(); //index safe - checked in constructor |
| if (declaringType == null) |
| return false; |
| for (int i= 0; i < fSelectedMembers.length; i++) { |
| if (! declaringType.equals(fSelectedMembers[i].getDeclaringType())) |
| return false; |
| } |
| return true; |
| } |
| |
| private RefactoringStatus checkAllSelectedMembers() throws JavaModelException { |
| for (int i = 0; i < fSelectedMembers.length; i++) { |
| RefactoringStatus status= checkElement(fSelectedMembers[i]); |
| if (! status.isOK()) |
| return status; |
| } |
| return new RefactoringStatus(); |
| } |
| |
| private static RefactoringStatus checkElement(IMember member) throws JavaModelException{ |
| if (member.getElementType() != IJavaElement.METHOD && |
| member.getElementType() != IJavaElement.FIELD) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.only_fields_and_methods")); //$NON-NLS-1$ |
| if (! member.exists()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.not_exists")); //$NON-NLS-1$ |
| |
| if (member.isBinary()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.binary")); //$NON-NLS-1$ |
| |
| if (member.isReadOnly()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.read_only")); //$NON-NLS-1$ |
| |
| if (! member.isStructureKnown()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.unknown_structure")); //$NON-NLS-1$ |
| |
| if (JdtFlags.isStatic(member)) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.static")); //$NON-NLS-1$ |
| |
| if (member.getElementType() == IJavaElement.METHOD){ |
| IMethod method= (IMethod) member; |
| if (method.isConstructor()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.constructors")); //$NON-NLS-1$ |
| |
| if (JdtFlags.isNative(method)) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.native")); //$NON-NLS-1$ |
| } |
| return new RefactoringStatus(); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public RefactoringStatus checkActivation(IProgressMonitor pm) throws JavaModelException { |
| try { |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| fSelectedMembers= WorkingCopyUtil.getOriginals(fSelectedMembers); |
| |
| result.merge(checkPossibleSubclasses(new SubProgressMonitor(pm, 1))); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(checkDeclaringType()); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(checkIfMembersExist()); |
| if (result.hasFatalError()) |
| return result; |
| |
| fMemberInfos= createInfosForAllPushableFieldsAndMethods(getDeclaringClass()); |
| setInfoAction(MemberActionInfo.PUSH_DOWN_ACTION, fSelectedMembers); |
| return result; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private void setInfoAction(int action, IMember[] members) { |
| List list= Arrays.asList(members); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (list.contains(info.getMember())) |
| info.setAction(action); |
| } |
| } |
| |
| private RefactoringStatus checkPossibleSubclasses(IProgressMonitor pm) throws JavaModelException { |
| IType[] modifiableSubclasses= getDestinationClassesForNonAbstractMembers(pm); |
| if (modifiableSubclasses.length == 0){ |
| String msg= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.no_subclasses", new String[]{createTypeLabel(getDeclaringClass())});//$NON-NLS-1$ |
| return RefactoringStatus.createFatalErrorStatus(msg); |
| } |
| return new RefactoringStatus(); |
| } |
| |
| private RefactoringStatus checkIfMembersExist() { |
| for (int i= 0; i < fSelectedMembers.length; i++) { |
| IMember orig= fSelectedMembers[i]; |
| if (orig == null || ! orig.exists()){ |
| String message= RefactoringCoreMessages.getString("PushDownRefactoring.not_in_saved"); //$NON-NLS-1$ |
| return RefactoringStatus.createFatalErrorStatus(message); |
| } |
| } |
| return new RefactoringStatus(); |
| } |
| |
| private RefactoringStatus checkDeclaringType() throws JavaModelException { |
| IType declaringType= getDeclaringClass(); |
| |
| if (declaringType.isInterface()) //for now |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.interface_members")); //$NON-NLS-1$ |
| |
| if (declaringType.isBinary()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.members_of_binary")); //$NON-NLS-1$ |
| |
| if (declaringType.isReadOnly()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PushDownRefactoring.members_of_read-only")); //$NON-NLS-1$ |
| |
| return new RefactoringStatus(); |
| } |
| |
| private static MemberActionInfo[] createInfosForAllPushableFieldsAndMethods(IType type) throws JavaModelException { |
| IMethod[] methods= type.getMethods(); |
| IField[] fields= type.getFields(); |
| List result= new ArrayList(methods.length + fields.length); |
| for (int i= 0; i < methods.length; i++) { |
| if (isPushable(methods[i])) |
| result.add(MemberActionInfo.create(methods[i])); |
| } |
| for (int i= 0; i < fields.length; i++) { |
| if (isPushable(fields[i])) |
| result.add(MemberActionInfo.create(fields[i])); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| private static boolean isPushable(IMember member) throws JavaModelException { |
| return checkElement(member).isOK(); |
| } |
| |
| public MemberActionInfo[] getMemberActionInfos(){ |
| return fMemberInfos; |
| } |
| |
| public void computeAdditionalRequiredMembersToPushDown(IProgressMonitor pm) throws JavaModelException { |
| IMember[] additional= getAdditionalRequiredMembers(pm); |
| setInfoAction(MemberActionInfo.PUSH_DOWN_ACTION, additional); |
| } |
| |
| public IMember[] getAdditionalRequiredMembers(IProgressMonitor pm) throws JavaModelException { |
| IMember[] members= getMembersToBeCreatedInSubclasses(); |
| pm.beginTask(RefactoringCoreMessages.getString("PushDownRefactoring.calculating"), members.length);//not true, but not easy to give anything better //$NON-NLS-1$ |
| List queue= new ArrayList(members.length); |
| queue.addAll(Arrays.asList(members)); |
| if (queue.isEmpty()) |
| return new IMember[0]; |
| int i= 0; |
| IMember current; |
| do{ |
| current= (IMember)queue.get(i); |
| addAllRequiredPushableMembers(queue, current, new SubProgressMonitor(pm, 1)); |
| i++; |
| if (queue.size() == i) |
| current= null; |
| } while(current != null); |
| queue.removeAll(Arrays.asList(members));//report only additional |
| return (IMember[]) queue.toArray(new IMember[queue.size()]); |
| } |
| |
| private void addAllRequiredPushableMembers(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| addAllRequiredPushableMethods(queue, member, new SubProgressMonitor(pm, 1)); |
| addAllRequiredPushableFields(queue, member, new SubProgressMonitor(pm, 1)); |
| pm.done(); |
| } |
| |
| private void addAllRequiredPushableFields(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| IField[] requiredFields= ReferenceFinderUtil.getFieldsReferencedIn(new IJavaElement[]{member}, pm); |
| for (int i= 0; i < requiredFields.length; i++) { |
| IField field= requiredFields[i]; |
| if (isRequiredPushableMember(queue, field)) |
| queue.add(field); |
| } |
| } |
| |
| private void addAllRequiredPushableMethods(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| IMethod[] requiredMethods= ReferenceFinderUtil.getMethodsReferencedIn(new IJavaElement[]{member}, new SubProgressMonitor(pm, 1)); |
| SubProgressMonitor sPm= new SubProgressMonitor(pm, 1); |
| sPm.beginTask("", requiredMethods.length); //$NON-NLS-1$ |
| for (int i= 0; i < requiredMethods.length; i++) { |
| IMethod method= requiredMethods[i]; |
| if (! MethodChecks.isVirtual(method) && isRequiredPushableMember(queue, method)) |
| queue.add(method); |
| } |
| sPm.done(); |
| } |
| |
| private boolean isRequiredPushableMember(List queue, IMember member) throws JavaModelException { |
| return member.getDeclaringType().equals(getDeclaringClass()) && ! queue.contains(member) && isPushable(member); |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public RefactoringStatus checkInput(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| pm.beginTask(RefactoringCoreMessages.getString("PushDownRefactoring.creating_preview"), 5); //$NON-NLS-1$ |
| clearManagers(); |
| |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| result.merge(checkMembersInDestinationClasses(new SubProgressMonitor(pm, 1))); |
| result.merge(checkElementsAccessedByModifiedMembers(new SubProgressMonitor(pm, 1))); |
| result.merge(checkReferencesToPushedDownMembers(new SubProgressMonitor(pm, 1))); |
| if (shouldMakeDeclaringClassAbstract()) |
| result.merge(checkCallsToDeclaringClassConstructors(new SubProgressMonitor(pm, 1))); |
| else |
| pm.worked(1); |
| |
| if (result.hasFatalError()) |
| return result; |
| |
| fChangeManager= createChangeManager(new SubProgressMonitor(pm, 1)); |
| result.merge(validateModifiesFiles()); |
| return result; |
| } catch (JavaModelException e){ |
| throw e; |
| } catch (CoreException e){ |
| throw new JavaModelException(e); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private void clearManagers() { |
| fAstManager.clear(); |
| fImportEditManager.clear(); |
| fRewriteManager.clear(); |
| } |
| |
| private RefactoringStatus checkReferencesToPushedDownMembers(IProgressMonitor pm) throws JavaModelException { |
| IMember[] membersToPush= getMembersToPushDown(); |
| RefactoringStatus result= new RefactoringStatus(); |
| List movedMembers= Arrays.asList(getMembersToBeCreatedInSubclasses()); |
| pm.beginTask("", membersToPush.length); //$NON-NLS-1$ |
| for (int i= 0; i < membersToPush.length; i++) { |
| IMember member= membersToPush[i]; |
| String label= createLabel(member); |
| IJavaElement[] referencing= getReferencingElementsFromSameClass(member, new SubProgressMonitor(pm, 1)); |
| for (int j= 0; j < referencing.length; j++) { |
| IJavaElement element= referencing[j]; |
| if (movedMembers.contains(element)) |
| continue; |
| if (! (element instanceof IMember)) |
| continue; |
| IMember referencingMember= (IMember)element; |
| Object[] keys= {label, createLabel(referencingMember)}; |
| String msg= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.referenced", keys); //$NON-NLS-1$ |
| result.addError(msg, JavaSourceContext.create(referencingMember)); |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private static IJavaElement[] getReferencingElementsFromSameClass(IMember member, IProgressMonitor pm) throws JavaModelException { |
| ISearchPattern pattern= RefactoringSearchEngine.createSearchPattern(new IJavaElement[]{member}, IJavaSearchConstants.REFERENCES); |
| IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[]{member.getDeclaringType()}); |
| SearchResultGroup[] groups= RefactoringSearchEngine.search(pm, scope, pattern); |
| Set result= new HashSet(3); |
| for (int i= 0; i < groups.length; i++) { |
| SearchResultGroup group= groups[i]; |
| SearchResult[] results= group.getSearchResults(); |
| for (int j= 0; j < results.length; j++) { |
| SearchResult searchResult= results[i]; |
| result.add(searchResult.getEnclosingElement()); |
| } |
| } |
| return (IJavaElement[]) result.toArray(new IJavaElement[result.size()]); |
| } |
| |
| private RefactoringStatus checkElementsAccessedByModifiedMembers(IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask(RefactoringCoreMessages.getString("PushDownRefactoring.checking"), 3); //$NON-NLS-1$ |
| IType[] subclasses= getDestinationClassesForNonAbstractMembers(new SubProgressMonitor(pm, 1)); |
| result.merge(checkAccessedTypes(subclasses, new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedFields(subclasses, new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedMethods(subclasses, new SubProgressMonitor(pm, 1))); |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedTypes(IType[] subclasses, IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| IType[] accessedTypes= getTypeReferencedInPushedDownMembers(pm); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType targetClass= subclasses[i]; |
| for (int j= 0; j < accessedTypes.length; j++) { |
| IType type= accessedTypes[j]; |
| if (! canBeAccessedFrom(type, targetClass)){ |
| String message= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.type_not_accessible", new String[]{createTypeLabel(type), createTypeLabel(targetClass)}); //$NON-NLS-1$ |
| result.addError(message, JavaSourceContext.create(type)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedFields(IType[] subclasses, IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= getMembersToBeCreatedInSubclasses(); |
| List pushedDownList= Arrays.asList(membersToPushDown); |
| IField[] accessedFields= ReferenceFinderUtil.getFieldsReferencedIn(membersToPushDown, pm); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType targetClass= subclasses[i]; |
| for (int j= 0; j < accessedFields.length; j++) { |
| IField field= accessedFields[j]; |
| boolean isAccessible= pushedDownList.contains(field) || |
| canBeAccessedFrom(field, targetClass); |
| if (! isAccessible){ |
| String message= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.field_not_accessible", new String[]{createFieldLabel(field), createTypeLabel(targetClass)}); //$NON-NLS-1$ |
| result.addError(message, JavaSourceContext.create(field)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedMethods(IType[] subclasses, IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= getMembersToBeCreatedInSubclasses(); |
| List pushedDownList= Arrays.asList(membersToPushDown); |
| IMethod[] accessedMethods= ReferenceFinderUtil.getMethodsReferencedIn(membersToPushDown, pm); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType targetClass= subclasses[i]; |
| for (int j= 0; j < accessedMethods.length; j++) { |
| IMethod method= accessedMethods[j]; |
| boolean isAccessible= canBeAccessedFrom(method, targetClass) || |
| pushedDownList.contains(method); |
| if (! isAccessible){ |
| String message= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.method_not_accessible", new String[]{createMethodLabel(method), createTypeLabel(targetClass)}); //$NON-NLS-1$ |
| result.addError(message, JavaSourceContext.create(method)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private boolean canBeAccessedFrom(IMember member, IType newHome) throws JavaModelException{ |
| Assert.isTrue(!(member instanceof IInitializer)); |
| if (! member.exists()) |
| return false; |
| |
| if (newHome.equals(member.getDeclaringType())) |
| return true; |
| |
| if (newHome.equals(member)) |
| return true; |
| |
| if (JdtFlags.isPrivate(member)) |
| return false; |
| |
| if (member.getDeclaringType() == null){ //top level |
| if (! (member instanceof IType)) |
| return false; |
| |
| if (JdtFlags.isPublic(member)) |
| return true; |
| |
| if (! JdtFlags.isPackageVisible(member)) |
| return false; |
| |
| return ((IType)member).getPackageFragment().equals(newHome.getPackageFragment()); |
| } else { |
| IType enclosingType= member.getDeclaringType(); |
| |
| if (! canBeAccessedFrom(enclosingType, newHome)) |
| return false; |
| |
| boolean samePackage= enclosingType.getPackageFragment().equals(newHome.getPackageFragment()); |
| if (samePackage) |
| return true; //private checked before |
| |
| if (enclosingType.equals(getDeclaringClass())) |
| return JdtFlags.isPublic(member) || JdtFlags.isProtected(member); |
| else |
| return JdtFlags.isPublic(member); |
| } |
| } |
| |
| private IType[] getTypeReferencedInPushedDownMembers(IProgressMonitor pm) throws JavaModelException { |
| if (fTypesReferencedInPushedDownMembers == null) |
| fTypesReferencedInPushedDownMembers= ReferenceFinderUtil.getTypesReferencedIn(getMembersToBeCreatedInSubclasses(), pm); |
| return fTypesReferencedInPushedDownMembers; |
| } |
| |
| private IMember[] getMembersToBeCreatedInSubclasses() { |
| return MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| } |
| |
| private IMember[] getMembersToPushDown() { |
| List fields= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isToBePushedDown()) |
| fields.add(info.getMember()); |
| } |
| return (IMember[]) fields.toArray(new IMember[fields.size()]); |
| } |
| |
| private RefactoringStatus checkCallsToDeclaringClassConstructors(IProgressMonitor pm) throws JavaModelException { |
| ASTNode[] refNodes= ConstructorReferenceFinder.getConstructorReferenceNodes(getDeclaringClass(), fAstManager, pm); |
| RefactoringStatus result= new RefactoringStatus(); |
| String msg= RefactoringCoreMessages.getFormattedString("PushDownRefactoring.gets_instantiated", new Object[]{createTypeLabel(getDeclaringClass())}); //$NON-NLS-1$ |
| for (int i= 0; i < refNodes.length; i++) { |
| ASTNode node= refNodes[i]; |
| if (node instanceof ClassInstanceCreation){ |
| Context context= JavaSourceContext.create(fAstManager.getCompilationUnit(node), node); |
| result.addError(msg, context); |
| } |
| } |
| return result; |
| } |
| |
| private RefactoringStatus checkMembersInDestinationClasses(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= getMembersToBeCreatedInSubclasses(); |
| |
| IType[] destinationClassesForNonAbstract= getDestinationClassesForNonAbstractMembers(new SubProgressMonitor(pm, 1)); |
| result.merge(checkNonAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForNonAbstract)); |
| |
| IType[] destinationClassesForAbstract= getDestinationClassesForAbstractMembers(new SubProgressMonitor(pm, 1)); |
| result.merge(checkAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForAbstract)); |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForAbstract) throws JavaModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] abstractMembersToPushDown= getAbstractMembers(membersToPushDown); |
| for (int i= 0; i < destinationClassesForAbstract.length; i++) { |
| result.merge(MemberCheckUtil.checkMembersInDestinationType(abstractMembersToPushDown, destinationClassesForAbstract[i])); |
| } |
| return result; |
| } |
| |
| private RefactoringStatus checkNonAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForNonAbstract) throws JavaModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] nonAbstractMembersToPushDown= getNonAbstractMembers(membersToPushDown); |
| for (int i= 0; i < destinationClassesForNonAbstract.length; i++) { |
| result.merge(MemberCheckUtil.checkMembersInDestinationType(nonAbstractMembersToPushDown, destinationClassesForNonAbstract[i])); |
| } |
| return result; |
| } |
| |
| private IFile[] getAllFilesToModify() throws CoreException{ |
| return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); |
| } |
| |
| private RefactoringStatus validateModifiesFiles() throws CoreException{ |
| return Checks.validateModifiesFiles(getAllFilesToModify()); |
| } |
| |
| //-- change creation ------- |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public IChange createChange(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| return new CompositeChange("Push down", fChangeManager.getAllChanges()); //$NON-NLS-1$ |
| } finally{ |
| pm.done(); |
| clearManagers(); |
| } |
| } |
| |
| private TextChangeManager createChangeManager(IProgressMonitor pm) throws CoreException{ |
| try{ |
| pm.beginTask(RefactoringCoreMessages.getString("PushDownRefactoring.preview"), 4); //$NON-NLS-1$ |
| |
| deleteMembersFromDeclaringClass(); |
| pm.worked(1); |
| |
| makeMethodsAbstractInDeclaringClass(); |
| pm.worked(1); |
| |
| if (shouldMakeDeclaringClassAbstract()) |
| makeDeclaringClassAbstract(); |
| pm.worked(1); |
| |
| createMembersInSubclasses(new SubProgressMonitor(pm, 1)); |
| |
| addImportsToSubclasses(new SubProgressMonitor(pm, 1)); |
| |
| TextChangeManager manager= new TextChangeManager(); |
| fillWithRewriteEdits(manager); |
| return manager; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private void addImportsToSubclasses(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 4); //$NON-NLS-1$ |
| IMember[] allMembers= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| IMember[] nonAbstractMembers= getNonAbstractMembers(allMembers); |
| IType[] destForNonAbstract= getDestinationClassesForNonAbstractMembers(new SubProgressMonitor(pm, 1)); |
| addImportsToTypesReferencedInMembers(nonAbstractMembers, destForNonAbstract, new SubProgressMonitor(pm, 1)); |
| |
| IMember[] abstractMembers= getAbstractMembers(allMembers); |
| IType[] destForAbstract= getDestinationClassesForAbstractMembers(new SubProgressMonitor(pm, 1)); |
| addImportsToTypesReferencedInMembers(abstractMembers, destForAbstract, new SubProgressMonitor(pm, 1)); |
| pm.done(); |
| } |
| |
| private void addImportsToTypesReferencedInMembers(IMember[] members, IType[] destinationClasses, IProgressMonitor pm) throws JavaModelException{ |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| IType[] typesReferenced= getTypesReferencedIn(members, pm); |
| for (int i= 0; i < destinationClasses.length; i++) { |
| ICompilationUnit cu= getWorkingCopyOfCu(destinationClasses[i]); |
| for (int j= 0; j < typesReferenced.length; j++) { |
| fImportEditManager.addImportTo(typesReferenced[j], cu); |
| } |
| } |
| pm.done(); |
| } |
| |
| private IType[] getTypesReferencedIn(IMember[] members, IProgressMonitor pm) throws JavaModelException { |
| return ReferenceFinderUtil.getTypesReferencedIn(members, pm); |
| } |
| |
| private void deleteMembersFromDeclaringClass() throws JavaModelException { |
| IMember[] toDelete= getMembersToDeleteFromDeclaringClass(); |
| for (int i= 0; i < toDelete.length; i++) { |
| if (toDelete[i] instanceof IField) |
| deleteFieldFromDeclaringClass((IField)toDelete[i]); |
| else if (toDelete[i] instanceof IMethod) |
| deleteMethodFromDeclaringClass((IMethod)toDelete[i]); |
| } |
| } |
| |
| private IMember[] getMembersToDeleteFromDeclaringClass() { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isToBeDeletedFromDeclaringClass()) |
| result.add(info.getMember()); |
| } |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } |
| |
| private void deleteFieldFromDeclaringClass(IField field) throws JavaModelException{ |
| FieldDeclaration fd= getFieldDeclarationNode(field); |
| if (fd.fragments().size() == 1){ |
| fRewriteManager.getRewrite(getWorkingCopyOfCu(field)).markAsRemoved(fd); |
| } else { |
| VariableDeclarationFragment fragment= getFieldDeclarationFragmentNode(field); |
| fRewriteManager.getRewrite(getWorkingCopyOfCu(field)).markAsRemoved(fragment); |
| } |
| } |
| |
| private void deleteMethodFromDeclaringClass(IMethod method) throws JavaModelException{ |
| MethodDeclaration md= getMethodDeclarationNode(method); |
| fRewriteManager.getRewrite(getWorkingCopyOfCu(method)).markAsRemoved(md); |
| } |
| |
| private void createMembersInSubclasses(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| MemberActionInfo[] allMembers= getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass(); |
| IType[] destinationsForNonAbstract= getDestinationClassesForNonAbstractMembers(new SubProgressMonitor(pm, 1)); |
| IType[] destinationsForAbstract= getDestinationClassesForAbstractMembers(new SubProgressMonitor(pm, 1)); |
| for (int i= 0; i < allMembers.length; i++) { |
| MemberActionInfo info= allMembers[i]; |
| if (JdtFlags.isAbstract(info.getMember())) |
| createInAll(info, destinationsForAbstract); |
| else |
| createInAll(info, destinationsForNonAbstract); |
| } |
| pm.done(); |
| } |
| |
| private void createInAll(MemberActionInfo info, IType[] types) throws JavaModelException{ |
| for (int j= 0; j < types.length; j++) { |
| IType subclass= types[j]; |
| TypeDeclaration typeDeclaration= getTypeDeclarationNode(subclass); |
| create(info, typeDeclaration); |
| } |
| } |
| |
| private void create(MemberActionInfo info, TypeDeclaration typeDeclaration) throws JavaModelException { |
| if (info.isFieldInfo()) |
| createField(info, typeDeclaration); |
| else |
| createMethod(info, typeDeclaration); |
| } |
| |
| private void createMethod(MemberActionInfo info, TypeDeclaration typeDeclaration) throws JavaModelException { |
| ASTRewrite rewrite= getRewriteFor(typeDeclaration); |
| MethodDeclaration newMethod= createNewMethodDeclarationNode(info, rewrite); |
| rewrite.markAsInserted(newMethod); |
| int index= computeIndexForNewMethodDeclaration(typeDeclaration); |
| typeDeclaration.bodyDeclarations().add(index, newMethod); |
| } |
| |
| private static int computeIndexForNewMethodDeclaration(TypeDeclaration typeDeclaration) { |
| int index= getIndexOfLastBodyDeclarationOfKind(typeDeclaration, ASTNode.METHOD_DECLARATION); |
| if (index == -1) |
| return 1 + getIndexOfLastBodyDeclarationOfKind(typeDeclaration, ASTNode.FIELD_DECLARATION); |
| else |
| return 1 + index; |
| } |
| |
| private MethodDeclaration createNewMethodDeclarationNode(MemberActionInfo info, ASTRewrite rewrite) throws JavaModelException { |
| Assert.isTrue(! info.isFieldInfo()); |
| IMethod method= (IMethod)info.getMember(); |
| MethodDeclaration oldMethod= getMethodDeclarationNode(method); |
| AST ast= getAST(rewrite); |
| MethodDeclaration newMethod= ast.newMethodDeclaration(); |
| copyBodyOfPushedDownMethod(rewrite, method, oldMethod, newMethod); |
| newMethod.setConstructor(oldMethod.isConstructor()); |
| newMethod.setExtraDimensions(oldMethod.getExtraDimensions()); |
| if (info.copyJavadocToCopiesInSubclasses()) |
| copyJavadocNode(rewrite, method, oldMethod, newMethod); |
| newMethod.setModifiers(getNewModifiersForCopiedMethod(info, oldMethod)); |
| newMethod.setName(ast.newSimpleName(oldMethod.getName().getIdentifier())); |
| copyReturnType(rewrite, oldMethod, newMethod); |
| copyParameters(rewrite, oldMethod, newMethod); |
| copyThrownExceptions(oldMethod, newMethod); |
| return newMethod; |
| } |
| |
| private void copyBodyOfPushedDownMethod(ASTRewrite targetRewrite, IMethod method, MethodDeclaration oldMethod, MethodDeclaration newMethod) throws JavaModelException { |
| if (oldMethod.getBody() == null){ |
| newMethod.setBody(null); |
| return; |
| } |
| Block oldBody= oldMethod.getBody(); |
| String oldBodySource= getBufferText(oldBody); |
| String[] lines= Strings.convertIntoLines(oldBodySource); |
| Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); |
| oldBodySource= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(method)); |
| Block newBody= (Block)targetRewrite.createPlaceholder(oldBodySource, ASTRewrite.BLOCK); |
| newMethod.setBody(newBody); |
| } |
| |
| private int getNewModifiersForCopiedMethod(MemberActionInfo info, MethodDeclaration oldMethod) throws JavaModelException { |
| return info.getNewModifiersForCopyInSubclass(oldMethod.getModifiers()); |
| } |
| |
| private void copyThrownExceptions(MethodDeclaration oldMethod, MethodDeclaration newMethod) { |
| AST ast= newMethod.getAST(); |
| for (int i= 0, n= oldMethod.thrownExceptions().size(); i < n; i++) { |
| Name oldException= (Name)oldMethod.thrownExceptions().get(i); |
| if (oldException.isSimpleName()){ |
| Name newException= ast.newSimpleName(((SimpleName)oldException).getIdentifier()); |
| newMethod.thrownExceptions().add(i, newException); |
| } else { |
| String[] identifiers= ASTNodes.getIdentifiers((QualifiedName)oldException); |
| Name newException= ast.newName(identifiers); |
| newMethod.thrownExceptions().add(i, newException); |
| } |
| } |
| } |
| |
| private void copyParameters(ASTRewrite targetRewrite, MethodDeclaration oldMethod, MethodDeclaration newMethod) throws JavaModelException { |
| for (int i= 0, n= oldMethod.parameters().size(); i < n; i++) { |
| SingleVariableDeclaration oldParam= (SingleVariableDeclaration)oldMethod.parameters().get(i); |
| SingleVariableDeclaration newParam= createPlaceholderForSingleVariableDeclaration(oldParam, targetRewrite); |
| newMethod.parameters().add(i, newParam); |
| } |
| } |
| |
| private void copyReturnType(ASTRewrite targetRewrite, MethodDeclaration oldMethod, MethodDeclaration newMethod) throws JavaModelException { |
| Type newReturnType= createPlaceholderForType(oldMethod.getReturnType(), targetRewrite); |
| newMethod.setReturnType(newReturnType); |
| } |
| |
| private void createField(MemberActionInfo info, TypeDeclaration typeDeclaration) throws JavaModelException { |
| ASTRewrite rewrite= getRewriteFor(typeDeclaration); |
| FieldDeclaration newField= createNewFieldDeclarationNode(info,rewrite); |
| rewrite.markAsInserted(newField); |
| int index= computeIndexForNewFieldDeclaration(typeDeclaration); |
| typeDeclaration.bodyDeclarations().add(index, newField); |
| } |
| |
| private static int computeIndexForNewFieldDeclaration(TypeDeclaration typeDeclaration) { |
| return 1 + getIndexOfLastBodyDeclarationOfKind(typeDeclaration, ASTNode.FIELD_DECLARATION); |
| } |
| |
| private static int getIndexOfLastBodyDeclarationOfKind(TypeDeclaration typeDeclaration, int nodeType) { |
| List list= typeDeclaration.bodyDeclarations(); |
| for (int i= list.size() - 1; i >= 0; i--) { |
| if (((BodyDeclaration)list.get(i)).getNodeType() == nodeType) |
| return i; |
| } |
| return -1; |
| } |
| |
| private FieldDeclaration createNewFieldDeclarationNode(MemberActionInfo info, ASTRewrite rewrite) throws JavaModelException { |
| Assert.isTrue(info.isFieldInfo()); |
| IField field= (IField)info.getMember(); |
| AST ast= getAST(rewrite); |
| VariableDeclarationFragment oldFieldFragment= getFieldDeclarationFragmentNode(field); |
| VariableDeclarationFragment newFragment= ast.newVariableDeclarationFragment(); |
| newFragment.setExtraDimensions(oldFieldFragment.getExtraDimensions()); |
| if (oldFieldFragment.getInitializer() != null){ |
| Expression newInitializer= createPlaceholderForExpression(oldFieldFragment.getInitializer(), rewrite); |
| newFragment.setInitializer(newInitializer); |
| } |
| newFragment.setName(ast.newSimpleName(oldFieldFragment.getName().getIdentifier())); |
| FieldDeclaration newField= ast.newFieldDeclaration(newFragment); |
| FieldDeclaration oldField= getFieldDeclarationNode(field); |
| if (info.copyJavadocToCopiesInSubclasses()) |
| copyJavadocNode(rewrite, field, oldField, newField); |
| newField.setModifiers(getNewModifiersForCopiedField(info, oldField)); |
| |
| Type newType= createPlaceholderForType(oldField.getType(), rewrite); |
| newField.setType(newType); |
| return newField; |
| } |
| |
| private int getNewModifiersForCopiedField(MemberActionInfo info, FieldDeclaration oldField) throws JavaModelException { |
| return info.getNewModifiersForCopyInSubclass(oldField.getModifiers()); |
| } |
| |
| private static void copyJavadocNode(ASTRewrite rewrite, IMember member, BodyDeclaration oldDeclaration, BodyDeclaration newDeclaration) throws JavaModelException { |
| Javadoc oldJavaDoc= oldDeclaration.getJavadoc(); |
| if (oldJavaDoc == null) |
| return; |
| String source= oldJavaDoc.getComment(); |
| String[] lines= Strings.convertIntoLines(source); |
| Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); |
| source= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(member)); |
| Javadoc newJavaDoc= (Javadoc)rewrite.createPlaceholder(source, ASTRewrite.JAVADOC); |
| newDeclaration.setJavadoc(newJavaDoc); |
| } |
| |
| private static IType[] toTypeArray(IMember[] member){ |
| List list= Arrays.asList(member); |
| return (IType[]) list.toArray(new IType[list.size()]); |
| } |
| |
| private static IMember[] getNonAbstractMembers(IMember[] members) throws JavaModelException{ |
| List list= new ArrayList(); //Arrays.asList does not support removing |
| list.addAll(Arrays.asList(members)); |
| list.removeAll(Arrays.asList(getAbstractMembers(members))); |
| return (IMember[]) list.toArray(new IMember[list.size()]); |
| } |
| |
| private static IMember[] getAbstractMembers(IMember[] members) throws JavaModelException{ |
| List result= new ArrayList(members.length); |
| for (int i= 0; i < members.length; i++) { |
| IMember member= members[i]; |
| if (JdtFlags.isAbstract(member)) |
| result.add(member); |
| } |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } |
| |
| private IType[] getDestinationClassesForAbstractMembers(IProgressMonitor pm) throws JavaModelException { |
| return toTypeArray(getAbstractMembers(getDestinationClassesForNonAbstractMembers(pm))); |
| } |
| |
| private IType[] getDestinationClassesForNonAbstractMembers(IProgressMonitor pm) throws JavaModelException { |
| IType[] allDirectSubclasses= getAllDirectSubclassesOfDeclaringClass(pm); |
| List result= new ArrayList(allDirectSubclasses.length); |
| for (int i= 0; i < allDirectSubclasses.length; i++) { |
| IType subclass= allDirectSubclasses[i]; |
| if (isModifiable(subclass)) |
| result.add(subclass); |
| } |
| return (IType[]) result.toArray(new IType[result.size()]); |
| } |
| |
| private static boolean isModifiable(IType clazz) throws JavaModelException { |
| if (! clazz.exists()) |
| return false; |
| if (clazz.isBinary()) |
| return false; |
| if (clazz.isReadOnly()) |
| return false; |
| if (clazz.getCompilationUnit() == null) |
| return false; |
| if (! clazz.isStructureKnown()) |
| return false; |
| return true; |
| } |
| |
| private IType[] getAllDirectSubclassesOfDeclaringClass(IProgressMonitor pm) throws JavaModelException { |
| return getHierarchyOfDeclaringClass(pm).getSubclasses(getDeclaringClass()); |
| } |
| |
| public ITypeHierarchy getHierarchyOfDeclaringClass(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| if (fCachedClassHierarchy != null) |
| return fCachedClassHierarchy; |
| fCachedClassHierarchy= getDeclaringClass().newTypeHierarchy(pm); |
| return fCachedClassHierarchy; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private MemberActionInfo[] getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass() { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isToBeCreatedInSubclassesOfDeclaringClass()) |
| result.add(info); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| private void makeDeclaringClassAbstract() throws JavaModelException { |
| TypeDeclaration declaration= getTypeDeclarationNode(getDeclaringClass()); |
| ASTRewrite rewrite= getRewriteFor(declaration); |
| AST ast= getAST(rewrite); |
| TypeDeclaration newType= ast.newTypeDeclaration(); |
| newType.setInterface(declaration.isInterface()); |
| newType.setModifiers(createNewModifiersForMakingDeclaringClassAbstract(declaration)); |
| rewrite.markAsModified(declaration, newType); |
| } |
| |
| private int createNewModifiersForMakingDeclaringClassAbstract(TypeDeclaration declaration) { |
| int initialModifiers= declaration.getModifiers(); |
| return Modifier.ABSTRACT | initialModifiers; |
| } |
| |
| private boolean shouldMakeDeclaringClassAbstract() throws JavaModelException { |
| return ! JdtFlags.isAbstract(getDeclaringClass()) && |
| getInfosForNewMethodsToBeDeclaredAbstract().length != 0; |
| } |
| |
| private void makeMethodsAbstractInDeclaringClass() throws JavaModelException { |
| MemberActionInfo[] methods= getInfosForNewMethodsToBeDeclaredAbstract(); |
| for (int i= 0; i < methods.length; i++) { |
| declareAbstract(methods[i]); |
| } |
| } |
| |
| private MemberActionInfo[] getInfosForNewMethodsToBeDeclaredAbstract() throws JavaModelException { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isNewMethodToBeDeclaredAbstract()) |
| result.add(info); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| private void declareAbstract(MemberActionInfo info) throws JavaModelException { |
| Assert.isTrue(! info.isFieldInfo()); |
| IMethod method= (IMethod)info.getMember(); |
| if (JdtFlags.isAbstract(method)) |
| return; |
| MethodDeclaration declaration= getMethodDeclarationNode(method); |
| ASTRewrite rewrite= getRewriteFor(declaration); |
| AST ast= getAST(rewrite); |
| MethodDeclaration newMethod= ast.newMethodDeclaration(); |
| rewrite.markAsRemoved(declaration.getBody()); |
| newMethod.setModifiers(createModifiersForMethodMadeAbstract(info, declaration.getModifiers())); |
| newMethod.setExtraDimensions(declaration.getExtraDimensions()); |
| newMethod.setConstructor(declaration.isConstructor()); |
| rewrite.markAsModified(declaration, newMethod); |
| } |
| |
| private int createModifiersForMethodMadeAbstract(MemberActionInfo info, int oldModifiers) throws JavaModelException { |
| return info.getNewModifiersForOriginal(oldModifiers); |
| } |
| |
| private void fillWithRewriteEdits(TextChangeManager manager) throws CoreException { |
| CompilationUnit[] cuNodes= fRewriteManager.getAllCompilationUnitNodes(); |
| for (int i= 0; i < cuNodes.length; i++) { |
| CompilationUnit cuNode= cuNodes[i]; |
| ASTRewrite rewrite= fRewriteManager.getRewrite(cuNode); |
| TextBuffer textBuffer= TextBuffer.create(fAstManager.getCompilationUnit(cuNode).getBuffer().getContents()); |
| MultiTextEdit resultingEdits= new MultiTextEdit(); |
| rewrite.rewriteNode(textBuffer, resultingEdits, null); |
| ICompilationUnit cu= fAstManager.getCompilationUnit(cuNode); |
| TextChange textChange= manager.get(cu); |
| if (fImportEditManager.hasImportEditFor(cu)) |
| resultingEdits.add(fImportEditManager.getImportEdit(cu)); |
| textChange.addTextEdit(RefactoringCoreMessages.getString("PushDownRefactoring.push_down_members"), resultingEdits); //$NON-NLS-1$ |
| rewrite.removeModifications(); |
| } |
| } |
| |
| private ICompilationUnit getWorkingCopyOfCu(IMember member){ |
| return WorkingCopyUtil.getWorkingCopyIfExists(member.getCompilationUnit()); |
| } |
| |
| private ASTRewrite getRewriteFor(ASTNode node) { |
| return fRewriteManager.getRewrite(ASTNodeMappingManager.getCompilationUnitNode(node)); |
| } |
| |
| private static AST getAST(ASTRewrite rewrite){ |
| return rewrite.getRootNode().getAST(); |
| } |
| |
| //-----declaration node finders ----- |
| |
| private FieldDeclaration getFieldDeclarationNode(IField field) throws JavaModelException { |
| return ASTNodeSearchUtil.getFieldDeclarationNode(field, fAstManager); |
| } |
| |
| private VariableDeclarationFragment getFieldDeclarationFragmentNode(IField field) throws JavaModelException { |
| return ASTNodeSearchUtil.getFieldDeclarationFragmentNode(field, fAstManager); |
| } |
| |
| private TypeDeclaration getTypeDeclarationNode(IType type) throws JavaModelException { |
| return ASTNodeSearchUtil.getTypeDeclarationNode(type, fAstManager); |
| } |
| |
| private MethodDeclaration getMethodDeclarationNode(IMethod method) throws JavaModelException { |
| return ASTNodeSearchUtil.getMethodDeclarationNode(method, fAstManager); |
| } |
| |
| //-- |
| private static String createLabel(IMember member){ |
| if (member instanceof IType) |
| return createTypeLabel((IType)member); |
| else if (member instanceof IMethod) |
| return createMethodLabel((IMethod)member); |
| else if (member instanceof IField) |
| return createFieldLabel((IField)member); |
| else if (member instanceof IInitializer) |
| return RefactoringCoreMessages.getString("PushDownRefactoring.initializer"); //$NON-NLS-1$ |
| Assert.isTrue(false); |
| return null; |
| } |
| |
| private static String createTypeLabel(IType type){ |
| return JavaModelUtil.getFullyQualifiedName(type); |
| } |
| |
| private static String createFieldLabel(IField field){ |
| return field.getElementName(); |
| } |
| |
| private static String createMethodLabel(IMethod method){ |
| return JavaElementUtil.createMethodSignature(method); |
| } |
| |
| private String getBufferText(ASTNode node) throws JavaModelException{ |
| return fAstManager.getCompilationUnit(node).getBuffer().getText(node.getStartPosition(), node.getLength()); |
| } |
| |
| //---- placeholder creators ---- |
| |
| private Expression createPlaceholderForExpression(Expression expression, ASTRewrite rewrite) throws JavaModelException{ |
| return (Expression)rewrite.createPlaceholder(getBufferText(expression), ASTRewrite.EXPRESSION); |
| } |
| |
| private SingleVariableDeclaration createPlaceholderForSingleVariableDeclaration(SingleVariableDeclaration declaration, ASTRewrite rewrite) throws JavaModelException{ |
| return (SingleVariableDeclaration)rewrite.createPlaceholder(getBufferText(declaration), ASTRewrite.SINGLEVAR_DECLARATION); |
| } |
| |
| private Type createPlaceholderForType(Type type, ASTRewrite rewrite) throws JavaModelException{ |
| return (Type)rewrite.createPlaceholder(getBufferText(type), ASTRewrite.TYPE); |
| } |
| } |