blob: b10f6200ec5428915f95575e142ada4cbede2786 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-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.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.GroupCategory;
import org.eclipse.ltk.core.refactoring.GroupCategorySet;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextEditBasedChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
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.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
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.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
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.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
import org.eclipse.jdt.internal.corext.dom.NodeFinder;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptor;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
import org.eclipse.jdt.internal.corext.refactoring.rename.MethodChecks;
import org.eclipse.jdt.internal.corext.refactoring.reorg.SourceReferenceUtil;
import org.eclipse.jdt.internal.corext.refactoring.structure.constraints.SuperTypeConstraintsSolver;
import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.CompilationUnitRange;
import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.types.TType;
import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ISourceConstraintVariable;
import org.eclipse.jdt.internal.corext.refactoring.typeconstraints2.ITypeConstraintVariable;
import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.TextEditBasedChangeManager;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.Strings;
import org.eclipse.jdt.ui.CodeGeneration;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
/**
* Refactoring processor for the pull up refactoring.
*
* @since 3.2
*/
public class PullUpRefactoringProcessor extends HierarchyProcessor {
/**
* AST node visitor which performs the actual mapping.
*/
private static class PullUpAstNodeMapper extends TypeVariableMapper {
/** Are we in an anonymous class declaration? */
private boolean fAnonymousClassDeclaration= false;
/** The source compilation unit rewrite to use */
private final CompilationUnitRewrite fSourceRewriter;
/** The super reference type */
private final IType fSuperReferenceType;
/** The target compilation unit rewrite to use */
private final CompilationUnitRewrite fTargetRewriter;
/** Are we in a type declaration statement? */
private boolean fTypeDeclarationStatement= false;
/**
* Creates a new pull up ast node mapper.
*
* @param sourceRewriter
* the source compilation unit rewrite to use
* @param targetRewriter
* the target compilation unit rewrite to use
* @param rewrite
* the AST rewrite to use
* @param type
* the super reference type
* @param mapping
* the type variable mapping
*/
public PullUpAstNodeMapper(final CompilationUnitRewrite sourceRewriter, final CompilationUnitRewrite targetRewriter, final ASTRewrite rewrite, final IType type, final TypeVariableMaplet[] mapping) {
super(rewrite, mapping);
Assert.isNotNull(rewrite);
Assert.isNotNull(type);
fSourceRewriter= sourceRewriter;
fTargetRewriter= targetRewriter;
fSuperReferenceType= type;
}
public final void endVisit(final AnonymousClassDeclaration node) {
fAnonymousClassDeclaration= false;
super.endVisit(node);
}
public final void endVisit(final TypeDeclarationStatement node) {
fTypeDeclarationStatement= false;
super.endVisit(node);
}
public final boolean visit(final AnonymousClassDeclaration node) {
fAnonymousClassDeclaration= true;
return super.visit(node);
}
public final boolean visit(final SuperFieldAccess node) {
if (!fAnonymousClassDeclaration && !fTypeDeclarationStatement) {
final AST ast= node.getAST();
final FieldAccess access= ast.newFieldAccess();
access.setExpression(ast.newThisExpression());
access.setName(ast.newSimpleName(node.getName().getIdentifier()));
fRewrite.replace(node, access, null);
if (!fSourceRewriter.getCu().equals(fTargetRewriter.getCu()))
fSourceRewriter.getImportRemover().registerRemovedNode(node);
return true;
}
return false;
}
public final boolean visit(final SuperMethodInvocation node) {
if (!fAnonymousClassDeclaration && !fTypeDeclarationStatement) {
final IBinding name= node.getName().resolveBinding();
if (name != null && name.getKind() == IBinding.METHOD) {
final ITypeBinding binding= ((IMethodBinding) name).getDeclaringClass();
if (binding != null) {
final IType type= (IType) binding.getJavaElement();
if (!fSuperReferenceType.equals(type))
return true;
}
}
final AST ast= node.getAST();
final ThisExpression expression= ast.newThisExpression();
final MethodInvocation invocation= ast.newMethodInvocation();
final SimpleName simple= ast.newSimpleName(node.getName().getIdentifier());
invocation.setName(simple);
invocation.setExpression(expression);
final List arguments= (List) node.getStructuralProperty(SuperMethodInvocation.ARGUMENTS_PROPERTY);
if (arguments != null && arguments.size() > 0) {
final ListRewrite rewriter= fRewrite.getListRewrite(invocation, MethodInvocation.ARGUMENTS_PROPERTY);
rewriter.insertLast(rewriter.createCopyTarget((ASTNode) arguments.get(0), (ASTNode) arguments.get(arguments.size() - 1)), null);
}
fRewrite.replace(node, invocation, null);
if (!fSourceRewriter.getCu().equals(fTargetRewriter.getCu()))
fSourceRewriter.getImportRemover().registerRemovedNode(node);
return true;
}
return false;
}
public final boolean visit(final TypeDeclarationStatement node) {
fTypeDeclarationStatement= true;
return super.visit(node);
}
}
protected static final String ATTRIBUTE_ABSTRACT= "abstract"; //$NON-NLS-1$
protected static final String ATTRIBUTE_DELETE= "delete"; //$NON-NLS-1$
protected static final String ATTRIBUTE_PULL= "pull"; //$NON-NLS-1$
protected static final String ATTRIBUTE_STUBS= "stubs"; //$NON-NLS-1$
private static final String ID_PULL_UP= "org.eclipse.jdt.ui.pull.up"; //$NON-NLS-1$
private static final String IDENTIFIER= "org.eclipse.jdt.ui.pullUpProcessor"; //$NON-NLS-1$
/** The pull up group category set */
private static final GroupCategorySet SET_PULL_UP= new GroupCategorySet(new GroupCategory("org.eclipse.jdt.internal.corext.pullUp", //$NON-NLS-1$
RefactoringCoreMessages.PullUpRefactoring_category_name, RefactoringCoreMessages.PullUpRefactoring_category_description));
private static void addMatchingMember(final Map mapping, final IMember key, final 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 static Block createMethodStub(final MethodDeclaration method, final AST ast) {
final Block body= ast.newBlock();
final Expression expression= ASTNodeFactory.newDefaultExpression(ast, method.getReturnType2(), method.getExtraDimensions());
if (expression != null) {
final ReturnStatement returnStatement= ast.newReturnStatement();
returnStatement.setExpression(expression);
body.statements().add(returnStatement);
}
return body;
}
private static Set getEffectedSubTypes(final ITypeHierarchy hierarchy, final IType type) throws JavaModelException {
final IType[] types= hierarchy.getSubclasses(type);
final Set result= new HashSet();
for (int index= 0; index < types.length; index++) {
if (JdtFlags.isAbstract(types[index]))
result.addAll(getEffectedSubTypes(hierarchy, types[index]));
else
result.add(types[index]);
}
return result;
}
private static IMember[] getMembers(final IMember[] members, final int type) {
final List list= Arrays.asList(JavaElementUtil.getElementsOfType(members, type));
return (IMember[]) list.toArray(new IMember[list.size()]);
}
private static void mergeMaps(final Map result, final Map map) {
for (final Iterator iter= result.keySet().iterator(); iter.hasNext();) {
final IMember key= (IMember) iter.next();
if (map.containsKey(key)) {
final Set resultSet= (Set) result.get(key);
final Set mapSet= (Set) map.get(key);
resultSet.addAll(mapSet);
}
}
}
private static void upgradeMap(final Map result, final Map map) {
for (final Iterator iter= map.keySet().iterator(); iter.hasNext();) {
final IMember key= (IMember) iter.next();
if (!result.containsKey(key)) {
final Set mapSet= (Set) map.get(key);
final Set resultSet= new HashSet(mapSet);
result.put(key, resultSet);
}
}
}
/** The methods to be declared abstract */
protected IMethod[] fAbstractMethods= new IMethod[0];
/** The cached supertype hierarchy of the declaring type */
private ITypeHierarchy fCachedDeclaringSuperTypeHierarchy;
/** The cached type hierarchy of the destination type */
private ITypeHierarchy fCachedDestinationTypeHierarchy;
/** The cached set of skipped supertypes */
private Set fCachedSkippedSuperTypes;
/** The map of compilation units to compilation unit rewrites */
protected Map fCompilationUnitRewrites;
/** Should method stubs be generated in subtypes? */
protected boolean fCreateMethodStubs= true;
/** The methods to be deleted in subtypes */
protected IMethod[] fDeletedMethods= new IMethod[0];
/** The destination type */
protected IType fDestinationType;
/**
* Creates a new pull up refactoring processor.
*
* @param members
* the members to pull up, or <code>null</code> if invoked by
* scripting
* @param settings
* the code generation settings, or <code>null</code> if
* invoked by scripting
*/
public PullUpRefactoringProcessor(final IMember[] members, final CodeGenerationSettings settings) {
this(members, settings, false);
}
/**
* Creates a new pull up refactoring processor.
*
* @param members
* the members to pull up, or <code>null</code> if invoked by
* scripting
* @param settings
* the code generation settings, or <code>null</code> if
* invoked by scripting
* @param layer
* <code>true</code> to create a working copy layer,
* <code>false</code> otherwise
*/
protected PullUpRefactoringProcessor(final IMember[] members, final CodeGenerationSettings settings, final boolean layer) {
super(members, settings, layer);
if (members != null) {
final IType type= RefactoringAvailabilityTester.getTopLevelType(fMembersToMove);
try {
if (type != null && RefactoringAvailabilityTester.getPullUpMembers(type).length != 0) {
fCachedDeclaringType= RefactoringAvailabilityTester.getTopLevelType(fMembersToMove);
fMembersToMove= new IMember[0];
}
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
}
}
private void addAllRequiredPullableMembers(final List queue, final IMember member, final IProgressMonitor monitor) throws JavaModelException {
Assert.isNotNull(queue);
Assert.isNotNull(member);
Assert.isNotNull(monitor);
SubProgressMonitor sub= null;
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, 3);
final IMethod[] requiredMethods= ReferenceFinderUtil.getMethodsReferencedIn(new IJavaElement[] { member}, fOwner, new SubProgressMonitor(monitor, 1));
sub= new SubProgressMonitor(monitor, 1);
boolean isStatic= false;
try {
sub.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, requiredMethods.length);
isStatic= JdtFlags.isStatic(member);
for (int index= 0; index < requiredMethods.length; index++) {
final IMethod requiredMethod= requiredMethods[index];
if (isStatic && !JdtFlags.isStatic(requiredMethod))
continue;
if (isRequiredPullableMember(queue, requiredMethod) && !(MethodChecks.isVirtual(requiredMethod) && isAvailableInDestination(requiredMethod, new SubProgressMonitor(sub, 1))))
queue.add(requiredMethod);
}
} finally {
sub.done();
}
final IField[] requiredFields= ReferenceFinderUtil.getFieldsReferencedIn(new IJavaElement[] { member}, fOwner, new SubProgressMonitor(monitor, 1));
sub= new SubProgressMonitor(monitor, 1);
try {
sub.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, requiredFields.length);
isStatic= JdtFlags.isStatic(member);
for (int index= 0; index < requiredFields.length; index++) {
final IField requiredField= requiredFields[index];
if (isStatic && !JdtFlags.isStatic(requiredField))
continue;
if (isRequiredPullableMember(queue, requiredField))
queue.add(requiredField);
}
} finally {
sub.done();
}
final IType[] requiredTypes= ReferenceFinderUtil.getTypesReferencedIn(new IJavaElement[] { member}, fOwner, new SubProgressMonitor(monitor, 1));
sub= new SubProgressMonitor(monitor, 1);
try {
sub.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, requiredMethods.length);
isStatic= JdtFlags.isStatic(member);
for (int index= 0; index < requiredTypes.length; index++) {
final IType requiredType= requiredTypes[index];
if (isStatic && !JdtFlags.isStatic(requiredType))
continue;
if (isRequiredPullableMember(queue, requiredType))
queue.add(requiredType);
}
} finally {
sub.done();
}
} finally {
monitor.done();
}
}
private void addMethodStubForAbstractMethod(final IMethod sourceMethod, final CompilationUnit declaringCuNode, final AbstractTypeDeclaration typeToCreateStubIn, final ICompilationUnit newCu, final CompilationUnitRewrite rewriter, final Map adjustments, final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
final MethodDeclaration methodToCreateStubFor= ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, declaringCuNode);
final AST ast= rewriter.getRoot().getAST();
final MethodDeclaration newMethod= ast.newMethodDeclaration();
newMethod.setBody(createMethodStub(methodToCreateStubFor, ast));
newMethod.setConstructor(false);
newMethod.setExtraDimensions(methodToCreateStubFor.getExtraDimensions());
newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getModifiersWithUpdatedVisibility(sourceMethod, JdtFlags.clearFlag(Modifier.NATIVE | Modifier.ABSTRACT, methodToCreateStubFor.getModifiers()), adjustments, new SubProgressMonitor(monitor, 1), false, status)));
newMethod.setName(((SimpleName) ASTNode.copySubtree(ast, methodToCreateStubFor.getName())));
final TypeVariableMaplet[] mapping= TypeVariableUtil.composeMappings(TypeVariableUtil.subTypeToSuperType(getDeclaringType(), getDestinationType()), TypeVariableUtil.superTypeToInheritedType(getDestinationType(), ((IType) typeToCreateStubIn.resolveBinding().getJavaElement())));
copyReturnType(rewriter.getASTRewrite(), getDeclaringType().getCompilationUnit(), methodToCreateStubFor, newMethod, mapping);
copyParameters(rewriter.getASTRewrite(), getDeclaringType().getCompilationUnit(), methodToCreateStubFor, newMethod, mapping);
copyThrownExceptions(methodToCreateStubFor, newMethod);
newMethod.setJavadoc(createJavadocForStub(typeToCreateStubIn.getName().getIdentifier(), methodToCreateStubFor, newMethod, newCu, rewriter.getASTRewrite()));
ImportRewriteUtil.addImports(rewriter, newMethod, new HashMap(), new HashMap(), false);
rewriter.getASTRewrite().getListRewrite(typeToCreateStubIn, typeToCreateStubIn.getBodyDeclarationsProperty()).insertAt(newMethod, ASTNodes.getInsertionIndex(newMethod, typeToCreateStubIn.bodyDeclarations()), rewriter.createCategorizedGroupDescription(RefactoringCoreMessages.PullUpRefactoring_add_method_stub, SET_PULL_UP));
}
private void addNecessaryMethodStubs(final List effected, final CompilationUnit root, final CompilationUnitRewrite unitRewriter, final Map adjustments, final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
final IType declaringType= getDeclaringType();
final IMethod[] methods= getAbstractMethods();
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, effected.size());
for (final Iterator iter= effected.iterator(); iter.hasNext();) {
final IType type= (IType) iter.next();
if (type.equals(declaringType))
continue;
final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, unitRewriter.getRoot());
final ICompilationUnit unit= type.getCompilationUnit();
final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 1);
try {
subMonitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, methods.length);
for (int j= 0; j < methods.length; j++) {
final IMethod method= methods[j];
if (null == JavaModelUtil.findMethod(method.getElementName(), method.getParameterTypes(), method.isConstructor(), type)) {
addMethodStubForAbstractMethod(method, root, declaration, unit, unitRewriter, adjustments, new SubProgressMonitor(subMonitor, 1), status);
}
}
subMonitor.done();
} finally {
subMonitor.done();
}
}
} finally {
monitor.done();
}
}
protected boolean canBeAccessedFrom(final IMember member, final IType target, final ITypeHierarchy hierarchy) throws JavaModelException {
if (super.canBeAccessedFrom(member, target, hierarchy)) {
if (target.isInterface())
return true;
if (target.equals(member.getDeclaringType()))
return true;
if (target.equals(member))
return true;
if (member instanceof IMethod) {
final IMethod method= (IMethod) member;
final IMethod stub= target.getMethod(method.getElementName(), method.getParameterTypes());
if (stub.exists())
return true;
}
if (member.getDeclaringType() == null) {
if (!(member instanceof IType))
return false;
if (JdtFlags.isPublic(member))
return true;
if (!JdtFlags.isPackageVisible(member))
return false;
if (JavaModelUtil.isSamePackage(((IType) member).getPackageFragment(), target.getPackageFragment()))
return true;
final IType type= member.getDeclaringType();
if (type != null)
return hierarchy.contains(type);
return false;
}
final IType declaringType= member.getDeclaringType();
if (!canBeAccessedFrom(declaringType, target, hierarchy))
return false;
if (declaringType.equals(getDeclaringType()))
return false;
return true;
}
return false;
}
private RefactoringStatus checkAccessedFields(final IProgressMonitor monitor, final ITypeHierarchy hierarchy) throws JavaModelException {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking_referenced_elements, 2);
final RefactoringStatus result= new RefactoringStatus();
final List pulledUpList= Arrays.asList(fMembersToMove);
final List deletedList= Arrays.asList(getMembersToDelete(new SubProgressMonitor(monitor, 1)));
final IField[] accessedFields= ReferenceFinderUtil.getFieldsReferencedIn(fMembersToMove, fOwner, new SubProgressMonitor(monitor, 1));
final IType destination= getDestinationType();
for (int i= 0; i < accessedFields.length; i++) {
final IField field= accessedFields[i];
if (!field.exists())
continue;
boolean isAccessible= pulledUpList.contains(field) || deletedList.contains(field) || canBeAccessedFrom(field, destination, hierarchy) || Flags.isEnum(field.getFlags());
if (!isAccessible) {
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_field_not_accessible, new String[] { JavaElementLabels.getTextLabel(field, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_FULLY_QUALIFIED)});
result.addError(message, JavaStatusContext.create(field));
} else if (getSkippedSuperTypes(new SubProgressMonitor(monitor, 1)).contains(field.getDeclaringType())) {
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_field_cannot_be_accessed, new String[] { JavaElementLabels.getTextLabel(field, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_FULLY_QUALIFIED)});
result.addError(message, JavaStatusContext.create(field));
}
}
monitor.done();
return result;
}
private RefactoringStatus checkAccessedMethods(final IProgressMonitor monitor, final ITypeHierarchy hierarchy) throws JavaModelException {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking_referenced_elements, 2);
final RefactoringStatus result= new RefactoringStatus();
final List pulledUpList= Arrays.asList(fMembersToMove);
final List declaredAbstractList= Arrays.asList(fAbstractMethods);
final List deletedList= Arrays.asList(getMembersToDelete(new SubProgressMonitor(monitor, 1)));
final IMethod[] accessedMethods= ReferenceFinderUtil.getMethodsReferencedIn(fMembersToMove, fOwner, new SubProgressMonitor(monitor, 1));
final IType destination= getDestinationType();
for (int index= 0; index < accessedMethods.length; index++) {
final IMethod method= accessedMethods[index];
if (!method.exists())
continue;
boolean isAccessible= pulledUpList.contains(method) || deletedList.contains(method) || declaredAbstractList.contains(method) || canBeAccessedFrom(method, destination, hierarchy);
if (!isAccessible) {
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_method_not_accessible, new String[] { JavaElementLabels.getTextLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_FULLY_QUALIFIED)});
result.addError(message, JavaStatusContext.create(method));
} else if (getSkippedSuperTypes(new SubProgressMonitor(monitor, 1)).contains(method.getDeclaringType())) {
final String[] keys= { JavaElementLabels.getTextLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_method_cannot_be_accessed, keys);
result.addError(message, JavaStatusContext.create(method));
}
}
monitor.done();
return result;
}
private RefactoringStatus checkAccessedTypes(final IProgressMonitor monitor, final ITypeHierarchy hierarchy) throws JavaModelException {
final RefactoringStatus result= new RefactoringStatus();
final IType[] accessedTypes= getTypesReferencedInMovedMembers(monitor);
final IType destination= getDestinationType();
final List pulledUpList= Arrays.asList(fMembersToMove);
for (int index= 0; index < accessedTypes.length; index++) {
final IType type= accessedTypes[index];
if (!type.exists())
continue;
if (!canBeAccessedFrom(type, destination, hierarchy) && !pulledUpList.contains(type)) {
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_type_not_accessible, new String[] { JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(destination, JavaElementLabels.ALL_FULLY_QUALIFIED)});
result.addError(message, JavaStatusContext.create(type));
}
}
monitor.done();
return result;
}
private RefactoringStatus checkAccesses(final IProgressMonitor monitor) throws JavaModelException {
final RefactoringStatus result= new RefactoringStatus();
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking_referenced_elements, 4);
final ITypeHierarchy hierarchy= getDestinationType().newSupertypeHierarchy(fOwner, new SubProgressMonitor(monitor, 1));
result.merge(checkAccessedTypes(new SubProgressMonitor(monitor, 1), hierarchy));
result.merge(checkAccessedFields(new SubProgressMonitor(monitor, 1), hierarchy));
result.merge(checkAccessedMethods(new SubProgressMonitor(monitor, 1), hierarchy));
} finally {
monitor.done();
}
return result;
}
private void checkAccessModifiers(final RefactoringStatus result, final Set notDeletedMembersInSubtypes) throws JavaModelException {
if (fDestinationType.isInterface())
return;
final List toDeclareAbstract= Arrays.asList(fAbstractMethods);
for (final Iterator iter= notDeletedMembersInSubtypes.iterator(); iter.hasNext();) {
final IMember member= (IMember) iter.next();
if (member.getElementType() == IJavaElement.METHOD && !toDeclareAbstract.contains(member)) {
final IMethod method= ((IMethod) member);
if (method.getDeclaringType().getPackageFragment().equals(fDestinationType.getPackageFragment())) {
if (JdtFlags.isPrivate(method))
result.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_lower_default_visibility, new String[] { JavaElementLabels.getTextLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(method.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)}), JavaStatusContext.create(method));
} else if (!JdtFlags.isPublic(method) && !JdtFlags.isProtected(method))
result.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_lower_protected_visibility, new String[] { JavaElementLabels.getTextLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(method.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)}), JavaStatusContext.create(method));
}
}
}
protected RefactoringStatus checkDeclaringSuperTypes(final IProgressMonitor monitor) throws JavaModelException {
final RefactoringStatus result= new RefactoringStatus();
if (getCandidateTypes(result, monitor).length == 0 && !result.hasFatalError()) {
final String msg= Messages.format(RefactoringCoreMessages.PullUpRefactoring_not_this_type, new String[] { JavaElementLabels.getTextLabel(getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)});
return RefactoringStatus.createFatalErrorStatus(msg);
}
return result;
}
protected RefactoringStatus checkDeclaringType(final IProgressMonitor monitor) throws JavaModelException {
final RefactoringStatus status= super.checkDeclaringType(monitor);
if (JavaModelUtil.getFullyQualifiedName(getDeclaringType()).equals("java.lang.Object")) //$NON-NLS-1$
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.PullUpRefactoring_no_java_lang_Object));
status.merge(checkDeclaringSuperTypes(monitor));
return status;
}
private void checkFieldTypes(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
final Map mapping= getMatchingMembers(getDestinationTypeHierarchy(monitor), getDestinationType(), true);
for (int i= 0; i < fMembersToMove.length; i++) {
if (fMembersToMove[i].getElementType() != IJavaElement.FIELD)
continue;
final IField field= (IField) fMembersToMove[i];
final String type= Signature.toString(field.getTypeSignature());
Assert.isTrue(mapping.containsKey(field));
for (final Iterator iter= ((Set) mapping.get(field)).iterator(); iter.hasNext();) {
final IField matchingField= (IField) iter.next();
if (field.equals(matchingField))
continue;
if (type.equals(Signature.toString(matchingField.getTypeSignature())))
continue;
final String[] keys= { JavaElementLabels.getTextLabel(matchingField, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(matchingField.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_different_field_type, keys);
final RefactoringStatusContext context= JavaStatusContext.create(matchingField.getCompilationUnit(), matchingField.getSourceRange());
status.addError(message, context);
}
}
}
/**
* {@inheritDoc}
*/
public RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, final CheckConditionsContext context) throws CoreException, OperationCanceledException {
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 12);
clearCaches();
final RefactoringStatus result= new RefactoringStatus();
result.merge(createWorkingCopyLayer(new SubProgressMonitor(monitor, 4)));
if (result.hasFatalError())
return result;
if (monitor.isCanceled())
throw new OperationCanceledException();
result.merge(checkGenericDeclaringType(new SubProgressMonitor(monitor, 1)));
result.merge(checkFinalFields(new SubProgressMonitor(monitor, 1)));
if (monitor.isCanceled())
throw new OperationCanceledException();
result.merge(checkAccesses(new SubProgressMonitor(monitor, 1)));
result.merge(checkMembersInTypeAndAllSubtypes(new SubProgressMonitor(monitor, 2)));
result.merge(checkIfSkippingOverElements(new SubProgressMonitor(monitor, 1)));
if (monitor.isCanceled())
throw new OperationCanceledException();
if (!JdtFlags.isAbstract(getDestinationType()) && getAbstractMethods().length > 0)
result.merge(checkConstructorCalls(getDestinationType(), new SubProgressMonitor(monitor, 1)));
else
monitor.worked(1);
if (result.hasFatalError())
return result;
fCompilationUnitRewrites= new HashMap(3);
result.merge(checkProjectCompliance(getCompilationUnitRewrite(fCompilationUnitRewrites, getDeclaringType().getCompilationUnit()), getDestinationType(), fMembersToMove));
fChangeManager= createChangeManager(new SubProgressMonitor(monitor, 1), result);
result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), getRefactoring().getValidationContext()));
return result;
} finally {
monitor.done();
}
}
private RefactoringStatus checkFinalFields(final IProgressMonitor monitor) throws JavaModelException {
final RefactoringStatus result= new RefactoringStatus();
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, fMembersToMove.length);
for (int index= 0; index < fMembersToMove.length; index++) {
final IMember member= fMembersToMove[index];
if (member.getElementType() == IJavaElement.FIELD) {
if (!JdtFlags.isStatic(member)) {
if (JdtFlags.isFinal(member)) {
final RefactoringStatusContext context= JavaStatusContext.create(member);
result.addWarning(RefactoringCoreMessages.PullUpRefactoring_final_fields, context);
} else if (getDestinationType().isInterface()) {
final RefactoringStatusContext context= JavaStatusContext.create(member);
result.addWarning(RefactoringCoreMessages.PullUpRefactoring_non_final_pull_up_to_interface, context);
}
}
}
monitor.worked(1);
if (monitor.isCanceled())
throw new OperationCanceledException();
}
monitor.done();
return result;
}
private RefactoringStatus checkGenericDeclaringType(final SubProgressMonitor monitor) throws JavaModelException {
Assert.isNotNull(monitor);
final RefactoringStatus status= new RefactoringStatus();
try {
final IMember[] pullables= getMembersToMove();
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, pullables.length);
final IType declaring= getDeclaringType();
final ITypeParameter[] parameters= declaring.getTypeParameters();
if (parameters.length > 0) {
final TypeVariableMaplet[] mapping= TypeVariableUtil.subTypeToInheritedType(declaring);
IMember member= null;
int length= 0;
for (int index= 0; index < pullables.length; index++) {
member= pullables[index];
final String[] unmapped= TypeVariableUtil.getUnmappedVariables(mapping, declaring, member);
length= unmapped.length;
switch (length) {
case 0:
break;
case 1:
status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable_not_available, new String[] { unmapped[0], declaring.getSuperclassName()}), JavaStatusContext.create(member));
break;
case 2:
status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable2_not_available, new String[] { unmapped[0], unmapped[1], declaring.getSuperclassName()}), JavaStatusContext.create(member));
break;
case 3:
status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable3_not_available, new String[] { unmapped[0], unmapped[1], unmapped[2], declaring.getSuperclassName()}), JavaStatusContext.create(member));
break;
default:
status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variables_not_available, new String[] { declaring.getSuperclassName()}), JavaStatusContext.create(member));
}
monitor.worked(1);
if (monitor.isCanceled())
throw new OperationCanceledException();
}
}
} finally {
monitor.done();
}
return status;
}
private RefactoringStatus checkIfDeclaredIn(final IMember element, final 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 checkIfFieldDeclaredIn(final IField iField, final IType type) {
final IField fieldInType= type.getField(iField.getElementName());
if (!fieldInType.exists())
return null;
final String[] keys= { JavaElementLabels.getTextLabel(fieldInType, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String msg= Messages.format(RefactoringCoreMessages.PullUpRefactoring_Field_declared_in_class, keys);
final RefactoringStatusContext context= JavaStatusContext.create(fieldInType);
return RefactoringStatus.createWarningStatus(msg, context);
}
private RefactoringStatus checkIfMethodDeclaredIn(final IMethod iMethod, final IType type) throws JavaModelException {
final IMethod methodInType= JavaModelUtil.findMethod(iMethod.getElementName(), iMethod.getParameterTypes(), iMethod.isConstructor(), type);
if (methodInType == null || !methodInType.exists())
return null;
final String[] keys= { JavaElementLabels.getTextLabel(methodInType, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String msg= Messages.format(RefactoringCoreMessages.PullUpRefactoring_Method_declared_in_class, keys);
final RefactoringStatusContext context= JavaStatusContext.create(methodInType);
return RefactoringStatus.createWarningStatus(msg, context);
}
private RefactoringStatus checkIfSkippingOverElements(final IProgressMonitor monitor) throws JavaModelException {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 1);
try {
final Set skippedTypes= getSkippedSuperTypes(new SubProgressMonitor(monitor, 1));
final IType[] skipped= (IType[]) skippedTypes.toArray(new IType[skippedTypes.size()]);
final RefactoringStatus result= new RefactoringStatus();
for (int i= 0; i < fMembersToMove.length; i++) {
final IMember element= fMembersToMove[i];
for (int j= 0; j < skipped.length; j++) {
result.merge(checkIfDeclaredIn(element, skipped[j]));
}
}
return result;
} finally {
monitor.done();
}
}
private RefactoringStatus checkIfTypeDeclaredIn(final IType iType, final IType type) {
final IType typeInType= type.getType(iType.getElementName());
if (!typeInType.exists())
return null;
final String[] keys= { JavaElementLabels.getTextLabel(typeInType, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String msg= Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_declared_in_class, keys);
final RefactoringStatusContext context= JavaStatusContext.create(typeInType);
return RefactoringStatus.createWarningStatus(msg, context);
}
/**
* {@inheritDoc}
*/
public RefactoringStatus checkInitialConditions(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 1);
final RefactoringStatus status= new RefactoringStatus();
status.merge(checkDeclaringType(new SubProgressMonitor(monitor, 1)));
if (status.hasFatalError())
return status;
status.merge(checkIfMembersExist());
if (status.hasFatalError())
return status;
return status;
} finally {
monitor.done();
}
}
private void checkMembersInDestinationType(final RefactoringStatus status, final Set set) throws JavaModelException {
final IMember[] destinationMembers= getCreatedDestinationMembers();
final List list= new ArrayList(destinationMembers.length);
list.addAll(Arrays.asList(destinationMembers));
list.addAll(set);
list.removeAll(Arrays.asList(fDeletedMethods));
final IMember[] members= (IMember[]) list.toArray(new IMember[list.size()]);
status.merge(MemberCheckUtil.checkMembersInDestinationType(members, getDestinationType()));
}
private RefactoringStatus checkMembersInTypeAndAllSubtypes(final IProgressMonitor monitor) throws JavaModelException {
final RefactoringStatus result= new RefactoringStatus();
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 3);
final Set notDeletedMembers= getNotDeletedMembers(new SubProgressMonitor(monitor, 1));
final Set notDeletedMembersInTargetType= new HashSet();
final Set notDeletedMembersInSubtypes= new HashSet();
for (final Iterator iter= notDeletedMembers.iterator(); iter.hasNext();) {
final IMember member= (IMember) iter.next();
if (getDestinationType().equals(member.getDeclaringType()))
notDeletedMembersInTargetType.add(member);
else
notDeletedMembersInSubtypes.add(member);
}
checkMembersInDestinationType(result, notDeletedMembersInTargetType);
checkAccessModifiers(result, notDeletedMembersInSubtypes);
checkMethodReturnTypes(new SubProgressMonitor(monitor, 1), result, notDeletedMembersInSubtypes);
checkFieldTypes(new SubProgressMonitor(monitor, 1), result);
monitor.done();
return result;
}
private void checkMethodReturnTypes(final IProgressMonitor monitor, final RefactoringStatus status, final Set notDeletedMembersInSubtypes) throws JavaModelException {
final Map mapping= getMatchingMembers(getDestinationTypeHierarchy(monitor), getDestinationType(), true);
final IMember[] members= getCreatedDestinationMembers();
for (int i= 0; i < members.length; i++) {
if (members[i].getElementType() != IJavaElement.METHOD)
continue;
final IMethod method= (IMethod) members[i];
final String returnType= Signature.toString(Signature.getReturnType(method.getSignature()).toString());
Assert.isTrue(mapping.containsKey(method));
final Set set= (Set) mapping.get(method);
if (set != null) {
for (final Iterator iter= set.iterator(); iter.hasNext();) {
final IMethod matchingMethod= (IMethod) iter.next();
if (method.equals(matchingMethod))
continue;
if (!notDeletedMembersInSubtypes.contains(matchingMethod))
continue;
if (returnType.equals(Signature.toString(Signature.getReturnType(matchingMethod.getSignature()).toString())))
continue;
final String[] keys= { JavaElementLabels.getTextLabel(matchingMethod, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(matchingMethod.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED)};
final String message= Messages.format(RefactoringCoreMessages.PullUpRefactoring_different_method_return_type, keys);
final RefactoringStatusContext context= JavaStatusContext.create(matchingMethod.getCompilationUnit(), matchingMethod.getNameRange());
status.addError(message, context);
}
}
}
}
protected void clearCaches() {
super.clearCaches();
fCachedMembersReferences.clear();
fCachedDestinationTypeHierarchy= null;
fCachedDeclaringSuperTypeHierarchy= null;
}
private void copyBodyOfPulledUpMethod(final CompilationUnitRewrite sourceRewrite, final CompilationUnitRewrite targetRewrite, final IMethod method, final MethodDeclaration oldMethod, final MethodDeclaration newMethod, final TypeVariableMaplet[] mapping, final IProgressMonitor monitor) throws JavaModelException {
final Block body= oldMethod.getBody();
if (body == null) {
newMethod.setBody(null);
return;
}
try {
final IDocument document= new Document(method.getCompilationUnit().getBuffer().getContents());
final ASTRewrite rewrite= ASTRewrite.create(body.getAST());
final ITrackedNodePosition position= rewrite.track(body);
body.accept(new PullUpAstNodeMapper(sourceRewrite, targetRewrite, rewrite, getDeclaringSuperTypeHierarchy(monitor).getSuperclass(getDeclaringType()), mapping));
rewrite.rewriteAST(document, method.getJavaProject().getOptions(true)).apply(document, TextEdit.NONE);
String content= document.get(position.getStartPosition(), position.getLength());
final String[] lines= Strings.convertIntoLines(content);
Strings.trimIndentation(lines, method.getJavaProject(), false);
content= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(method));
newMethod.setBody((Block) targetRewrite.getASTRewrite().createStringPlaceholder(content, ASTNode.BLOCK));
} catch (MalformedTreeException exception) {
JavaPlugin.log(exception);
} catch (BadLocationException exception) {
JavaPlugin.log(exception);
}
}
private void createAbstractMethod(final IMethod sourceMethod, final CompilationUnitRewrite sourceRewriter, final CompilationUnit declaringCuNode, final AbstractTypeDeclaration destination, final TypeVariableMaplet[] mapping, final CompilationUnitRewrite targetRewrite, final Map adjustments, final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
final MethodDeclaration oldMethod= ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, declaringCuNode);
if (JavaModelUtil.is50OrHigher(sourceMethod.getJavaProject()) && (fSettings.overrideAnnotation || JavaCore.ERROR.equals(sourceMethod.getJavaProject().getOption(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION, true)))) {
final MarkerAnnotation annotation= sourceRewriter.getAST().newMarkerAnnotation();
annotation.setTypeName(sourceRewriter.getAST().newSimpleName("Override")); //$NON-NLS-1$
sourceRewriter.getASTRewrite().getListRewrite(oldMethod, MethodDeclaration.MODIFIERS2_PROPERTY).insertFirst(annotation, sourceRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.PullUpRefactoring_add_override_annotation, SET_PULL_UP));
}
final MethodDeclaration newMethod= targetRewrite.getAST().newMethodDeclaration();
newMethod.setBody(null);
newMethod.setConstructor(false);
newMethod.setExtraDimensions(oldMethod.getExtraDimensions());
newMethod.setJavadoc(null);
int modifiers= getModifiersWithUpdatedVisibility(sourceMethod, Modifier.ABSTRACT | JdtFlags.clearFlag(Modifier.NATIVE | Modifier.FINAL, sourceMethod.getFlags()), adjustments, monitor, false, status);
if (oldMethod.isVarargs())
modifiers&= ~Flags.AccVarargs;
newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(targetRewrite.getAST(), modifiers));
newMethod.setName(((SimpleName) ASTNode.copySubtree(targetRewrite.getAST(), oldMethod.getName())));
copyReturnType(targetRewrite.getASTRewrite(), getDeclaringType().getCompilationUnit(), oldMethod, newMethod, mapping);
copyParameters(targetRewrite.getASTRewrite(), getDeclaringType().getCompilationUnit(), oldMethod, newMethod, mapping);
copyThrownExceptions(oldMethod, newMethod);
ImportRewriteUtil.addImports(targetRewrite, newMethod, new HashMap(), new HashMap(), false);
targetRewrite.getASTRewrite().getListRewrite(destination, destination.getBodyDeclarationsProperty()).insertAt(newMethod, ASTNodes.getInsertionIndex(newMethod, destination.bodyDeclarations()), targetRewrite.createCategorizedGroupDescription(RefactoringCoreMessages.PullUpRefactoring_add_abstract_method, SET_PULL_UP));
}
/**
* {@inheritDoc}
*/
public Change createChange(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
try {
final Map arguments= new HashMap();
String project= null;
final IType declaring= getDeclaringType();
final IJavaProject javaProject= declaring.getJavaProject();
if (javaProject != null)
project= javaProject.getElementName();
int flags= JavaRefactoringDescriptor.JAR_IMPORTABLE | JavaRefactoringDescriptor.JAR_REFACTORABLE | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
try {
if (declaring.isLocal() || declaring.isAnonymous())
flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
final String description= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PullUpRefactoring_descriptor_description_short, new String[] { fMembersToMove[0].getElementName(), fDestinationType.getElementName()}) : Messages.format(RefactoringCoreMessages.PullUpRefactoring_descriptor_description_short_multiple, fDestinationType.getElementName());
final String header= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PullUpRefactoring_descriptor_description_full, new String[] { JavaElementLabels.getElementLabel(fMembersToMove[0], JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(fDestinationType, JavaElementLabels.ALL_FULLY_QUALIFIED)}) : Messages.format(RefactoringCoreMessages.PullUpRefactoring_descriptor_description, new String[] { JavaElementLabels.getElementLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getElementLabel(fDestinationType, JavaElementLabels.ALL_FULLY_QUALIFIED)});
final JavaRefactoringDescriptorComment comment= new JavaRefactoringDescriptorComment(this, header);
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_target_element_pattern, JavaElementLabels.getElementLabel(fDestinationType, JavaElementLabels.ALL_FULLY_QUALIFIED)));
addSuperTypeSettings(comment, true);
final JavaRefactoringDescriptor descriptor= new JavaRefactoringDescriptor(ID_PULL_UP, project, description, comment.asString(), arguments, flags);
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fDestinationType));
arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplace).toString());
arguments.put(ATTRIBUTE_INSTANCEOF, Boolean.valueOf(fInstanceOf).toString());
arguments.put(ATTRIBUTE_STUBS, Boolean.valueOf(fCreateMethodStubs).toString());
arguments.put(ATTRIBUTE_PULL, new Integer(fMembersToMove.length).toString());
for (int offset= 0; offset < fMembersToMove.length; offset++)
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (offset + 1), descriptor.elementToHandle(fMembersToMove[offset]));
arguments.put(ATTRIBUTE_DELETE, new Integer(fDeletedMethods.length).toString());
for (int offset= 0; offset < fDeletedMethods.length; offset++)
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (offset + fMembersToMove.length + 1), descriptor.elementToHandle(fDeletedMethods[offset]));
arguments.put(ATTRIBUTE_ABSTRACT, new Integer(fAbstractMethods.length).toString());
for (int offset= 0; offset < fAbstractMethods.length; offset++)
arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (offset + fMembersToMove.length + fDeletedMethods.length + 1), descriptor.elementToHandle(fAbstractMethods[offset]));
return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.PullUpRefactoring_Pull_Up, fChangeManager.getAllChanges());
} finally {
monitor.done();
clearCaches();
}
}
private TextEditBasedChangeManager createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 24);
final ICompilationUnit source= getDeclaringType().getCompilationUnit();
final IType destination= getDestinationType();
final ICompilationUnit target= destination.getCompilationUnit();
final CompilationUnitRewrite sourceRewriter= getCompilationUnitRewrite(fCompilationUnitRewrites, source);
final CompilationUnitRewrite targetRewriter= getCompilationUnitRewrite(fCompilationUnitRewrites, target);
final Map deleteMap= createMembersToDeleteMap(new SubProgressMonitor(monitor, 1));
final Map effectedMap= createEffectedTypesMap(new SubProgressMonitor(monitor, 1));
final ICompilationUnit[] units= getAffectedCompilationUnits(new SubProgressMonitor(monitor, 1));
ICompilationUnit unit= null;
CompilationUnitRewrite rewrite= null;
final Map adjustments= new HashMap();
MemberVisibilityAdjustor adjustor= null;
final IProgressMonitor sub= new SubProgressMonitor(monitor, 1);
try {
sub.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, units.length * 11);
for (int index= 0; index < units.length; index++) {
unit= units[index];
if (!(source.equals(unit) || target.equals(unit) || deleteMap.containsKey(unit) || effectedMap.containsKey(unit))) {
sub.worked(10);
continue;
}
rewrite= getCompilationUnitRewrite(fCompilationUnitRewrites, unit);
if (deleteMap.containsKey(unit)) {
LinkedList list= new LinkedList((List) deleteMap.get(unit));
if (destination.isInterface()) {
for (final Iterator iterator= list.iterator(); iterator.hasNext();) {
final IMember member= (IMember) iterator.next();
if (member instanceof IMethod)
iterator.remove();
}
}
deleteDeclarationNodes(sourceRewriter, sourceRewriter.getCu().equals(targetRewriter.getCu()), rewrite, list, SET_PULL_UP);
}
final CompilationUnit root= sourceRewriter.getRoot();
if (unit.equals(target)) {
final ASTRewrite rewriter= rewrite.getASTRewrite();
if (!JdtFlags.isAbstract(destination) && !destination.isInterface() && getAbstractMethods().length > 0) {
final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(destination, rewrite.getRoot());
ModifierRewrite.create(rewriter, declaration).setModifiers(declaration.getModifiers() | Modifier.ABSTRACT, rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.PullUpRefactoring_make_target_abstract, SET_PULL_UP));
}
final TypeVariableMaplet[] mapping= TypeVariableUtil.subTypeToSuperType(getDeclaringType(), destination);
final IProgressMonitor subsub= new SubProgressMonitor(sub, 1);
final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(destination, rewrite.getRoot());
fMembersToMove= JavaElementUtil.sortByOffset(fMembersToMove);
subsub.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, fMembersToMove.length);
IMember member= null;
for (int offset= fMembersToMove.length - 1; offset >= 0; offset--) {
member= fMembersToMove[offset];
adjustor= new MemberVisibilityAdjustor(destination, member);
adjustor.setRewrite(sourceRewriter.getASTRewrite(), root);
// TW: set to error if bug 78387 is fixed
adjustor.setFailureSeverity(RefactoringStatus.WARNING);
adjustor.setOwner(fOwner);
adjustor.setRewrites(fCompilationUnitRewrites);
adjustor.setStatus(status);
adjustor.setAdjustments(adjustments);
adjustor.adjustVisibility(new SubProgressMonitor(subsub, 1));
adjustments.remove(member);
if (member instanceof IField) {
final VariableDeclarationFragment oldField= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField) member, root);
if (oldField != null) {
int flags= getModifiersWithUpdatedVisibility(member, member.getFlags(), adjustments, new SubProgressMonitor(subsub, 1), true, status);
if (destination.isInterface())
flags|= Flags.AccFinal;
final FieldDeclaration newField= createNewFieldDeclarationNode(rewriter, root, (IField) member, oldField, mapping, new SubProgressMonitor(subsub, 1), status, flags);
rewriter.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newField, ASTNodes.getInsertionIndex(newField, declaration.bodyDeclarations()), rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PULL_UP));
ImportRewriteUtil.addImports(rewrite, oldField.getParent(), new HashMap(), new HashMap(), false);
}
} else if (member instanceof IMethod) {
final MethodDeclaration oldMethod= ASTNodeSearchUtil.getMethodDeclarationNode((IMethod) member, root);
if (oldMethod != null) {
if (JdtFlags.isStatic(member) && fDestinationType.isInterface())
status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.PullUpRefactoring_moving_static_method_to_interface, new String[] { JavaElementLabels.getTextLabel(member, JavaElementLabels.ALL_FULLY_QUALIFIED)}), JavaStatusContext.create(member)));
final MethodDeclaration newMethod= createNewMethodDeclarationNode(sourceRewriter, rewrite, ((IMethod) member), oldMethod, root, mapping, adjustments, new SubProgressMonitor(subsub, 1), status);
rewriter.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newMethod, ASTNodes.getInsertionIndex(newMethod, declaration.bodyDeclarations()), rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PULL_UP));
ImportRewriteUtil.addImports(rewrite, oldMethod, new HashMap(), new HashMap(), false);
}
} else if (member instanceof IType) {
final AbstractTypeDeclaration oldType= ASTNodeSearchUtil.getAbstractTypeDeclarationNode((IType) member, root);
if (oldType != null) {
final BodyDeclaration newType= createNewTypeDeclarationNode(((IType) member), oldType, root, mapping, rewriter);
rewriter.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newType, ASTNodes.getInsertionIndex(newType, declaration.bodyDeclarations()), rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PULL_UP));
ImportRewriteUtil.addImports(rewrite, oldType, new HashMap(), new HashMap(), false);
}
} else
Assert.isTrue(false);
subsub.worked(1);
}
subsub.done();
for (int offset= 0; offset < fAbstractMethods.length; offset++)
createAbstractMethod(fAbstractMethods[offset], sourceRewriter, root, declaration, mapping, rewrite, adjustments, new SubProgressMonitor(sub, 1), status);
} else
sub.worked(2);
if (unit.equals(sourceRewriter.getCu())) {
final IProgressMonitor subsub= new SubProgressMonitor(sub, 1);
subsub.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, fAbstractMethods.length * 2);
IMethod method= null;
for (int offset= 0; offset < fAbstractMethods.length; offset++) {
method= fAbstractMethods[offset];
adjustor= new MemberVisibilityAdjustor(destination, method);
adjustor.setRewrite(sourceRewriter.getASTRewrite(), root);
adjustor.setRewrites(fCompilationUnitRewrites);
// TW: set to error if bug 78387 is fixed
adjustor.setFailureSeverity(RefactoringStatus.WARNING);
adjustor.setOwner(fOwner);
adjustor.setStatus(status);
adjustor.setAdjustments(adjustments);
if (needsVisibilityAdjustment(method, false, new SubProgressMonitor(subsub, 1), status))
adjustments.put(method, new MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment(method, Modifier.ModifierKeyword.PROTECTED_KEYWORD, RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_method_warning, new String[] { MemberVisibilityAdjustor.getLabel(method), RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_protected}), JavaStatusContext.create(method))));
}
} else
sub.worked(2);
if (effectedMap.containsKey(unit))
addNecessaryMethodStubs((List) effectedMap.get(unit), root, rewrite, adjustments, new SubProgressMonitor(sub, 2), status);
if (sub.isCanceled())
throw new OperationCanceledException();
}
} finally {
sub.done();
}
if (adjustor != null && !adjustments.isEmpty())
adjustor.rewriteVisibility(new SubProgressMonitor(monitor, 1));
final TextEditBasedChangeManager manager= new TextEditBasedChangeManager();
if (fReplace) {
for (final Iterator iterator= fCompilationUnitRewrites.keySet().iterator(); iterator.hasNext();) {
unit= (ICompilationUnit) iterator.next();
rewrite= (CompilationUnitRewrite) fCompilationUnitRewrites.get(unit);
if (rewrite != null) {
final CompilationUnitChange change= rewrite.createChange(false, null);
if (change != null)
manager.manage(unit, change);
}
}
TextEdit edit= null;
TextEditBasedChange change= null;
final Map workingcopies= new HashMap();
final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 1);
try {
subMonitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, fCompilationUnitRewrites.keySet().size());
for (final Iterator iterator= fCompilationUnitRewrites.keySet().iterator(); iterator.hasNext();) {
unit= (ICompilationUnit) iterator.next();
change= manager.get(unit);
if (change instanceof TextChange) {
edit= ((TextChange) change).getEdit();
if (edit != null) {
final ICompilationUnit copy= createWorkingCopy(unit, edit, status, new SubProgressMonitor(monitor, 1));
if (copy != null)
workingcopies.put(unit, copy);
}
}
}
final ICompilationUnit current= (ICompilationUnit) workingcopies.get(sourceRewriter.getCu());
if (current != null)
rewriteTypeOccurrences(manager, sourceRewriter, current, new HashSet(), status, new SubProgressMonitor(monitor, 16));
} finally {
subMonitor.done();
ICompilationUnit[] cus= manager.getAllCompilationUnits();
for (int index= 0; index < cus.length; index++) {
CompilationUnitChange current= (CompilationUnitChange) manager.get(cus[index]);
if (change != null && current.getEdit() == null)
manager.remove(cus[index]);
}
}
}
registerChanges(manager);
return manager;
} finally {
fCompilationUnitRewrites.clear();
monitor.done();
}
}
private Map createEffectedTypesMap(final IProgressMonitor monitor) throws JavaModelException {
if (!(fCreateMethodStubs && getAbstractMethods().length > 0))
return new HashMap(0);
final Set effected= getEffectedSubTypes(getDestinationTypeHierarchy(monitor), getDestinationType());
final Map result= new HashMap();
for (final Iterator iterator= effected.iterator(); iterator.hasNext();) {
final IType type= (IType) iterator.next();
final ICompilationUnit unit= type.getCompilationUnit();
if (!result.containsKey(unit))
result.put(unit, new ArrayList(1));
((List) result.get(unit)).add(type);
}
return result;
}
private Javadoc createJavadocForStub(final String enclosingTypeName, final MethodDeclaration oldMethod, final MethodDeclaration newMethodNode, final ICompilationUnit cu, final ASTRewrite rewrite) throws CoreException {
if (fSettings.createComments) {
final IMethodBinding binding= oldMethod.resolveBinding();
if (binding != null) {
final ITypeBinding[] params= binding.getParameterTypes();
final String fullTypeName= JavaModelUtil.getFullyQualifiedName(getDestinationType());
final String[] fullParamNames= new String[params.length];
for (int i= 0; i < fullParamNames.length; i++) {
fullParamNames[i]= Bindings.getFullyQualifiedName(params[i]);
}
final String comment= CodeGeneration.getMethodComment(cu, enclosingTypeName, newMethodNode, false, binding.getName(), fullTypeName, fullParamNames, StubUtility.getLineDelimiterUsed(cu));
return (Javadoc) rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC);
}
}
return null;
}
private Map createMembersToDeleteMap(final IProgressMonitor monitor) throws JavaModelException {
final IMember[] membersToDelete= getMembersToDelete(monitor);
final Map result= new HashMap();
for (int i= 0; i < membersToDelete.length; i++) {
final IMember member= membersToDelete[i];
final ICompilationUnit cu= member.getCompilationUnit();
if (!result.containsKey(cu))
result.put(cu, new ArrayList(1));
((List) result.get(cu)).add(member);
}
return result;
}
private MethodDeclaration createNewMethodDeclarationNode(final CompilationUnitRewrite sourceRewrite, final CompilationUnitRewrite targetRewrite, final IMethod sourceMethod, final MethodDeclaration oldMethod, final CompilationUnit declaringCuNode, final TypeVariableMaplet[] mapping, final Map adjustments, final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
final ASTRewrite rewrite= targetRewrite.getASTRewrite();
final AST ast= rewrite.getAST();
final MethodDeclaration newMethod= ast.newMethodDeclaration();
if (!getDestinationType().isInterface())
copyBodyOfPulledUpMethod(sourceRewrite, targetRewrite, sourceMethod, oldMethod, newMethod, mapping, monitor);
newMethod.setConstructor(oldMethod.isConstructor());
newMethod.setExtraDimensions(oldMethod.getExtraDimensions());
copyJavadocNode(rewrite, sourceMethod, oldMethod, newMethod);
int modifiers= getModifiersWithUpdatedVisibility(sourceMethod, sourceMethod.getFlags(), adjustments, monitor, true, status);
if (oldMethod.isVarargs())
modifiers&= ~Flags.AccVarargs;
copyAnnotations(oldMethod, newMethod);
newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifiers));
newMethod.setName(((SimpleName) ASTNode.copySubtree(ast, oldMethod.getName())));
copyReturnType(rewrite, getDeclaringType().getCompilationUnit(), oldMethod, newMethod, mapping);
copyParameters(rewrite, getDeclaringType().getCompilationUnit(), oldMethod, newMethod, mapping);
copyThrownExceptions(oldMethod, newMethod);
copyTypeParameters(oldMethod, newMethod);
return newMethod;
}
private BodyDeclaration createNewTypeDeclarationNode(final IType type, final AbstractTypeDeclaration oldType, final CompilationUnit declaringCuNode, final TypeVariableMaplet[] mapping, final ASTRewrite rewrite) throws JavaModelException {
final ICompilationUnit declaringCu= getDeclaringType().getCompilationUnit();
if (!JdtFlags.isPublic(type) && !JdtFlags.isProtected(type)) {
if (mapping.length > 0)
return createPlaceholderForTypeDeclaration(oldType, declaringCu, mapping, rewrite, true);
return createPlaceholderForProtectedTypeDeclaration(oldType, declaringCuNode, declaringCu, rewrite, true);
}
if (mapping.length > 0)
return createPlaceholderForTypeDeclaration(oldType, declaringCu, mapping, rewrite, true);
return createPlaceholderForTypeDeclaration(oldType, declaringCu, rewrite, true);
}
private ICompilationUnit createWorkingCopy(final ICompilationUnit unit, final TextEdit edit, final RefactoringStatus status, final IProgressMonitor monitor) {
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 1);
final ICompilationUnit copy= getSharedWorkingCopy(unit.getPrimary(), new SubProgressMonitor(monitor, 1));
final IDocument document= new Document(unit.getBuffer().getContents());
edit.apply(document, TextEdit.UPDATE_REGIONS);
copy.getBuffer().setContents(document.get());
JavaModelUtil.reconcile(copy);
return copy;
} catch (JavaModelException exception) {
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractInterfaceProcessor_internal_error));
} catch (MalformedTreeException exception) {
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractInterfaceProcessor_internal_error));
} catch (BadLocationException exception) {
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractInterfaceProcessor_internal_error));
} finally {
monitor.done();
}
return null;
}
/**
* Creates a working copy layer if necessary.
*
* @param monitor
* the progress monitor to use
* @return a status describing the outcome of the operation
*/
protected RefactoringStatus createWorkingCopyLayer(IProgressMonitor monitor) {
try {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 1);
ICompilationUnit unit= getDeclaringType().getCompilationUnit();
if (fLayer)
unit= unit.findWorkingCopy(fOwner);
resetWorkingCopies(unit);
return new RefactoringStatus();
} finally {
monitor.done();
}
}
private IMethod[] getAbstractMethods() throws JavaModelException {
final IMethod[] toDeclareAbstract= fAbstractMethods;
final IMethod[] abstractPulledUp= getAbstractMethodsToPullUp();
final 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 {
final List result= new ArrayList(fMembersToMove.length);
for (int i= 0; i < fMembersToMove.length; i++) {
final IMember member= fMembersToMove[i];
if (member instanceof IMethod && JdtFlags.isAbstract(member))
result.add(member);
}
return (IMethod[]) result.toArray(new IMethod[result.size()]);
}
public IMember[] getAdditionalRequiredMembersToPullUp(final IProgressMonitor monitor) throws JavaModelException {
final IMember[] members= getCreatedDestinationMembers();
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, members.length);// not
final 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(monitor, 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 ICompilationUnit[] getAffectedCompilationUnits(final IProgressMonitor monitor) throws JavaModelException {
final IType[] allSubtypes= getDestinationTypeHierarchy(monitor).getAllSubtypes(getDestinationType());
final Set result= new HashSet(allSubtypes.length);
for (int i= 0; i < allSubtypes.length; i++) {
result.add(allSubtypes[i].getCompilationUnit());
}
result.add(getDestinationType().getCompilationUnit());
return (ICompilationUnit[]) result.toArray(new ICompilationUnit[result.size()]);
}
public IType[] getCandidateTypes(final RefactoringStatus status, final IProgressMonitor monitor) throws JavaModelException {
final IType declaring= getDeclaringType();
final IType[] superTypes= declaring.newSupertypeHierarchy(fOwner, monitor).getAllSupertypes(declaring);
final List list= new ArrayList(superTypes.length);
int binary= 0;
for (int index= 0; index < superTypes.length; index++) {
final IType type= superTypes[index];
if (type != null && type.exists() && !type.isReadOnly() && !type.isBinary() && !"java.lang.Object".equals(type.getFullyQualifiedName())) { //$NON-NLS-1$
list.add(type);
} else {
if (type != null && type.isBinary()) {
binary++;
}
}
}
if (superTypes.length == 1 && superTypes[0].getFullyQualifiedName().equals("java.lang.Object")) //$NON-NLS-1$
status.addFatalError(RefactoringCoreMessages.PullUPRefactoring_not_java_lang_object);
else if (superTypes.length == binary)
status.addFatalError(RefactoringCoreMessages.PullUPRefactoring_no_all_binary);
Collections.reverse(list);
return (IType[]) list.toArray(new IType[list.size()]);
}
protected CompilationUnitRewrite getCompilationUnitRewrite(final Map rewrites, final ICompilationUnit unit) {
Assert.isNotNull(rewrites);
Assert.isNotNull(unit);
CompilationUnitRewrite rewrite= (CompilationUnitRewrite) rewrites.get(unit);
if (rewrite == null) {
rewrite= new CompilationUnitRewrite(fOwner, unit);
rewrites.put(unit, rewrite);
}
return rewrite;
}
private IMember[] getCreatedDestinationMembers() {
final List result= new ArrayList(fMembersToMove.length + fAbstractMethods.length);
result.addAll(Arrays.asList(fMembersToMove));
result.addAll(Arrays.asList(fAbstractMethods));
return (IMember[]) result.toArray(new IMember[result.size()]);
}
public boolean getCreateMethodStubs() {
return fCreateMethodStubs;
}
public ITypeHierarchy getDeclaringSuperTypeHierarchy(final IProgressMonitor monitor) throws JavaModelException {
try {
if (fCachedDeclaringSuperTypeHierarchy != null)
return fCachedDeclaringSuperTypeHierarchy;
fCachedDeclaringSuperTypeHierarchy= getDeclaringType().newSupertypeHierarchy(fOwner, monitor);
return fCachedDeclaringSuperTypeHierarchy;
} finally {
monitor.done();
}
}
public IType getDestinationType() {
return fDestinationType;
}
public ITypeHierarchy getDestinationTypeHierarchy(final IProgressMonitor monitor) throws JavaModelException {
try {
if (fCachedDestinationTypeHierarchy != null && fCachedDestinationTypeHierarchy.getType().equals(getDestinationType()))
return fCachedDestinationTypeHierarchy;
fCachedDestinationTypeHierarchy= getDestinationType().newTypeHierarchy(fOwner, monitor);
return fCachedDestinationTypeHierarchy;
} finally {
monitor.done();
}
}
/**
* {@inheritDoc}
*/
public Object[] getElements() {
return fMembersToMove;
}
/**
* {@inheritDoc}
*/
public String getIdentifier() {
return IDENTIFIER;
}
public IMember[] getMatchingElements(final IProgressMonitor monitor, final boolean includeAbstract) throws JavaModelException {
try {
final Set result= new HashSet();
final IType destination= getDestinationType();
final Map matching= getMatchingMembers(getDestinationTypeHierarchy(monitor), getDestinationType(), includeAbstract);
for (final Iterator iterator= matching.keySet().iterator(); iterator.hasNext();) {
final IMember key= (IMember) iterator.next();
Assert.isTrue(!key.getDeclaringType().equals(destination));
result.addAll((Set) matching.get(key));
}
return (IMember[]) result.toArray(new IMember[result.size()]);
} finally {
monitor.done();
}
}
private Map getMatchingMembers(final ITypeHierarchy hierarchy, final IType type, final boolean includeAbstract) throws JavaModelException {
final Map result= new HashMap();
result.putAll(getMatchingMembersMapping(type));
final IType[] subTypes= hierarchy.getAllSubtypes(type);
for (int i= 0; i < subTypes.length; i++) {
final Map map= getMatchingMembersMapping(subTypes[i]);
mergeMaps(result, map);
upgradeMap(result, map);
}
if (includeAbstract)
return result;
for (int i= 0; i < fAbstractMethods.length; i++) {
if (result.containsKey(fAbstractMethods[i]))
result.remove(fAbstractMethods[i]);
}
return result;
}
private Map getMatchingMembersMapping(final IType initial) throws JavaModelException {
final Map result= new HashMap();
final IMember[] members= getCreatedDestinationMembers();
for (int i= 0; i < members.length; i++) {
final IMember member= members[i];
if (member instanceof IMethod) {
final IMethod method= (IMethod) member;
final IMethod found= MemberCheckUtil.findMethod(method, initial.getMethods());
if (found != null)
addMatchingMember(result, method, found);
} else if (member instanceof IField) {
final IField field= (IField) member;
final IField found= initial.getField(field.getElementName());
if (found.exists())
addMatchingMember(result, field, found);
} else if (member instanceof IType) {
final IType type= (IType) member;
final IType found= initial.getType(type.getElementName());
if (found.exists())
addMatchingMember(result, type, found);
} else
Assert.isTrue(false);
}
return result;
}
private IMember[] getMembersToDelete(final IProgressMonitor monitor) throws JavaModelException {
try {
final IMember[] typesToDelete= getMembers(fMembersToMove, IJavaElement.TYPE);
final IMember[] matchingElements= getMatchingElements(monitor, false);
final IMember[] matchingFields= getMembers(matchingElements, IJavaElement.FIELD);
return JavaElementUtil.merge(JavaElementUtil.merge(matchingFields, typesToDelete), fDeletedMethods);
} finally {
monitor.done();
}
}
private int getModifiersWithUpdatedVisibility(final IMember member, final int modifiers, final Map adjustments, final IProgressMonitor monitor, final boolean considerReferences, final RefactoringStatus status) throws JavaModelException {
if (needsVisibilityAdjustment(member, considerReferences, monitor, status)) {
final MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment adjustment= new MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment(member, Modifier.ModifierKeyword.PROTECTED_KEYWORD, RefactoringStatus.createWarningStatus(Messages.format(MemberVisibilityAdjustor.getMessage(member), new String[] { MemberVisibilityAdjustor.getLabel(member), MemberVisibilityAdjustor.getLabel(Modifier.ModifierKeyword.PROTECTED_KEYWORD)})));
adjustment.setNeedsRewriting(false);
adjustments.put(member, adjustment);
return JdtFlags.clearAccessModifiers(modifiers) | Modifier.PROTECTED;
}
if (getDestinationType().isInterface()) {
final int flags= JdtFlags.clearAccessModifiers(modifiers) | Modifier.PUBLIC;
if (member instanceof IMethod)
return JdtFlags.clearFlag(Modifier.STATIC, flags);
return flags;
}
return modifiers;
}
private Set getNotDeletedMembers(final IProgressMonitor monitor) throws JavaModelException {
final Set matchingSet= new HashSet();
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 2);
matchingSet.addAll(Arrays.asList(getMatchingElements(new SubProgressMonitor(monitor, 1), true)));
matchingSet.removeAll(Arrays.asList(getMembersToDelete(new SubProgressMonitor(monitor, 1))));
monitor.done();
return matchingSet;
}
/**
* {@inheritDoc}
*/
public String getProcessorName() {
return RefactoringCoreMessages.PullUpRefactoring_Pull_Up;
}
public IMember[] getPullableMembersOfDeclaringType() {
try {
return RefactoringAvailabilityTester.getPullUpMembers(getDeclaringType());
} catch (JavaModelException e) {
return new IMember[0];
}
}
// skipped super classes are those declared in the hierarchy between the
// declaring type of the selected members
// and the target type
private Set getSkippedSuperTypes(final IProgressMonitor monitor) throws JavaModelException {
monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, 1);
try {
if (fCachedSkippedSuperTypes != null && getDestinationTypeHierarchy(new SubProgressMonitor(monitor, 1)).getType().equals(getDestinationType()))
return fCachedSkippedSuperTypes;
final ITypeHierarchy hierarchy= getDestinationTypeHierarchy(new SubProgressMonitor(monitor, 1));
fCachedSkippedSuperTypes= new HashSet(2);
IType current= hierarchy.getSuperclass(getDeclaringType());
while (current != null && !current.equals(getDestinationType())) {
fCachedSkippedSuperTypes.add(current);
current= hierarchy.getSuperclass(current);
}
return fCachedSkippedSuperTypes;
} finally {
monitor.done();
}
}
/**
* {@inheritDoc}
*/
public RefactoringStatus initialize(final RefactoringArguments arguments) {
if (arguments instanceof JavaRefactoringArguments) {
final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
String handle= extended.getAttribute(JavaRefactoringDescriptor.ATTRIBUTE_INPUT);
if (handle != null) {
final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
return ScriptableRefactoring.createInputFatalStatus(element, getRefactoring().getName(), ID_PULL_UP);
else
fDestinationType= (IType) element;
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.ATTRIBUTE_INPUT));
final String stubs= extended.getAttribute(ATTRIBUTE_STUBS);
if (stubs != null) {
fCreateMethodStubs= Boolean.valueOf(stubs).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STUBS));
final String instance= extended.getAttribute(ATTRIBUTE_INSTANCEOF);
if (instance != null) {
fInstanceOf= Boolean.valueOf(instance).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INSTANCEOF));
final String replace= extended.getAttribute(ATTRIBUTE_REPLACE);
if (replace != null) {
fReplace= Boolean.valueOf(replace).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REPLACE));
int pullCount= 0;
int abstractCount= 0;
int deleteCount= 0;
String value= extended.getAttribute(ATTRIBUTE_ABSTRACT);
if (value != null && !"".equals(value)) {//$NON-NLS-1$
try {
abstractCount= Integer.parseInt(value);
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ABSTRACT));
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_ABSTRACT));
value= extended.getAttribute(ATTRIBUTE_DELETE);
if (value != null && !"".equals(value)) {//$NON-NLS-1$
try {
deleteCount= Integer.parseInt(value);
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELETE));
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELETE));
value= extended.getAttribute(ATTRIBUTE_PULL);
if (value != null && !"".equals(value)) {//$NON-NLS-1$
try {
pullCount= Integer.parseInt(value);
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_PULL));
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_PULL));
final RefactoringStatus status= new RefactoringStatus();
List elements= new ArrayList();
for (int index= 0; index < pullCount; index++) {
final String attribute= JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (index + 1);
handle= extended.getAttribute(attribute);
if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists())
status.merge(ScriptableRefactoring.createInputWarningStatus(element, getRefactoring().getName(), ID_PULL_UP));
else
elements.add(element);
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
}
fMembersToMove= (IMember[]) elements.toArray(new IMember[elements.size()]);
elements= new ArrayList();
for (int index= 0; index < deleteCount; index++) {
final String attribute= JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (pullCount + index + 1);
handle= extended.getAttribute(attribute);
if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists())
status.merge(ScriptableRefactoring.createInputWarningStatus(element, getRefactoring().getName(), ID_PULL_UP));
else
elements.add(element);
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
}
fDeletedMethods= (IMethod[]) elements.toArray(new IMethod[elements.size()]);
elements= new ArrayList();
for (int index= 0; index < abstractCount; index++) {
final String attribute= JavaRefactoringDescriptor.ATTRIBUTE_ELEMENT + (pullCount + abstractCount + index + 1);
handle= extended.getAttribute(attribute);
if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists())
status.merge(ScriptableRefactoring.createInputWarningStatus(element, getRefactoring().getName(), ID_PULL_UP));
else
elements.add(element);
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
}
fAbstractMethods= (IMethod[]) elements.toArray(new IMethod[elements.size()]);
IJavaProject project= null;
if (fMembersToMove.length > 0)
project= fMembersToMove[0].getJavaProject();
fSettings= JavaPreferencesSettings.getCodeGenerationSettings(project);
if (!status.isOK())
return status;
} else
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
return new RefactoringStatus();
}
/**
* {@inheritDoc}
*/
public boolean isApplicable() throws CoreException {
return RefactoringAvailabilityTester.isPullUpAvailable(fMembersToMove);
}
private boolean isAvailableInDestination(final IMethod method, final IProgressMonitor monitor) throws JavaModelException {
try {
final IType destination= getDestinationType();
final IMethod first= JavaModelUtil.findMethod(method.getElementName(), method.getParameterTypes(), false, destination);
if (first != null && MethodChecks.isVirtual(first))
return true;
final ITypeHierarchy hierarchy= getDestinationTypeHierarchy(monitor);
final IMethod found= JavaModelUtil.findMethodInHierarchy(hierarchy, destination, method.getElementName(), method.getParameterTypes(), false);
return found != null && MethodChecks.isVirtual(found);
} finally {
monitor.done();
}
}
private boolean isRequiredPullableMember(final List queue, final IMember member) throws JavaModelException {
final IType declaring= member.getDeclaringType();
if (declaring == null) // not a member
return false;
return declaring.equals(getDeclaringType()) && !queue.contains(member) && RefactoringAvailabilityTester.isPullUpAvailable(member);
}
protected void registerChanges(final TextEditBasedChangeManager manager) throws CoreException {
ICompilationUnit unit= null;
CompilationUnitRewrite rewrite= null;
for (final Iterator iterator= fCompilationUnitRewrites.keySet().iterator(); iterator.hasNext();) {
unit= (ICompilationUnit) iterator.next();
rewrite= (CompilationUnitRewrite) fCompilationUnitRewrites.get(unit);
if (rewrite != null) {
final CompilationUnitChange change= rewrite.createChange();
if (change != null)
manager.manage(unit, change);
}
}
}
/**
* Resets the environment before the first wizard page becomes visible.
*/
public void resetEnvironment() {
ICompilationUnit unit= getDeclaringType().getCompilationUnit();
if (fLayer)
unit= unit.findWorkingCopy(fOwner);
resetWorkingCopies(unit);
}
/**
* {@inheritDoc}
*/
protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set replacements, final IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask("", 100); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
CompilationUnitRewrite currentRewrite= null;
final CompilationUnitRewrite existingRewrite= (CompilationUnitRewrite) fCompilationUnitRewrites.get(unit.getPrimary());
final boolean isTouched= existingRewrite != null;
if (isTouched)
currentRewrite= existingRewrite;
else
currentRewrite= new CompilationUnitRewrite(unit, node);
final Collection collection= (Collection) fTypeOccurrences.get(unit);
if (collection != null && !collection.isEmpty()) {
final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 100);
try {
subMonitor.beginTask("", collection.size() * 10); //$NON-NLS-1$
subMonitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
TType estimate= null;
ISourceConstraintVariable variable= null;
ITypeConstraintVariable constraint= null;
for (final Iterator iterator= collection.iterator(); iterator.hasNext();) {
variable= (ISourceConstraintVariable) iterator.next();
if (variable instanceof ITypeConstraintVariable) {
constraint= (ITypeConstraintVariable) variable;
estimate= (TType) constraint.getData(SuperTypeConstraintsSolver.DATA_TYPE_ESTIMATE);
if (estimate != null) {
final CompilationUnitRange range= constraint.getRange();
if (isTouched)
rewriteTypeOccurrence(range, estimate, requestor, currentRewrite, node, replacements, currentRewrite.createCategorizedGroupDescription(RefactoringCoreMessages.SuperTypeRefactoringProcessor_update_type_occurrence, SET_SUPER_TYPE));
else {
final ASTNode result= NodeFinder.perform(node, range.getSourceRange());
if (result != null)
rewriteTypeOccurrence(estimate, currentRewrite, result, currentRewrite.createCategorizedGroupDescription(RefactoringCoreMessages.SuperTypeRefactoringProcessor_update_type_occurrence, SET_SUPER_TYPE));
}
subMonitor.worked(10);
}
}
}
} finally {
subMonitor.done();
}
}
if (!isTouched) {
final TextChange change= currentRewrite.createChange();
if (change != null)
manager.manage(unit, change);
}
} finally {
monitor.done();
}
}
protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final CompilationUnitRewrite sourceRewrite, final ICompilationUnit copy, final Set replacements, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask("", 100); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.PullUpRefactoring_checking);
final IType declaring= getDeclaringType();
final IJavaProject project= declaring.getJavaProject();
final ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setWorkingCopyOwner(fOwner);
parser.setResolveBindings(true);
parser.setProject(project);
parser.setCompilerOptions(RefactoringASTParser.getCompilerOptions(project));
parser.createASTs(new ICompilationUnit[] { copy}, new String[0], new ASTRequestor() {
public final void acceptAST(final ICompilationUnit unit, final CompilationUnit node) {
try {
final IType subType= (IType) JavaModelUtil.findInCompilationUnit(unit, declaring);
final AbstractTypeDeclaration subDeclaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(subType, node);
if (subDeclaration != null) {
final ITypeBinding subBinding= subDeclaration.resolveBinding();
if (subBinding != null) {
String name= null;
ITypeBinding superBinding= null;
final ITypeBinding[] superBindings= Bindings.getAllSuperTypes(subBinding);
for (int index= 0; index < superBindings.length; index++) {
name= superBindings[index].getName();
if (name.startsWith(fDestinationType.getElementName()))
superBinding= superBindings[index];
}
if (superBinding != null) {
solveSuperTypeConstraints(unit, node, subType, subBinding, superBinding, new SubProgressMonitor(monitor, 80), status);
if (!status.hasFatalError())
rewriteTypeOccurrences(manager, this, sourceRewrite, unit, node, replacements, status, new SubProgressMonitor(monitor, 120));
}
}
}
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ExtractInterfaceProcessor_internal_error));
}
}
public final void acceptBinding(final String key, final IBinding binding) {
// Do nothing
}
}, new NullProgressMonitor());
} finally {
monitor.done();
}
}
/**
* Sets the methods to declare abstract.
*
* @param methods
* the methods to declare abstract
*/
public void setAbstractMethods(final IMethod[] methods) {
Assert.isNotNull(methods);
fAbstractMethods= methods;
}
/**
* Determines whether to create method stubs for non-implemented abstract
* methods.
*
* @param create
* <code>true</code> to create stubs, <code>false</code>
* otherwise
*/
public void setCreateMethodStubs(final boolean create) {
fCreateMethodStubs= create;
}
/**
* Sets the methods to delete
*
* @param methods
* the methods to delete
*/
public void setDeletedMethods(final IMethod[] methods) {
Assert.isNotNull(methods);
fDeletedMethods= methods;
}
/**
* Sets the destination type.
*
* @param type
* the destination type
*/
public void setDestinationType(final IType type) {
Assert.isNotNull(type);
if (!type.equals(fDestinationType))
fCachedDestinationTypeHierarchy= null;
fDestinationType= type;
}
/**
* Sets the members to move.
*
* @param members
* the members to move
*/
public void setMembersToMove(final IMember[] members) {
Assert.isNotNull(members);
fMembersToMove= (IMember[]) SourceReferenceUtil.sortByOffset(members);
}
}