| /******************************************************************************* |
| * 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.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.TextEdit; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| import org.eclipse.core.resources.IFile; |
| |
| 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.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.ToolFactory; |
| import org.eclipse.jdt.core.compiler.IScanner; |
| import org.eclipse.jdt.core.compiler.ITerminalSymbols; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; |
| 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.IBinding; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| 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.ReturnStatement; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.SuperFieldAccess; |
| import org.eclipse.jdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.TypeDeclarationStatement; |
| 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.SourceRange; |
| import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; |
| import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeConstants; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.jdt.internal.corext.dom.ASTRewrite; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| 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.JavaStatusContext; |
| 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.rename.RefactoringScopeFactory; |
| 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.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 PullUpRefactoring extends Refactoring { |
| private static class SuperReferenceFinderVisitor extends ASTVisitor{ |
| |
| private Collection fFoundRanges; |
| private int fMethodSourceStart; |
| private int fMethodSourceEnd; |
| private String fMethodSource; |
| private String fSuperTypeName; |
| |
| SuperReferenceFinderVisitor(IMethod method, IType superType) throws JavaModelException{ |
| fFoundRanges= new ArrayList(0); |
| fMethodSourceStart= method.getSourceRange().getOffset(); |
| fMethodSourceEnd= method.getSourceRange().getOffset() + method.getSourceRange().getLength(); |
| fMethodSource= method.getSource(); |
| fSuperTypeName= JavaModelUtil.getFullyQualifiedName(superType); |
| } |
| |
| ISourceRange[] getSuperReferenceRanges(){ |
| return (ISourceRange[]) fFoundRanges.toArray(new ISourceRange[fFoundRanges.size()]); |
| } |
| |
| private boolean withinMethod(ASTNode node){ |
| return (node.getStartPosition() >= fMethodSourceStart) && (node.getStartPosition() <= fMethodSourceEnd); |
| } |
| |
| private ISourceRange getSuperRange(String scanSource){ |
| IScanner scanner= ToolFactory.createScanner(false, false, false, false); |
| scanner.setSource(scanSource.toCharArray()); |
| try { |
| int token = scanner.getNextToken(); |
| while (token != ITerminalSymbols.TokenNameEOF) { |
| switch (token) { |
| case ITerminalSymbols.TokenNamesuper : |
| int start= scanner.getCurrentTokenEndPosition() + 1 - scanner.getCurrentTokenSource().length; |
| int end= scanner.getCurrentTokenEndPosition() + 1; |
| return new SourceRange(start, end - start); |
| } |
| token = scanner.getNextToken(); |
| } |
| } catch(InvalidInputException e) { |
| return new SourceRange(0, 0); //FIX ME |
| } |
| return new SourceRange(0, 0);//FIX ME |
| } |
| |
| private String getSource(int start, int end){ |
| return fMethodSource.substring(start - fMethodSourceStart, end - fMethodSourceStart); |
| } |
| |
| private String getScanSource(SuperMethodInvocation node){ |
| return getSource(getScanSourceOffset(node), node.getName().getStartPosition()); |
| } |
| |
| private String getScanSource(SuperFieldAccess node){ |
| return getSource(getScanSourceOffset(node), node.getName().getStartPosition()); |
| } |
| |
| private static int getScanSourceOffset(SuperMethodInvocation node){ |
| if (node.getQualifier() == null) |
| return node.getStartPosition(); |
| else |
| return node.getQualifier().getStartPosition() + node.getQualifier().getLength(); |
| } |
| |
| private static int getScanSourceOffset(SuperFieldAccess node){ |
| if (node.getQualifier() == null) |
| return node.getStartPosition(); |
| else |
| return node.getQualifier().getStartPosition() + node.getQualifier().getLength(); |
| } |
| |
| //---- visit methods ------------------ |
| |
| public boolean visit(SuperFieldAccess node) { |
| if (! withinMethod(node)) |
| return true; |
| |
| ISourceRange superRange= getSuperRange(getScanSource(node)); |
| fFoundRanges.add(new SourceRange(superRange.getOffset() + getScanSourceOffset(node), superRange.getLength())); |
| return true; |
| } |
| |
| public boolean visit(SuperMethodInvocation node) { |
| if (! withinMethod(node)) |
| return true; |
| |
| IBinding nameBinding= node.getName().resolveBinding(); |
| if (nameBinding != null && nameBinding.getKind() == IBinding.METHOD){ |
| ITypeBinding declaringType= ((IMethodBinding)nameBinding).getDeclaringClass(); |
| if (declaringType != null && ! fSuperTypeName.equals(Bindings.getFullyQualifiedName(declaringType))) |
| return true; |
| } |
| ISourceRange superRange= getSuperRange(getScanSource(node)); |
| fFoundRanges.add(new SourceRange(superRange.getOffset() + getScanSourceOffset(node), superRange.getLength())); |
| return true; |
| } |
| |
| //- stop nodes --- |
| |
| public boolean visit(TypeDeclarationStatement node) { |
| if (withinMethod(node)) |
| return false; |
| return true; |
| } |
| |
| public boolean visit(AnonymousClassDeclaration node) { |
| if (withinMethod(node)) |
| return false; |
| return true; |
| } |
| } |
| |
| private IMember[] fMembersToPullUp; |
| private IMethod[] fMethodsToDeclareAbstract; |
| private IMethod[] fMethodsToDelete; |
| private TextChangeManager fChangeManager; |
| private IType fTargetType; |
| private boolean fCreateMethodStubs; |
| |
| private final ImportRewriteManager fImportManager; |
| private final CodeGenerationSettings fPreferenceSettings; |
| |
| //caches |
| private IType fCachedDeclaringType; |
| private IType[] fCachedTypesReferencedInPulledUpMembers; |
| private ITypeHierarchy fCachedTargetClassHierarchy; |
| private Set fCachedSkippedSuperclasses;//Set<IType> |
| private final Map fCachedMembersReferences; //Map<IMember, SearchResultGroup[]> |
| |
| private PullUpRefactoring(IMember[] elements, CodeGenerationSettings preferenceSettings){ |
| Assert.isNotNull(elements); |
| Assert.isNotNull(preferenceSettings); |
| fMembersToPullUp= elements; |
| fMethodsToDelete= new IMethod[0]; |
| fMethodsToDeclareAbstract= new IMethod[0]; |
| fPreferenceSettings= preferenceSettings; |
| fImportManager= new ImportRewriteManager(preferenceSettings); |
| fCreateMethodStubs= true; |
| fCachedMembersReferences= new HashMap(2); |
| } |
| |
| public static PullUpRefactoring create(IMember[] members, CodeGenerationSettings preferenceSettings) throws JavaModelException{ |
| if (! isAvailable(members)) |
| return null; |
| if (isOneTypeWithPullableMembers(members)) { |
| PullUpRefactoring result= new PullUpRefactoring(new IMember[0], preferenceSettings); |
| result.fCachedDeclaringType= getSingleTopLevelType(members); |
| return result; |
| } |
| return new PullUpRefactoring(members, preferenceSettings); |
| } |
| |
| public static boolean isAvailable(IMember[] members) throws JavaModelException{ |
| if (isOneTypeWithPullableMembers(members)) |
| return true; |
| return members != null && |
| members.length != 0 && |
| areAllPullable(members) && |
| haveCommonDeclaringType(members); |
| } |
| |
| private static boolean isOneTypeWithPullableMembers(IMember[] members) throws JavaModelException { |
| IType singleTopLevelType= getSingleTopLevelType(members); |
| return (singleTopLevelType != null && getPullableMembers(singleTopLevelType).length != 0); |
| } |
| |
| private static IType getSingleTopLevelType(IMember[] members) { |
| if (members != null && members.length == 1 && Checks.isTopLevelType(members[0])) |
| return (IType)members[0]; |
| return null; |
| } |
| |
| /* |
| * @see IRefactoring#getName() |
| */ |
| public String getName() { |
| return RefactoringCoreMessages.getString("PullUpRefactoring.Pull_Up"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Sets the methodsToDelete. |
| * @param methodsToDelete The methodsToDelete to set |
| * no validation is done on these methods - they will simply be removed. |
| * it is the caller's resposibility to ensure that the selection makes sense from the behavior-preservation point of view. |
| */ |
| public void setMethodsToDelete(IMethod[] methodsToDelete) { |
| Assert.isNotNull(methodsToDelete); |
| fMethodsToDelete= getOriginals(methodsToDelete); |
| } |
| |
| /* |
| * no validation is done here - the members must be a subset of those |
| * returned by the call to getPullableMembersOfDeclaringType |
| */ |
| public void setMethodsToDeclareAbstract(IMethod[] methods) { |
| Assert.isNotNull(methods); |
| fMethodsToDeclareAbstract= getOriginals(methods); |
| } |
| |
| /* |
| * no validation is done here - the members must be a subset of those |
| * returned by the call to getPullableMembersOfDeclaringType |
| */ |
| public void setMembersToPullUp(IMember[] elements){ |
| Assert.isNotNull(elements); |
| fMembersToPullUp= (IMember[])SourceReferenceUtil.sortByOffset(elements); |
| fMembersToPullUp= WorkingCopyUtil.getOriginals(fMembersToPullUp); |
| } |
| |
| private IMember[] getMembersToDelete(IProgressMonitor pm) throws JavaModelException{ |
| try{ |
| IMember[] typesToDelete= WorkingCopyUtil.getOriginals(getMembersOfType(fMembersToPullUp, IJavaElement.TYPE)); |
| IMember[] matchingElements= getMatchingElements(pm, false); |
| IMember[] matchingFields= WorkingCopyUtil.getOriginals(getMembersOfType(matchingElements, IJavaElement.FIELD)); |
| return merge(matchingFields, typesToDelete, fMethodsToDelete); |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| public void setCreateMethodStubs(boolean create) { |
| fCreateMethodStubs= create; |
| } |
| |
| public boolean getCreateMethodStubs() { |
| return fCreateMethodStubs; |
| } |
| |
| public IMember[] getMembersToPullUp() { |
| return fMembersToPullUp; |
| } |
| |
| public IType getDeclaringType(){ |
| if (fCachedDeclaringType != null) |
| return fCachedDeclaringType; |
| //all members declared in same type - checked in precondition |
| Assert.isTrue(fMembersToPullUp.length > 0);//ensured by constructor |
| fCachedDeclaringType= (IType)WorkingCopyUtil.getOriginal(fMembersToPullUp[0].getDeclaringType()); |
| return fCachedDeclaringType; |
| } |
| |
| public IMember[] getPullableMembersOfDeclaringType() { |
| try{ |
| return getPullableMembers(getDeclaringType()); |
| } catch (JavaModelException e){ |
| return new IMember[0]; |
| } |
| } |
| |
| private static IMember[] getPullableMembers(IType type) throws JavaModelException { |
| List list= new ArrayList(3); |
| addAllPullable(type.getFields(), list); |
| addAllPullable(type.getMethods(), list); |
| addAllPullable(type.getTypes(), list); |
| return (IMember[]) list.toArray(new IMember[list.size()]); |
| } |
| |
| private static void addAllPullable(IMember[] members, List list) throws JavaModelException{ |
| for (int i= 0; i < members.length; i++) { |
| if (isPullable(members[i])) |
| list.add(members[i]); |
| } |
| } |
| private static boolean isPullable(IMember member) throws JavaModelException { |
| if (member.getElementType() != IJavaElement.METHOD && |
| member.getElementType() != IJavaElement.FIELD && |
| member.getElementType() != IJavaElement.TYPE) |
| return false; |
| |
| if (! Checks.isAvailable(member)) |
| return false; |
| |
| if (member instanceof IType){ |
| if (! JdtFlags.isStatic(member)) |
| return false; |
| } |
| if (member instanceof IMethod ){ |
| IMethod method= (IMethod) member; |
| if (method.isConstructor()) |
| return false; |
| |
| if (JdtFlags.isNative(method)) //for now - move to input preconditions |
| return false; |
| } |
| |
| return true; |
| } |
| |
| public ITypeHierarchy getTypeHierarchyOfTargetClass(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| if (fCachedTargetClassHierarchy != null && fCachedTargetClassHierarchy.getType().equals(getTargetClass())) |
| return fCachedTargetClassHierarchy; |
| fCachedTargetClassHierarchy= getTargetClass().newTypeHierarchy(pm); |
| return fCachedTargetClassHierarchy; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| public IType[] getPossibleTargetClasses(IProgressMonitor pm) throws JavaModelException { |
| IType[] superClasses= getDeclaringType().newSupertypeHierarchy(pm).getAllSuperclasses(getDeclaringType()); |
| List superClassList= new ArrayList(superClasses.length); |
| for (int i= 0; i < superClasses.length; i++) { |
| if (isPossibleTargetClass(superClasses[i])) |
| superClassList.add(superClasses[i]); |
| } |
| Collections.reverse(superClassList); |
| return (IType[]) superClassList.toArray(new IType[superClassList.size()]); |
| } |
| |
| private boolean isPossibleTargetClass(IType clazz) { |
| return clazz != null && clazz.exists() && ! clazz.isReadOnly() && ! clazz.isBinary() && ! "java.lang.Object".equals(clazz.getFullyQualifiedName()); //$NON-NLS-1$ |
| } |
| |
| public IType getTargetClass(){ |
| return fTargetType; |
| } |
| |
| public void setTargetClass(IType targetType){ |
| Assert.isNotNull(targetType); |
| if (! targetType.equals(fTargetType)) |
| fCachedTargetClassHierarchy= null; |
| fTargetType= targetType; |
| } |
| |
| public IMember[] getMatchingElements(IProgressMonitor pm, boolean includeMethodsToDeclareAbstract) throws JavaModelException { |
| try{ |
| Set result= new HashSet(); |
| IType targetClass= getTargetClass(); |
| Map matching= getMatchingMemberMatching(pm, includeMethodsToDeclareAbstract); |
| for (Iterator iter= matching.keySet().iterator(); iter.hasNext();) { |
| IMember key= (IMember) iter.next(); |
| if (key.getDeclaringType().equals(targetClass)) |
| iter.remove(); |
| else |
| result.addAll((Set)matching.get(key)); |
| } |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| public IMember[] getAdditionalRequiredMembersToPullUp(IProgressMonitor pm) throws JavaModelException{ |
| IMember[] members= getMembersToBeCreatedInTargetClass(); |
| pm.beginTask(RefactoringCoreMessages.getString("PullUpRefactoring.calculating_required"), 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); |
| addAllRequiredPullableMembers(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 addAllRequiredPullableMembers(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| addAllRequiredPullableMethods(queue, member, new SubProgressMonitor(pm, 1)); |
| addAllRequiredPullableFields(queue, member, new SubProgressMonitor(pm, 1)); |
| addAllRequiredPullableTypes(queue, member, new SubProgressMonitor(pm, 1)); |
| pm.done(); |
| } |
| |
| private void addAllRequiredPullableTypes(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| IType[] requiredTypes= ReferenceFinderUtil.getTypesReferencedIn(new IJavaElement[]{member}, pm); |
| boolean isStatic= JdtFlags.isStatic(member); |
| for (int i= 0; i < requiredTypes.length; i++) { |
| IType requiredType= requiredTypes[i]; |
| if (isStatic && ! JdtFlags.isStatic(requiredType)) |
| continue; |
| if (isRequiredPullableMember(queue, requiredType)) |
| queue.add(requiredType); |
| } |
| } |
| |
| private void addAllRequiredPullableFields(List queue, IMember member, IProgressMonitor pm) throws JavaModelException { |
| IField[] requiredFields= ReferenceFinderUtil.getFieldsReferencedIn(new IJavaElement[]{member}, pm); |
| boolean isStatic= JdtFlags.isStatic(member); |
| for (int i= 0; i < requiredFields.length; i++) { |
| IField requiredField= requiredFields[i]; |
| if (isStatic && ! JdtFlags.isStatic(requiredField)) |
| continue; |
| if (isRequiredPullableMember(queue, requiredField)) |
| queue.add(requiredField); |
| } |
| } |
| |
| private void addAllRequiredPullableMethods(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$ |
| boolean isStatic= JdtFlags.isStatic(member); |
| for (int i= 0; i < requiredMethods.length; i++) { |
| IMethod requiredMethod= requiredMethods[i]; |
| if (isStatic && ! JdtFlags.isStatic(requiredMethod)) |
| continue; |
| if (isRequiredPullableMember(queue, requiredMethod) && ! isVirtualAccessibleFromTargetClass(requiredMethod, new SubProgressMonitor(sPm, 1))) |
| queue.add(requiredMethod); |
| } |
| sPm.done(); |
| } |
| |
| private boolean isVirtualAccessibleFromTargetClass(IMethod method, IProgressMonitor pm) throws JavaModelException { |
| try{ |
| return MethodChecks.isVirtual(method) && isDeclaredInTargetClassOrItsSuperclass(method, pm); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private boolean isDeclaredInTargetClassOrItsSuperclass(IMethod method, IProgressMonitor pm) throws JavaModelException { |
| try{ |
| boolean isConstructor= false; |
| String[] paramTypes= method.getParameterTypes(); |
| String name= method.getElementName(); |
| IType targetClass= getTargetClass(); |
| ITypeHierarchy hierarchy= getTypeHierarchyOfTargetClass(pm); |
| IMethod first= JavaModelUtil.findMethod(name, paramTypes, isConstructor, targetClass); |
| if (first != null && MethodChecks.isVirtual(first)) |
| return true; |
| IMethod found= JavaModelUtil.findMethodDeclarationInHierarchy(hierarchy, targetClass, name, paramTypes, isConstructor); |
| return found != null && MethodChecks.isVirtual(found); |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private boolean isRequiredPullableMember(List queue, IMember member) throws JavaModelException { |
| if (member.getDeclaringType() == null) //not a member |
| return false; |
| return member.getDeclaringType().equals(getDeclaringType()) && ! queue.contains(member) && isPullable(member); |
| } |
| |
| /* |
| * mapping: Map: IMember -> Set of IMembers (of the same type as key) |
| */ |
| private static void addToMapping(Map mapping, IMember key, IMember matchingMember){ |
| Set matchingSet; |
| if (mapping.containsKey(key)){ |
| matchingSet= (Set)mapping.get(key); |
| }else{ |
| matchingSet= new HashSet(); |
| mapping.put(key, matchingSet); |
| } |
| Assert.isTrue(! matchingSet.contains(matchingMember)); |
| matchingSet.add(matchingMember); |
| } |
| |
| private Map getMatchingMembersMappingFromTypeAndAllSubtypes(ITypeHierarchy hierarchy, IType type, boolean includeMethodsToDeclareAbstract) throws JavaModelException { |
| Map result= new HashMap(); //IMember -> Set of IMembers (of the same type as key) |
| result.putAll(getMatchingMembersMapping(type)); |
| IType[] subTypes= hierarchy.getAllSubtypes(type); |
| for (int i = 0; i < subTypes.length; i++) { |
| mergeSets(result, getMatchingMembersMapping(subTypes[i])); |
| } |
| if (includeMethodsToDeclareAbstract) |
| return result; |
| |
| for (int i= 0; i < fMethodsToDeclareAbstract.length; i++) { |
| if (result.containsKey(fMethodsToDeclareAbstract[i])) |
| result.remove(fMethodsToDeclareAbstract[i]); |
| } |
| return result; |
| } |
| |
| /* |
| * result: IMember -> Set |
| * map: IMember -> Set |
| * this method merges sets for common keys and adds entries to result for |
| * those keys that exist only in map |
| */ |
| private static void mergeSets(Map result, Map map) { |
| mergeSetsForCommonKeys(result, map); |
| putAllThatDoNotExistInResultYet(result, map); |
| } |
| |
| private static void mergeSetsForCommonKeys(Map result, Map map) { |
| for (Iterator iter= result.keySet().iterator(); iter.hasNext();) { |
| IMember key= (IMember) iter.next(); |
| if (map.containsKey(key)){ |
| Set resultSet= (Set)result.get(key); |
| Set mapSet= (Set)map.get(key); |
| resultSet.addAll(mapSet); |
| } |
| } |
| } |
| |
| private static void putAllThatDoNotExistInResultYet(Map result, Map map) { |
| for (Iterator iter= map.keySet().iterator(); iter.hasNext();) { |
| IMember key= (IMember) iter.next(); |
| if (! result.containsKey(key)){ |
| Set mapSet= (Set)map.get(key); |
| Set resultSet= new HashSet(mapSet); |
| result.put(key, resultSet); |
| } |
| } |
| } |
| |
| private Map getMatchingMembersMapping(IType analyzedType) throws JavaModelException { |
| Map result= new HashMap();//IMember -> Set of IMembers (of the same type as key) |
| IMember[] members= getMembersToBeCreatedInTargetClass(); |
| for (int i = 0; i < members.length; i++) { |
| IMember member= members[i]; |
| if (member instanceof IMethod){ |
| IMethod method= (IMethod)member; |
| IMethod found= MemberCheckUtil.findMethod(method, analyzedType.getMethods()); |
| if (found != null) |
| addToMapping(result, method, found); |
| } else if (member instanceof IField){ |
| IField field= (IField)member; |
| IField found= analyzedType.getField(field.getElementName()); |
| if (found.exists()) |
| addToMapping(result, field, found); |
| } else if (member instanceof IType){ |
| IType type= (IType)member; |
| IField found= type.getField(type.getElementName()); |
| if (found.exists()) |
| addToMapping(result, type, found); |
| } else |
| Assert.isTrue(false); |
| } |
| |
| return result; |
| } |
| |
| /* |
| * @see Refactoring#checkActivation(IProgressMonitor) |
| */ |
| public RefactoringStatus checkActivation(IProgressMonitor pm) throws JavaModelException { |
| try { |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| fMembersToPullUp= WorkingCopyUtil.getOriginals(fMembersToPullUp); |
| |
| result.merge(checkDeclaringType(new SubProgressMonitor(pm, 1))); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(checkIfMembersExist()); |
| if (result.hasFatalError()) |
| return result; |
| return result; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private RefactoringStatus checkIfMembersExist() { |
| RefactoringStatus result= new RefactoringStatus(); |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| IMember orig= fMembersToPullUp[i]; |
| if (orig == null || ! orig.exists()){ |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.does_not_exist", orig.getElementName());//$NON-NLS-1$ |
| result.addFatalError(message); |
| } |
| } |
| return result; |
| } |
| |
| private static IMethod[] getOriginals(IMethod[] methods){ |
| IMethod[] result= new IMethod[methods.length]; |
| for (int i= 0; i < methods.length; i++) { |
| result[i]= (IMethod)WorkingCopyUtil.getOriginal(methods[i]); |
| } |
| return result; |
| } |
| |
| private static IMember[] merge(IMember[] a1, IMember[] a2, IMember[] a3){ |
| return JavaElementUtil.merge(JavaElementUtil.merge(a1, a2), a3); |
| } |
| |
| private static IMember[] getMembersOfType(IMember[] members, int type){ |
| List list= Arrays.asList(JavaElementUtil.getElementsOfType(members, type)); |
| return (IMember[]) list.toArray(new IMember[list.size()]); |
| } |
| |
| /* |
| * @see Refactoring#checkInput(IProgressMonitor) |
| */ |
| public RefactoringStatus checkInput(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| pm.beginTask(RefactoringCoreMessages.getString("PullUpRefactoring.preview"), 7); //$NON-NLS-1$ |
| clearCaches(); |
| |
| RefactoringStatus result= new RefactoringStatus(); |
| result.merge(checkFinalFields(new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccesses(new SubProgressMonitor(pm, 1))); |
| result.merge(checkMembersInDestinationType()); |
| pm.worked(1); |
| result.merge(checkMembersInSubclasses(new SubProgressMonitor(pm, 1))); |
| result.merge(checkIfSkippingOverElements(new SubProgressMonitor(pm, 1))); |
| if (shouldMakeTargetClassAbstract()) |
| result.merge(checkCallsToTargetClassConstructors(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 RefactoringStatus checkMembersInDestinationType() throws JavaModelException { |
| IMember[] membersToBeCreatedInTargetClass = getMembersToBeCreatedInTargetClass(); |
| List newMembersInDesitnationType= new ArrayList(membersToBeCreatedInTargetClass.length); |
| newMembersInDesitnationType.addAll(Arrays.asList(membersToBeCreatedInTargetClass)); |
| newMembersInDesitnationType.removeAll(Arrays.asList(fMethodsToDelete)); |
| IMember[] members= (IMember[]) newMembersInDesitnationType.toArray(new IMember[newMembersInDesitnationType.size()]); |
| return MemberCheckUtil.checkMembersInDestinationType(members, getTargetClass()); |
| } |
| |
| private void clearCaches() { |
| fCachedTypesReferencedInPulledUpMembers= null; |
| fImportManager.clear(); |
| fCachedMembersReferences.clear(); |
| fCachedTargetClassHierarchy= null; |
| } |
| |
| private RefactoringStatus checkCallsToTargetClassConstructors(IProgressMonitor pm) throws JavaModelException { |
| SearchResultGroup[] groups= ConstructorReferenceFinder.getConstructorReferences(getTargetClass(), pm); |
| RefactoringStatus result= new RefactoringStatus(); |
| String[] keys= {createTypeLabel(getTargetClass())}; |
| String msg= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.gets_instantiated", keys); //$NON-NLS-1$ |
| for (int i= 0; i < groups.length; i++) { |
| ICompilationUnit cu= groups[i].getCompilationUnit(); |
| if (cu == null) |
| continue; |
| CompilationUnit cuNode= AST.parseCompilationUnit(cu, false); |
| ASTNode[] refNodes= ASTNodeSearchUtil.getAstNodes(groups[i].getSearchResults(), cuNode); |
| for (int j= 0; j < refNodes.length; j++) { |
| ASTNode node= refNodes[j]; |
| if ((node instanceof ClassInstanceCreation) || ConstructorReferenceFinder.isImplicitConstructorReferenceNodeInClassCreations(node)){ |
| Context context= JavaStatusContext.create(cu, node); |
| result.addError(msg, context); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkIfSkippingOverElements(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| try{ |
| Set skippedSuperclassSet= getSkippedSuperclasses(new SubProgressMonitor(pm, 1)); |
| IType[] skippedSuperclasses= (IType[]) skippedSuperclassSet.toArray(new IType[skippedSuperclassSet.size()]); |
| RefactoringStatus result= new RefactoringStatus(); |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| IMember element= fMembersToPullUp[i]; |
| for (int j= 0; j < skippedSuperclasses.length; j++) { |
| result.merge(checkIfDeclaredIn(element, skippedSuperclasses[j])); |
| } |
| } |
| return result; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private RefactoringStatus checkIfDeclaredIn(IMember element, IType type) throws JavaModelException { |
| if (element instanceof IMethod) |
| return checkIfMethodDeclaredIn((IMethod)element, type); |
| else if (element instanceof IField) |
| return checkIfFieldDeclaredIn((IField)element, type); |
| else if (element instanceof IType) |
| return checkIfTypeDeclaredIn((IType)element, type); |
| Assert.isTrue(false); |
| return null; |
| } |
| |
| private RefactoringStatus checkIfTypeDeclaredIn(IType iType, IType type) { |
| IType typeInType= type.getType(iType.getElementName()); |
| if (! typeInType.exists()) |
| return null; |
| String[] keys= {createTypeLabel(typeInType), createTypeLabel(type)}; |
| String msg= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.Type_declared_in_class", keys); //$NON-NLS-1$ |
| Context context= JavaStatusContext.create(typeInType); |
| return RefactoringStatus.createWarningStatus(msg, context); |
| } |
| |
| private RefactoringStatus checkIfFieldDeclaredIn(IField iField, IType type) { |
| IField fieldInType= type.getField(iField.getElementName()); |
| if (! fieldInType.exists()) |
| return null; |
| String[] keys= {createFieldLabel(fieldInType), createTypeLabel(type)}; |
| String msg= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.Field_declared_in_class", keys); //$NON-NLS-1$ |
| Context context= JavaStatusContext.create(fieldInType); |
| return RefactoringStatus.createWarningStatus(msg, context); |
| } |
| |
| private RefactoringStatus checkIfMethodDeclaredIn(IMethod iMethod, IType type) throws JavaModelException { |
| IMethod methodInType= JavaModelUtil.findMethod(iMethod.getElementName(), iMethod.getParameterTypes(), iMethod.isConstructor(), type); |
| if (methodInType == null || ! methodInType.exists()) |
| return null; |
| String[] keys= {createMethodLabel(methodInType), createTypeLabel(type)}; |
| String msg= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.methodis_declared_in_class", keys); //$NON-NLS-1$ |
| Context context= JavaStatusContext.create(methodInType); |
| return RefactoringStatus.createWarningStatus(msg, context); |
| } |
| |
| 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 static boolean areAllPullable(IMember[] members) throws JavaModelException { |
| for (int i = 0; i < members.length; i++) { |
| if (! isPullable(members[i])) |
| return false; |
| } |
| return true; |
| } |
| |
| private RefactoringStatus checkDeclaringType(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| IType declaringType= getDeclaringType(); |
| |
| if (declaringType.isInterface()) //for now |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PullUpRefactoring.no_interface_members")); //$NON-NLS-1$ |
| |
| if (JavaModelUtil.getFullyQualifiedName(declaringType).equals("java.lang.Object")) //$NON-NLS-1$ |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PullUpRefactoring.no_java.lang.Object")); //$NON-NLS-1$ |
| |
| if (declaringType.isBinary()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PullUpRefactoring.no_binary_types")); //$NON-NLS-1$ |
| |
| if (declaringType.isReadOnly()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PullUpRefactoring.no_read_only_types")); //$NON-NLS-1$ |
| |
| return checkSuperclassesOfDeclaringClass(pm); |
| } |
| |
| private RefactoringStatus checkSuperclassesOfDeclaringClass(IProgressMonitor pm) throws JavaModelException{ |
| if (getPossibleTargetClasses(pm).length == 0) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("PullUpRefactoring.not_this_type")); //$NON-NLS-1$ |
| return new RefactoringStatus(); |
| } |
| |
| private static boolean haveCommonDeclaringType(IMember[] members){ |
| if (members.length == 0) |
| return false; |
| IType declaringType= members[0].getDeclaringType(); |
| if (declaringType == null) |
| return false; |
| for (int i= 0; i < members.length; i++) { |
| if (! declaringType.equals(members[i].getDeclaringType())) |
| return false; |
| } |
| return true; |
| } |
| |
| private RefactoringStatus checkFinalFields(IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask("", fMembersToPullUp.length); //$NON-NLS-1$ |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| IMember member= fMembersToPullUp[i]; |
| if (member.getElementType() == IJavaElement.FIELD && ! JdtFlags.isStatic(member) && JdtFlags.isFinal(member)){ |
| Context context= JavaStatusContext.create(member); |
| result.addWarning(RefactoringCoreMessages.getString("PullUpRefactoring.final_fields"), context); //$NON-NLS-1$ |
| } |
| pm.worked(1); |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccesses(IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask(RefactoringCoreMessages.getString("PullUpRefactoring.checking_referenced_elements"), 3); //$NON-NLS-1$ |
| result.merge(checkAccessedTypes(new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedFields(new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedMethods(new SubProgressMonitor(pm, 1))); |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedTypes(IProgressMonitor pm) throws JavaModelException{ |
| RefactoringStatus result= new RefactoringStatus(); |
| IType[] accessedTypes= getTypeReferencedInPulledUpMembers(pm); |
| IType targetClass= getTargetClass(); |
| for (int i= 0; i < accessedTypes.length; i++) { |
| IType iType= accessedTypes[i]; |
| if (! iType.exists()) |
| continue; |
| |
| if (! canBeAccessedFrom(iType, targetClass)){ |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.type_not_accessible", //$NON-NLS-1$ |
| new String[]{createTypeLabel(iType), createTypeLabel(targetClass)}); |
| result.addError(message, JavaStatusContext.create(iType)); |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedFields(IProgressMonitor pm) throws JavaModelException{ |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| List pulledUpList= Arrays.asList(fMembersToPullUp); |
| List deletedList= Arrays.asList(getMembersToDelete(new SubProgressMonitor(pm, 1))); |
| IField[] accessedFields= ReferenceFinderUtil.getFieldsReferencedIn(fMembersToPullUp, new SubProgressMonitor(pm, 1)); |
| |
| IType targetClass= getTargetClass(); |
| for (int i= 0; i < accessedFields.length; i++) { |
| IField field= accessedFields[i]; |
| if (! field.exists()) |
| continue; |
| |
| boolean isAccessible= pulledUpList.contains(field) || |
| deletedList.contains(field) || |
| canBeAccessedFrom(field, targetClass); |
| if (! isAccessible){ |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.field_not_accessible", //$NON-NLS-1$ |
| new String[]{createFieldLabel(field), createTypeLabel(targetClass)}); |
| result.addError(message, JavaStatusContext.create(field)); |
| } else if (isDeclaredInSkippedSuperclass(field, new NullProgressMonitor())){ |
| String[] keys= {createFieldLabel(field), createTypeLabel(targetClass)}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.field_cannot_be_accessed", keys); //$NON-NLS-1$ |
| result.addError(message, JavaStatusContext.create(field)); |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedMethods(IProgressMonitor pm) throws JavaModelException{ |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| List pulledUpList= Arrays.asList(fMembersToPullUp); |
| List declaredAbstractList= Arrays.asList(fMethodsToDeclareAbstract); |
| List deletedList= Arrays.asList(getMembersToDelete(new SubProgressMonitor(pm, 1))); |
| IMethod[] accessedMethods= ReferenceFinderUtil.getMethodsReferencedIn(fMembersToPullUp, new SubProgressMonitor(pm, 1)); |
| |
| IType targetClass= getTargetClass(); |
| for (int i= 0; i < accessedMethods.length; i++) { |
| IMethod method= accessedMethods[i]; |
| if (! method.exists()) |
| continue; |
| boolean isAccessible= pulledUpList.contains(method) || |
| deletedList.contains(method) || |
| declaredAbstractList.contains(method) || |
| canBeAccessedFrom(method, targetClass); |
| if (! isAccessible){ |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.method_not_accessible", //$NON-NLS-1$ |
| new String[]{createMethodLabel(method), createTypeLabel(targetClass)}); |
| result.addError(message, JavaStatusContext.create(method)); |
| } else if (isDeclaredInSkippedSuperclass(method, new NullProgressMonitor())){ |
| String[] keys= {createMethodLabel(method), createTypeLabel(targetClass)}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.method_cannot_be_accessed", keys); //$NON-NLS-1$ |
| result.addError(message, JavaStatusContext.create(method)); |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private boolean isDeclaredInSkippedSuperclass(IMember member, IProgressMonitor pm) throws JavaModelException { |
| return getSkippedSuperclasses(pm).contains(member.getDeclaringType()); |
| } |
| |
| //skipped superclasses are those declared in the hierarchy between the declaring type of the selected members |
| //and the target type |
| private Set getSkippedSuperclasses(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| try { |
| if (fCachedSkippedSuperclasses != null && getTypeHierarchyOfTargetClass(new SubProgressMonitor(pm, 1)).getType().equals(getTargetClass())) |
| return fCachedSkippedSuperclasses; |
| ITypeHierarchy hierarchy= getTypeHierarchyOfTargetClass(new SubProgressMonitor(pm, 1)); |
| fCachedSkippedSuperclasses= new HashSet(2); |
| IType current= hierarchy.getSuperclass(getDeclaringType()); |
| while(current != null && ! current.equals(getTargetClass())){ |
| fCachedSkippedSuperclasses.add(current); |
| current= hierarchy.getSuperclass(current); |
| } |
| return fCachedSkippedSuperclasses; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| 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 JavaModelUtil.isSamePackage(((IType)member).getPackageFragment(), newHome.getPackageFragment()); |
| } else { |
| IType enclosingType= member.getDeclaringType(); |
| |
| if (! canBeAccessedFrom(enclosingType, newHome)) |
| return false; |
| |
| if (enclosingType.equals(getDeclaringType())) //cannot reach down the hierachy |
| return false; |
| |
| if (JdtFlags.isPublic(member)) |
| return true; |
| |
| //FIX ME: protected and default treated the same |
| return JavaModelUtil.isSamePackage(enclosingType.getPackageFragment(), newHome.getPackageFragment()); |
| } |
| } |
| |
| private RefactoringStatus checkMembersInSubclasses(IProgressMonitor pm) throws JavaModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| Set notDeletedMembers= getNotDeletedMembers(new SubProgressMonitor(pm, 1)); |
| checkAccessModifiers(result, notDeletedMembers); |
| checkMethodReturnTypes(new SubProgressMonitor(pm, 1), result, notDeletedMembers); |
| checkFieldTypes(new SubProgressMonitor(pm, 1), result); |
| pm.done(); |
| return result; |
| } |
| |
| private void checkMethodReturnTypes(IProgressMonitor pm, RefactoringStatus result, Set notDeletedMembers) throws JavaModelException { |
| Map mapping= getMatchingMemberMatching(pm, true); |
| IMember[] members= getMembersToBeCreatedInTargetClass(); |
| for (int i= 0; i < members.length; i++) { |
| if (members[i].getElementType() != IJavaElement.METHOD) |
| continue; |
| IMethod method= (IMethod)members[i]; |
| String returnType= getReturnTypeName(method); |
| Assert.isTrue(mapping.containsKey(method)); |
| for (Iterator iter= ((Set)mapping.get(method)).iterator(); iter.hasNext();) { |
| IMethod matchingMethod= (IMethod) iter.next(); |
| if (method.equals(matchingMethod)) |
| continue; |
| if (!notDeletedMembers.contains(matchingMethod)) |
| continue; |
| if (returnType.equals(getReturnTypeName(matchingMethod))) |
| continue; |
| String[] keys= {createMethodLabel(matchingMethod), createTypeLabel(matchingMethod.getDeclaringType())}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.different_method_return_type", //$NON-NLS-1$ |
| keys); |
| Context context= JavaStatusContext.create(matchingMethod.getCompilationUnit(), matchingMethod.getNameRange()); |
| result.addError(message, context); |
| } |
| } |
| |
| } |
| |
| private void checkFieldTypes(IProgressMonitor pm, RefactoringStatus result) throws JavaModelException { |
| Map mapping= getMatchingMemberMatching(pm, true); |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| if (fMembersToPullUp[i].getElementType() != IJavaElement.FIELD) |
| continue; |
| IField field= (IField)fMembersToPullUp[i]; |
| String type= getTypeName(field); |
| Assert.isTrue(mapping.containsKey(field)); |
| for (Iterator iter= ((Set)mapping.get(field)).iterator(); iter.hasNext();) { |
| IField matchingField= (IField) iter.next(); |
| if (field.equals(matchingField)) |
| continue; |
| if (type.equals(getTypeName(matchingField))) |
| continue; |
| String[] keys= {createFieldLabel(matchingField), createTypeLabel(matchingField.getDeclaringType())}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.different_field_type", //$NON-NLS-1$ |
| keys); |
| Context context= JavaStatusContext.create(matchingField.getCompilationUnit(), matchingField.getSourceRange()); |
| result.addError(message, context); |
| } |
| } |
| } |
| |
| private Map getMatchingMemberMatching(IProgressMonitor pm, boolean includeMethodsToDeclareAbstract) throws JavaModelException { |
| return getMatchingMembersMappingFromTypeAndAllSubtypes(getTypeHierarchyOfTargetClass(pm), getTargetClass(), includeMethodsToDeclareAbstract); |
| } |
| |
| private void checkAccessModifiers(RefactoringStatus result, Set notDeletedMembers) throws JavaModelException { |
| List toDeclareAbstract= Arrays.asList(fMethodsToDeclareAbstract); |
| for (Iterator iter= notDeletedMembers.iterator(); iter.hasNext();) { |
| IMember member= (IMember) iter.next(); |
| |
| if (member.getElementType() == IJavaElement.METHOD && ! toDeclareAbstract.contains(member)) |
| checkMethodAccessModifiers(result, ((IMethod) member)); |
| } |
| } |
| |
| private void checkMethodAccessModifiers(RefactoringStatus result, IMethod method) throws JavaModelException { |
| Context errorContext= JavaStatusContext.create(method); |
| |
| if (JdtFlags.isStatic(method)){ |
| String[] keys= {createMethodLabel(method), createTypeLabel(method.getDeclaringType())}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.static_method", //$NON-NLS-1$ |
| keys); |
| result.addError(message, errorContext); |
| } |
| if (isVisibilityLowerThanProtected(method)){ |
| String[] keys= {createMethodLabel(method), createTypeLabel(method.getDeclaringType())}; |
| String message= RefactoringCoreMessages.getFormattedString("PullUpRefactoring.lower_visibility", //$NON-NLS-1$ |
| keys); |
| result.addError(message, errorContext); |
| } |
| } |
| |
| private static String getReturnTypeName(IMethod method) throws JavaModelException { |
| return Signature.toString(Signature.getReturnType(method.getSignature()).toString()); |
| } |
| |
| private static String getTypeName(IField field) throws JavaModelException { |
| return Signature.toString(field.getTypeSignature()); |
| } |
| |
| private Set getNotDeletedMembers(IProgressMonitor pm) throws JavaModelException { |
| Set matchingSet= new HashSet(); |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| matchingSet.addAll(Arrays.asList(getMatchingElements(new SubProgressMonitor(pm, 1), true))); |
| matchingSet.removeAll(Arrays.asList(getMembersToDelete(new SubProgressMonitor(pm, 1)))); |
| pm.done(); |
| return matchingSet; |
| } |
| |
| private IFile[] getAllFilesToModify() throws CoreException{ |
| return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); |
| } |
| |
| private RefactoringStatus validateModifiesFiles() throws CoreException{ |
| return Checks.validateModifiesFiles(getAllFilesToModify()); |
| } |
| |
| private static boolean isVisibilityLowerThanProtected(IMember member)throws JavaModelException { |
| return ! JdtFlags.isPublic(member) && ! JdtFlags.isProtected(member); |
| } |
| |
| //--- change creation |
| |
| /* |
| * @see IRefactoring#createChange(IProgressMonitor) |
| */ |
| public IChange createChange(IProgressMonitor pm) throws JavaModelException { |
| try{ |
| return new CompositeChange(RefactoringCoreMessages.getString("PullUpRefactoring.Pull_Up"), fChangeManager.getAllChanges()); //$NON-NLS-1$ |
| } finally{ |
| pm.done(); |
| clearCaches(); |
| } |
| } |
| |
| private TextChangeManager createChangeManager(IProgressMonitor pm) throws CoreException{ |
| try{ |
| pm.beginTask(RefactoringCoreMessages.getString("PullUpRefactoring.preview"), 4); //$NON-NLS-1$ |
| |
| ICompilationUnit declaringCu= getDeclaringWorkingCopy(); |
| CompilationUnit declaringCuNode= AST.parseCompilationUnit(declaringCu, true); |
| |
| addImportsToTargetCu(new SubProgressMonitor(pm, 1), declaringCuNode); |
| |
| TextChangeManager manager= new TextChangeManager(); |
| |
| //ICompilationUnit -> List of IMember |
| Map membersToDeleteMap= createMembersToDeleteMap(new SubProgressMonitor(pm, 1)); |
| |
| //ICompilationUnit -> List of IType |
| Map nonAbstractSubclassesMap= createNonAbstractSubclassesMapping(new SubProgressMonitor(pm, 1)); |
| |
| ICompilationUnit[] cus= getCompilationUnitsOfSubclassesOfTargetClass(new SubProgressMonitor(pm, 1)); |
| IProgressMonitor subPm= new SubProgressMonitor(pm, 1); |
| subPm.beginTask("", cus.length*6); |
| ICompilationUnit targetCu= getTargetWorkingCopy(); |
| for (int i= 0; i < cus.length; i++) { |
| ICompilationUnit cu= cus[i]; |
| if (! needsRewriting(cu, membersToDeleteMap, nonAbstractSubclassesMap)) { |
| subPm.worked(6); |
| continue; |
| } |
| CompilationUnit cuNode= cu.equals(declaringCu) ? declaringCuNode : AST.parseCompilationUnit(cu, true); |
| ASTRewrite rewrite= new ASTRewrite(cuNode); |
| if (membersToDeleteMap.containsKey(cu)){ |
| List members= (List) membersToDeleteMap.get(cu); |
| deleteDeclarationNodes(cuNode, rewrite, members); |
| } |
| if (cu.equals(targetCu)){ |
| if (shouldMakeTargetClassAbstract()) |
| makeTargetClassAbstract(rewrite, cuNode); |
| copyMembersToTargetClass(declaringCuNode, cuNode, rewrite, new SubProgressMonitor(subPm, 1)); |
| createAbstractMethodsInTargetClass(cuNode, declaringCuNode, rewrite, new SubProgressMonitor(subPm, 1)); |
| } else{ |
| subPm.worked(2); |
| } |
| if (cu.equals(declaringCu)){ |
| increaseVisibilityOfMethodsDeclaredAbstract(rewrite, cuNode, new SubProgressMonitor(subPm, 2)); |
| } else{ |
| subPm.worked(2); |
| } |
| if (nonAbstractSubclassesMap.containsKey(cu)){ |
| List subclasses= (List)nonAbstractSubclassesMap.get(cu); |
| addMethodStubsToNonAbstractSubclassesOfTargetClass(subclasses, cuNode, declaringCuNode, rewrite, new SubProgressMonitor(subPm, 2)); |
| } |
| addTextEditFromRewrite(manager, cu, rewrite); |
| } |
| subPm.done(); |
| return manager; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| /* |
| * members: List<IMember> |
| */ |
| private static void deleteDeclarationNodes(CompilationUnit cuNode, ASTRewrite rewrite, List members) throws JavaModelException { |
| List declarationNodes= getDeclarationNodes(cuNode, members); |
| for (Iterator iter= declarationNodes.iterator(); iter.hasNext();) { |
| ASTNode node= (ASTNode) iter.next(); |
| if (node instanceof VariableDeclarationFragment){ |
| if (node.getParent() instanceof FieldDeclaration){ |
| FieldDeclaration fd= (FieldDeclaration)node.getParent(); |
| if (areAllFragmentsDeleted(fd, declarationNodes)) |
| rewrite.markAsRemoved(fd); |
| else |
| rewrite.markAsRemoved(node); |
| } |
| } else { |
| rewrite.markAsRemoved(node); |
| } |
| } |
| } |
| |
| private static boolean areAllFragmentsDeleted(FieldDeclaration fd, List declarationNodes) { |
| for (Iterator iter= fd.fragments().iterator(); iter.hasNext();) { |
| if (! declarationNodes.contains(iter.next())) |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * return List<ASTNode> |
| * members List<IMember> |
| */ |
| private static List getDeclarationNodes(CompilationUnit cuNode, List members) throws JavaModelException { |
| List result= new ArrayList(members.size()); |
| for (Iterator iter= members.iterator(); iter.hasNext(); ) { |
| IMember member= (IMember)iter.next(); |
| ASTNode declarationNode= null; |
| if (member instanceof IField) |
| declarationNode= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField)member, cuNode); |
| else if (member instanceof IType) |
| declarationNode= ASTNodeSearchUtil.getTypeDeclarationNode((IType)member, cuNode); |
| else if (member instanceof IMethod) |
| declarationNode= ASTNodeSearchUtil.getMethodDeclarationNode((IMethod)member, cuNode); |
| if (declarationNode != null) |
| result.add(declarationNode); |
| } |
| return result; |
| } |
| |
| //ICompilationUnit -> List of IMember |
| private Map createMembersToDeleteMap(IProgressMonitor pm) throws JavaModelException { |
| IMember[] membersToDelete= getMembersToDelete(pm); |
| Map result= new HashMap(); |
| for (int i= 0; i < membersToDelete.length; i++) { |
| IMember member= WorkingCopyUtil.getWorkingCopyIfExists(membersToDelete[i]); |
| ICompilationUnit cu= member.getCompilationUnit(); |
| if (! result.containsKey(cu)) |
| result.put(cu, new ArrayList(1)); |
| ((List)result.get(cu)).add(member); |
| } |
| return result; |
| } |
| |
| private boolean needsRewriting(ICompilationUnit unit, Map membersToDeleteMap, Map nonAbstractSubclassesMap) throws JavaModelException { |
| return getDeclaringWorkingCopy().equals(unit) || |
| getTargetWorkingCopy().equals(unit) || |
| membersToDeleteMap.containsKey(unit) || |
| nonAbstractSubclassesMap.containsKey(unit); |
| } |
| |
| private void addTextEditFromRewrite(TextChangeManager manager, ICompilationUnit cu, ASTRewrite rewrite) throws CoreException { |
| TextBuffer textBuffer= TextBuffer.create(cu.getBuffer().getContents()); |
| TextEdit resultingEdits= new MultiTextEdit(); |
| rewrite.rewriteNode(textBuffer, resultingEdits); |
| |
| TextChange textChange= manager.get(cu); |
| if (fImportManager.hasImportEditFor(cu)) |
| resultingEdits.addChild(fImportManager.getImportRewrite(cu).createEdit(textBuffer)); |
| textChange.addTextEdit(RefactoringCoreMessages.getString("PullUpRefactoring.42"), resultingEdits); //$NON-NLS-1$ |
| rewrite.removeModifications(); |
| } |
| |
| private ICompilationUnit[] getCompilationUnitsOfSubclassesOfTargetClass(IProgressMonitor pm) throws JavaModelException { |
| IType[] allSubtypes= getTypeHierarchyOfTargetClass(pm).getAllSubtypes(getTargetClass()); |
| Set result= new HashSet(allSubtypes.length); |
| for (int i= 0; i < allSubtypes.length; i++) { |
| result.add(WorkingCopyUtil.getWorkingCopyIfExists(allSubtypes[i].getCompilationUnit())); |
| } |
| result.add(getTargetWorkingCopy()); |
| return (ICompilationUnit[]) result.toArray(new ICompilationUnit[result.size()]); |
| } |
| |
| private void increaseVisibilityOfMethodsDeclaredAbstract(ASTRewrite rewrite, CompilationUnit cuNode, IProgressMonitor pm) throws JavaModelException { |
| for (int i= 0; i < fMethodsToDeclareAbstract.length; i++) { |
| IMethod method= fMethodsToDeclareAbstract[i]; |
| if (! needsToChangeVisibility(method, pm, false)) |
| continue; |
| MethodDeclaration methodDeclaration= ASTNodeSearchUtil.getMethodDeclarationNode(method, cuNode);//getMethodDeclarationNode(method); |
| |
| int newModifiers= getModifiersWithUpdatedVisibility(method, methodDeclaration.getModifiers(), pm, false); |
| rewrite.markAsReplaced(methodDeclaration, ASTNodeConstants.MODIFIERS, new Integer(newModifiers), null); |
| } |
| } |
| |
| private void createAbstractMethodsInTargetClass(CompilationUnit targetCuNode, CompilationUnit declaringCuNode, ASTRewrite rewrite, IProgressMonitor pm) throws JavaModelException { |
| TypeDeclaration targetClass= ASTNodeSearchUtil.getTypeDeclarationNode(getTargetClass(), targetCuNode); |
| pm.beginTask("", fMethodsToDeclareAbstract.length); |
| for (int i= 0; i < fMethodsToDeclareAbstract.length; i++) { |
| createAbstractMethodInTargetClass(fMethodsToDeclareAbstract[i], declaringCuNode, rewrite, targetClass, new SubProgressMonitor(pm, 1)); |
| } |
| pm.done(); |
| } |
| |
| private void createAbstractMethodInTargetClass(IMethod sourceMethod, CompilationUnit declaringCuNode, ASTRewrite rewrite, TypeDeclaration targetClass, IProgressMonitor pm) throws JavaModelException { |
| MethodDeclaration methodDeclaration= ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, declaringCuNode); |
| AST ast= getAST(rewrite); |
| MethodDeclaration newMethod= ast.newMethodDeclaration(); |
| newMethod.setBody(null); |
| newMethod.setConstructor(false); |
| newMethod.setExtraDimensions(methodDeclaration.getExtraDimensions()); |
| newMethod.setJavadoc(null); |
| newMethod.setModifiers(createModifiersForAbstractDeclaration(sourceMethod, pm)); |
| newMethod.setName(createCopyOfSimpleName(methodDeclaration.getName(), ast)); |
| copyReturnType(rewrite, getDeclaringWorkingCopy(), methodDeclaration, newMethod); |
| copyParameters(rewrite, getDeclaringWorkingCopy(), methodDeclaration, newMethod); |
| copyThrownExceptions(methodDeclaration, newMethod); |
| targetClass.bodyDeclarations().add(newMethod); |
| rewrite.markAsInserted(newMethod); |
| } |
| |
| private int createModifiersForAbstractDeclaration(IMethod method, IProgressMonitor pm) throws JavaModelException { |
| int modifiers= Modifier.ABSTRACT | JdtFlags.clearFlag(Modifier.NATIVE | Modifier.FINAL, method.getFlags()); |
| return getModifiersWithUpdatedVisibility(method, modifiers, pm, false); |
| } |
| |
| private int getModifiersWithUpdatedVisibility(IMember member, int modifiers, IProgressMonitor pm, boolean considerReferences) throws JavaModelException{ |
| if (needsToChangeVisibility(member, pm, considerReferences)) |
| return JdtFlags.clearAccessModifiers(modifiers) | Modifier.PROTECTED; |
| else |
| return modifiers; |
| } |
| |
| //concreteSubclasses: List of IType |
| private void addMethodStubsToNonAbstractSubclassesOfTargetClass(List concreteSubclasses, CompilationUnit cuNode, CompilationUnit declaringCuNode, ASTRewrite rewrite, IProgressMonitor pm) throws CoreException { |
| IType declaringType= getDeclaringType(); |
| IMethod[] methods= getAbstractMethodsAddedToTargetClass(); |
| IType[] typesToImport= getTypesReferencedInDeclarations(methods, declaringCuNode); |
| pm.beginTask("", concreteSubclasses.size()); |
| for (Iterator iter= concreteSubclasses.iterator(); iter.hasNext();) { |
| IType clazz= (IType)iter.next(); |
| if (clazz.equals(declaringType)) |
| continue; |
| boolean anyStubAdded= false; |
| TypeDeclaration classToCreateStubIn= ASTNodeSearchUtil.getTypeDeclarationNode(clazz, cuNode); |
| ICompilationUnit cuToCreateStubIn= clazz.getCompilationUnit(); |
| IProgressMonitor subPm= new SubProgressMonitor(pm, 1); |
| subPm.beginTask("", methods.length); |
| for (int j= 0; j < methods.length; j++) { |
| IMethod method= methods[j]; |
| if (null == JavaModelUtil.findMethod(method.getElementName(), method.getParameterTypes(), method.isConstructor(), clazz)){ |
| addStub(method, declaringCuNode, classToCreateStubIn, cuToCreateStubIn, rewrite, new SubProgressMonitor(subPm, 1)); |
| anyStubAdded= true; |
| } |
| } |
| subPm.done(); |
| if (anyStubAdded) |
| addImports(typesToImport, WorkingCopyUtil.getWorkingCopyIfExists(clazz.getCompilationUnit())); |
| } |
| pm.done(); |
| } |
| |
| private void addStub(IMethod sourceMethod, CompilationUnit declaringCuNode, TypeDeclaration typeToCreateStubIn, ICompilationUnit newCu, ASTRewrite rewrite, IProgressMonitor pm) throws CoreException { |
| MethodDeclaration methodToCreateStubFor= ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, declaringCuNode); |
| AST ast= getAST(rewrite); |
| MethodDeclaration newMethod= ast.newMethodDeclaration(); |
| newMethod.setBody(getMethodStubBody(methodToCreateStubFor, ast)); |
| newMethod.setConstructor(false); |
| newMethod.setExtraDimensions(methodToCreateStubFor.getExtraDimensions()); |
| newMethod.setModifiers(createModifiersForMethodStubs(sourceMethod, methodToCreateStubFor, pm)); |
| newMethod.setName(createCopyOfSimpleName(methodToCreateStubFor.getName(), ast)); |
| copyReturnType(rewrite, getDeclaringWorkingCopy(), methodToCreateStubFor, newMethod); |
| copyParameters(rewrite, getDeclaringWorkingCopy(), methodToCreateStubFor, newMethod); |
| copyThrownExceptions(methodToCreateStubFor, newMethod); |
| newMethod.setJavadoc(createJavadocForStub(typeToCreateStubIn.getName().getIdentifier(), methodToCreateStubFor, newMethod, newCu, rewrite)); |
| typeToCreateStubIn.bodyDeclarations().add(newMethod); |
| rewrite.markAsInserted(newMethod); |
| } |
| |
| private static Block getMethodStubBody(MethodDeclaration method, AST ast) { |
| Block body= ast.newBlock(); |
| Type returnType= method.getReturnType(); |
| |
| Expression expression= ASTNodeFactory.newDefaultExpression(ast, returnType, method.getExtraDimensions()); |
| if (expression != null) { |
| ReturnStatement returnStatement= ast.newReturnStatement(); |
| returnStatement.setExpression(expression); |
| body.statements().add(returnStatement); |
| } |
| return body; |
| } |
| |
| private Javadoc createJavadocForStub(String enclosingTypeName, MethodDeclaration oldMethod, MethodDeclaration newMethodNode, ICompilationUnit cu, ASTRewrite rewrite) throws CoreException { |
| if (! fPreferenceSettings.createComments) |
| return null; |
| IMethodBinding binding= oldMethod.resolveBinding(); |
| if (binding == null) |
| return null; |
| ITypeBinding[] params= binding.getParameterTypes(); |
| String fullTypeName= JavaModelUtil.getFullyQualifiedName(getTargetClass()); |
| String[] fullParamNames= new String[params.length]; |
| for (int i= 0; i < fullParamNames.length; i++) { |
| fullParamNames[i]= Bindings.getFullyQualifiedName(params[i]); |
| } |
| String comment= StubUtility.getMethodComment(cu, enclosingTypeName, newMethodNode, true, false, fullTypeName, fullParamNames, String.valueOf('\n')); |
| return (Javadoc) rewrite.createPlaceholder(comment, ASTRewrite.JAVADOC); |
| } |
| |
| private int createModifiersForMethodStubs(IMethod method, MethodDeclaration methodDeclaration, IProgressMonitor pm) throws JavaModelException { |
| int modifiers= JdtFlags.clearFlag(Modifier.NATIVE | Modifier.ABSTRACT, methodDeclaration.getModifiers()); |
| return getModifiersWithUpdatedVisibility(method, modifiers, pm, false); |
| } |
| |
| //ICompilationUnit -> List of IType |
| private Map createNonAbstractSubclassesMapping(IProgressMonitor pm) throws JavaModelException{ |
| if (! shouldAddMethodStubsToConcreteSubclassesOfTargetClass()) |
| return new HashMap(0); |
| Set nonAbstractSubclasses= getNonAbstractSubclasses(getTypeHierarchyOfTargetClass(pm), getTargetClass()); |
| Map result= new HashMap(); |
| for (Iterator iter= nonAbstractSubclasses.iterator(); iter.hasNext();) { |
| IType type= (IType)iter.next(); |
| ICompilationUnit cu= WorkingCopyUtil.getWorkingCopyIfExists(type.getCompilationUnit()); |
| if (! result.containsKey(cu)) |
| result.put(cu, new ArrayList(1)); |
| ((List)result.get(cu)).add(type); |
| } |
| return result; |
| } |
| |
| //Set of IType |
| private static Set getNonAbstractSubclasses(ITypeHierarchy hierarchy, IType clazz) throws JavaModelException { |
| IType[] subclasses= hierarchy.getSubclasses(clazz); |
| Set result= new HashSet(); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType subclass= subclasses[i]; |
| if (JdtFlags.isAbstract(subclass)) |
| result.addAll(getNonAbstractSubclasses(hierarchy, subclass)); |
| else |
| result.add(subclass); |
| } |
| return result; |
| } |
| |
| private boolean shouldAddMethodStubsToConcreteSubclassesOfTargetClass() throws JavaModelException { |
| return fCreateMethodStubs && getAbstractMethodsAddedToTargetClass().length > 0; |
| } |
| |
| private void makeTargetClassAbstract(ASTRewrite rewrite, CompilationUnit cuNode) throws JavaModelException { |
| TypeDeclaration targetClass= ASTNodeSearchUtil.getTypeDeclarationNode(getTargetClass(), cuNode); |
| |
| int newModifiers= targetClass.getModifiers() | Modifier.ABSTRACT; |
| rewrite.markAsReplaced(targetClass, ASTNodeConstants.MODIFIERS, new Integer(newModifiers), null); |
| } |
| |
| private boolean shouldMakeTargetClassAbstract() throws JavaModelException { |
| return ! JdtFlags.isAbstract(getTargetClass()) && |
| getAbstractMethodsAddedToTargetClass().length > 0; |
| } |
| |
| private IMember[] getMembersToBeCreatedInTargetClass(){ |
| List result= new ArrayList(fMembersToPullUp.length + fMethodsToDeclareAbstract.length); |
| result.addAll(Arrays.asList(fMembersToPullUp)); |
| result.addAll(Arrays.asList(fMethodsToDeclareAbstract)); |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } |
| |
| private IMethod[] getAbstractMethodsAddedToTargetClass() throws JavaModelException{ |
| IMethod[] toDeclareAbstract= fMethodsToDeclareAbstract; |
| IMethod[] abstractPulledUp= getAbstractMethodsToPullUp(); |
| List result= new ArrayList(toDeclareAbstract.length + abstractPulledUp.length); |
| result.addAll(Arrays.asList(toDeclareAbstract)); |
| result.addAll(Arrays.asList(abstractPulledUp)); |
| return (IMethod[]) result.toArray(new IMethod[result.size()]); |
| } |
| |
| private IMethod[] getAbstractMethodsToPullUp() throws JavaModelException { |
| List result= new ArrayList(fMembersToPullUp.length); |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| IMember member= fMembersToPullUp[i]; |
| if (member instanceof IMethod && JdtFlags.isAbstract(member)) |
| result.add(member); |
| } |
| return (IMethod[]) result.toArray(new IMethod[result.size()]); |
| } |
| |
| private IType getSuperclassOfDeclaringClass(IProgressMonitor pm) throws JavaModelException { |
| IType declaringType= getDeclaringType(); |
| return declaringType.newSupertypeHierarchy(pm).getSuperclass(declaringType); |
| } |
| |
| private void copyMembersToTargetClass(CompilationUnit declaringCuNode, CompilationUnit targetCuNode, ASTRewrite rewrite, IProgressMonitor pm) throws JavaModelException { |
| TypeDeclaration targetClass= ASTNodeSearchUtil.getTypeDeclarationNode(getTargetClass(), targetCuNode); |
| fMembersToPullUp= JavaElementUtil.sortByOffset(fMembersToPullUp); |
| pm.beginTask("", fMembersToPullUp.length); //$NON-NLS-1$ |
| for (int i = fMembersToPullUp.length - 1; i >= 0; i--) { //backwards - to preserve method order |
| IMember member= fMembersToPullUp[i]; |
| if (member instanceof IField) |
| copyFieldToTargetClass((IField)member, declaringCuNode, targetClass, rewrite, new SubProgressMonitor(pm, 1)); |
| else if (member instanceof IMethod) |
| copyMethodToTargetClass((IMethod)member, declaringCuNode, targetClass, rewrite, new SubProgressMonitor(pm, 1)); |
| else if (member instanceof IType) |
| copyTypeToTargetClass((IType)member, declaringCuNode, targetClass, rewrite); |
| else |
| Assert.isTrue(false); |
| pm.worked(1); |
| } |
| pm.done(); |
| } |
| |
| private void copyTypeToTargetClass(IType type, CompilationUnit declaringCuNode, TypeDeclaration targetClass, ASTRewrite rewrite) throws JavaModelException { |
| BodyDeclaration newType= createNewTypeDeclarationNode(type, declaringCuNode, rewrite); |
| rewrite.markAsInserted(newType); |
| targetClass.bodyDeclarations().add(newType); |
| } |
| |
| private BodyDeclaration createNewTypeDeclarationNode(IType type, CompilationUnit declaringCuNode, ASTRewrite rewrite) throws JavaModelException { |
| TypeDeclaration oldType= ASTNodeSearchUtil.getTypeDeclarationNode(type, declaringCuNode); |
| return createPlaceholderForTypeDeclaration(oldType, getDeclaringWorkingCopy(), rewrite, true); |
| } |
| |
| private void copyMethodToTargetClass(IMethod method, CompilationUnit declaringCuNode, TypeDeclaration targetClass, ASTRewrite targetRewrite, IProgressMonitor pm) throws JavaModelException { |
| MethodDeclaration newMethod= createNewMethodDeclarationNode(method, declaringCuNode, targetRewrite, pm); |
| targetRewrite.markAsInserted(newMethod); |
| targetClass.bodyDeclarations().add(newMethod); |
| } |
| |
| private MethodDeclaration createNewMethodDeclarationNode(IMethod sourceMethod, CompilationUnit declaringCuNode, ASTRewrite targetRewrite, IProgressMonitor pm) throws JavaModelException { |
| MethodDeclaration sourceMethodNode= ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, declaringCuNode); |
| AST ast= getAST(targetRewrite); |
| MethodDeclaration newMethod= ast.newMethodDeclaration(); |
| copyBodyOfPulledUpMethod(targetRewrite, sourceMethod, sourceMethodNode, newMethod, pm); |
| newMethod.setConstructor(sourceMethodNode.isConstructor()); |
| newMethod.setExtraDimensions(sourceMethodNode.getExtraDimensions()); |
| copyJavadocNode(targetRewrite, sourceMethod, sourceMethodNode, newMethod); |
| newMethod.setModifiers(getNewModifiers(sourceMethod, pm, true)); |
| newMethod.setName(createCopyOfSimpleName(sourceMethodNode.getName(), ast)); |
| copyReturnType(targetRewrite, getDeclaringWorkingCopy(), sourceMethodNode, newMethod); |
| copyParameters(targetRewrite, getDeclaringWorkingCopy(), sourceMethodNode, newMethod); |
| copyThrownExceptions(sourceMethodNode, newMethod); |
| return newMethod; |
| } |
| |
| private void copyBodyOfPulledUpMethod(ASTRewrite targetRewrite, IMethod method, MethodDeclaration oldMethod, MethodDeclaration newMethod, IProgressMonitor pm) throws JavaModelException { |
| if (oldMethod.getBody() == null){ |
| newMethod.setBody(null); |
| return; |
| } |
| Block oldBody= oldMethod.getBody(); |
| ISourceRange[] superRefOffsert= SourceRange.reverseSortByOffset(findSuperReferenceRanges(method, getSuperclassOfDeclaringClass(pm))); |
| |
| String oldBodySource= getBufferText(oldBody, getDeclaringWorkingCopy()); |
| StringBuffer newBodyCodeBuff= new StringBuffer(oldBodySource); |
| for (int i= 0; i < superRefOffsert.length; i++) { |
| ISourceRange range= superRefOffsert[i]; |
| int start= range.getOffset() - oldBody.getStartPosition(); |
| int end= start + range.getLength(); |
| newBodyCodeBuff.replace(start, end, "this"); //$NON-NLS-1$ |
| } |
| String newBodySource= newBodyCodeBuff.toString(); |
| String[] lines= Strings.convertIntoLines(newBodySource); |
| Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); |
| newBodySource= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(method)); |
| Block newBody= (Block)targetRewrite.createPlaceholder(newBodySource, ASTRewrite.BLOCK); |
| newMethod.setBody(newBody); |
| } |
| |
| private static ISourceRange[] findSuperReferenceRanges(IMethod method, IType superType) throws JavaModelException{ |
| Assert.isNotNull(method); |
| if (JdtFlags.isStatic(method)) |
| return new ISourceRange[0]; |
| SuperReferenceFinderVisitor visitor= new SuperReferenceFinderVisitor(method, superType); |
| AST.parseCompilationUnit(method.getCompilationUnit(), true).accept(visitor); |
| return visitor.getSuperReferenceRanges(); |
| } |
| |
| private static void copyThrownExceptions(MethodDeclaration oldMethod, MethodDeclaration newMethod) { |
| AST ast= newMethod.getAST(); |
| for (int i= 0, n= oldMethod.thrownExceptions().size(); i < n; i++) { |
| Name oldExceptionName= (Name)oldMethod.thrownExceptions().get(i); |
| newMethod.thrownExceptions().add(i, createCopyOfName(oldExceptionName, ast)); |
| } |
| } |
| |
| private static Name createCopyOfName(Name name, AST ast){ |
| return (Name)ASTNode.copySubtree(ast, name); |
| } |
| |
| private static SimpleName createCopyOfSimpleName(SimpleName simpleName, AST ast){ |
| return (SimpleName)ASTNode.copySubtree(ast, simpleName); |
| } |
| |
| private static void copyParameters(ASTRewrite targetRewrite, ICompilationUnit cu, 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, cu, targetRewrite); |
| newMethod.parameters().add(i, newParam); |
| } |
| } |
| |
| private static void copyReturnType(ASTRewrite targetRewrite, ICompilationUnit sourceCu, MethodDeclaration oldMethod, MethodDeclaration newMethod) throws JavaModelException { |
| Type newReturnType= createPlaceholderForType(oldMethod.getReturnType(), sourceCu, targetRewrite); |
| newMethod.setReturnType(newReturnType); |
| } |
| |
| 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 void copyFieldToTargetClass(IField field, CompilationUnit declaringCuNode, TypeDeclaration targetClass, ASTRewrite rewrite, IProgressMonitor pm) throws JavaModelException { |
| FieldDeclaration newField= createNewFieldDeclarationNode(field, declaringCuNode, rewrite, pm); |
| rewrite.markAsInserted(newField); |
| targetClass.bodyDeclarations().add(newField); |
| } |
| |
| private FieldDeclaration createNewFieldDeclarationNode(IField field, CompilationUnit declaringCuNode, ASTRewrite rewrite, IProgressMonitor pm) throws JavaModelException { |
| AST ast= getAST(rewrite); |
| VariableDeclarationFragment oldFieldFragment= ASTNodeSearchUtil.getFieldDeclarationFragmentNode(field, declaringCuNode); |
| VariableDeclarationFragment newFragment= ast.newVariableDeclarationFragment(); |
| newFragment.setExtraDimensions(oldFieldFragment.getExtraDimensions()); |
| if (oldFieldFragment.getInitializer() != null){ |
| Expression newInitializer= createPlaceholderForExpression(oldFieldFragment.getInitializer(), field.getCompilationUnit(), rewrite); |
| newFragment.setInitializer(newInitializer); |
| } |
| newFragment.setName(createCopyOfSimpleName(oldFieldFragment.getName(), ast)); |
| FieldDeclaration newField= ast.newFieldDeclaration(newFragment); |
| FieldDeclaration oldField= ASTNodeSearchUtil.getFieldDeclarationNode(field, declaringCuNode); |
| copyJavadocNode(rewrite, field, oldField, newField); |
| newField.setModifiers(getNewModifiers(field, pm, true)); |
| |
| Type newType= createPlaceholderForType(oldField.getType(), field.getCompilationUnit(), rewrite); |
| newField.setType(newType); |
| return newField; |
| } |
| |
| private boolean needsToChangeVisibility(IMember member, IProgressMonitor pm, boolean considerReferences) throws JavaModelException { |
| if (JdtFlags.isPublic(member) || JdtFlags.isProtected(member)) |
| return false; |
| if (! considerReferences) |
| return true; |
| return isReferencedBySomethingElseThanMembersToPull(member, pm); |
| } |
| |
| private boolean isReferencedBySomethingElseThanMembersToPull(IMember member, IProgressMonitor pm) throws JavaModelException { |
| SearchResultGroup[] references= getReferences(member, pm); |
| if (references.length == 0) |
| return false; |
| if (references.length > 1) |
| return true; |
| ICompilationUnit referencingCu= WorkingCopyUtil.getWorkingCopyIfExists(references[0].getCompilationUnit()); |
| if (! getDeclaringWorkingCopy().equals(referencingCu)) |
| return true; |
| SearchResult[] searchResults= references[0].getSearchResults(); |
| for (int i= 0; i < searchResults.length; i++) { |
| if (! isWithinMemberToPullUp(searchResults[i])) |
| return true; |
| } |
| return false; |
| } |
| |
| private SearchResultGroup[] getReferences(IMember member, IProgressMonitor pm) throws JavaModelException { |
| if (! fCachedMembersReferences.containsKey(member)) { |
| IJavaSearchScope scope= RefactoringScopeFactory.create(member); |
| ISearchPattern pattern= SearchEngine.createSearchPattern(member, IJavaSearchConstants.REFERENCES); |
| fCachedMembersReferences.put(member, RefactoringSearchEngine.search(pm, scope, pattern)); |
| } |
| return (SearchResultGroup[])fCachedMembersReferences.get(member); |
| } |
| |
| private boolean isWithinMemberToPullUp(SearchResult result) throws JavaModelException { |
| int referenceStart= result.getStart(); |
| for (int i= 0; i < fMembersToPullUp.length; i++) { |
| if (liesWithin(fMembersToPullUp[i].getSourceRange(), referenceStart)) |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean liesWithin(ISourceRange range, int offset) { |
| return range.getOffset() <= offset && range.getOffset() + range.getLength() >= offset; |
| } |
| |
| private int getNewModifiers(IMember member, IProgressMonitor pm, boolean considerReferences) throws JavaModelException { |
| return getModifiersWithUpdatedVisibility(member, member.getFlags(), pm, considerReferences); |
| } |
| |
| private void addImportsToTargetCu(IProgressMonitor pm, CompilationUnit declaringCuNode) throws CoreException { |
| addImports(getTypesThatNeedToBeImportedInTargetCu(pm, declaringCuNode), getTargetWorkingCopy()); |
| } |
| |
| private void addImports(IType[] typesToImport, ICompilationUnit cu) throws CoreException { |
| for (int i= 0; i < typesToImport.length; i++) { |
| fImportManager.addImportTo(typesToImport[i], cu); |
| } |
| } |
| |
| private ICompilationUnit getDeclaringWorkingCopy() { |
| return WorkingCopyUtil.getWorkingCopyIfExists(getDeclaringType().getCompilationUnit()); |
| } |
| |
| private ICompilationUnit getTargetWorkingCopy() { |
| return WorkingCopyUtil.getWorkingCopyIfExists(getTargetClass().getCompilationUnit()); |
| } |
| |
| private IType[] getTypesThatNeedToBeImportedInTargetCu(IProgressMonitor pm, CompilationUnit declaringCuNode) throws JavaModelException { |
| if (getTargetWorkingCopy().equals(getDeclaringWorkingCopy())) |
| return new IType[0]; |
| IType[] typesInPulledUpMembers= getTypeReferencedInPulledUpMembers(pm); |
| IType[] typesInMethodsDeclaredAbstract= getTypesReferencedInDeclarations(fMethodsToDeclareAbstract, declaringCuNode); |
| List result= new ArrayList(typesInMethodsDeclaredAbstract.length + typesInPulledUpMembers.length); |
| result.addAll(Arrays.asList(typesInMethodsDeclaredAbstract)); |
| result.addAll(Arrays.asList(typesInPulledUpMembers)); |
| return (IType[]) result.toArray(new IType[result.size()]); |
| } |
| |
| private IType[] getTypeReferencedInPulledUpMembers(IProgressMonitor pm) throws JavaModelException { |
| if (fCachedTypesReferencedInPulledUpMembers == null) |
| fCachedTypesReferencedInPulledUpMembers= ReferenceFinderUtil.getTypesReferencedIn(fMembersToPullUp, pm); |
| return fCachedTypesReferencedInPulledUpMembers; |
| } |
| |
| private IType[] getTypesReferencedInDeclarations(IMethod[] methods, CompilationUnit declaringCuNode) throws JavaModelException{ |
| MethodDeclaration[] methodDeclarations= getMethodDeclarations(methods, declaringCuNode); |
| ITypeBinding[] referencedTypesBindings= ReferenceFinderUtil.getTypesReferencedInDeclarations(methodDeclarations); |
| List types= new ArrayList(referencedTypesBindings.length); |
| IJavaProject proj= getDeclaringType().getJavaProject(); |
| for (int i= 0; i < referencedTypesBindings.length; i++) { |
| ITypeBinding typeBinding= referencedTypesBindings[i]; |
| if (typeBinding == null) |
| continue; |
| if (typeBinding.isArray()) |
| typeBinding= typeBinding.getElementType(); |
| IType type= Bindings.findType(typeBinding, proj); |
| if (type != null) |
| types.add(type); |
| } |
| return (IType[]) types.toArray(new IType[types.size()]); |
| } |
| |
| private static MethodDeclaration[] getMethodDeclarations(IMethod[] methods, CompilationUnit cuNode) throws JavaModelException { |
| List result= new ArrayList(methods.length); |
| for (int i= 0; i < methods.length; i++) { |
| result.add(ASTNodeSearchUtil.getMethodDeclarationNode(methods[i], cuNode)); |
| } |
| return (MethodDeclaration[]) result.toArray(new MethodDeclaration[result.size()]); |
| } |
| |
| private static AST getAST(ASTRewrite rewrite){ |
| return rewrite.getAST(); |
| } |
| |
| private static String getBufferText(ASTNode node, ICompilationUnit cu) throws JavaModelException{ |
| return cu.getBuffer().getText(node.getStartPosition(), node.getLength()); |
| } |
| |
| private static String getNewText(ASTNode astNode, ICompilationUnit cu, boolean removeIndentation) throws JavaModelException { |
| String bufferText= getBufferText(astNode, cu); |
| if (removeIndentation) |
| return getUnindentedText(bufferText, cu); |
| else |
| return bufferText; |
| } |
| |
| private static String getUnindentedText(String bufferText, ICompilationUnit cu) throws JavaModelException { |
| String[] lines= Strings.convertIntoLines(bufferText); |
| Strings.trimIndentation(lines, CodeFormatterUtil.getTabWidth(), false); |
| return Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(cu)); |
| } |
| |
| //---- placeholder creators ---- |
| |
| private static Expression createPlaceholderForExpression(Expression expression, ICompilationUnit cu, ASTRewrite rewrite) throws JavaModelException{ |
| return (Expression)rewrite.createPlaceholder(getBufferText(expression, cu), ASTRewrite.EXPRESSION); |
| } |
| |
| private static SingleVariableDeclaration createPlaceholderForSingleVariableDeclaration(SingleVariableDeclaration declaration, ICompilationUnit cu, ASTRewrite rewrite) throws JavaModelException{ |
| return (SingleVariableDeclaration)rewrite.createPlaceholder(getBufferText(declaration, cu), ASTRewrite.SINGLEVAR_DECLARATION); |
| } |
| |
| private static Type createPlaceholderForType(Type type, ICompilationUnit cu, ASTRewrite rewrite) throws JavaModelException{ |
| return (Type)rewrite.createPlaceholder(getBufferText(type, cu), ASTRewrite.TYPE); |
| } |
| |
| private static BodyDeclaration createPlaceholderForTypeDeclaration(BodyDeclaration bodyDeclaration, ICompilationUnit cu, ASTRewrite rewrite, boolean removeIndentation) throws JavaModelException{ |
| String newBufferText= getNewText(bodyDeclaration, cu, removeIndentation); |
| return (BodyDeclaration)rewrite.createPlaceholder(newBufferText, ASTRewrite.TYPE_DECLARATION); |
| } |
| } |