blob: a0324943b95a74b696c628cfdb9f81b82c73227a [file] [log] [blame]
/*******************************************************************************
* 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);
}
}