/*******************************************************************************
 * Copyright (c) 2006, 2014 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
 *     Benjamin Muskalla - 228950: [pull up] exception if target calls super with multiple parameters
 *******************************************************************************/
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.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;

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.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.Name;
import org.eclipse.jdt.core.dom.NodeFinder;
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.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
import org.eclipse.jdt.core.refactoring.descriptors.PullUpDescriptor;

import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
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.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
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.DynamicValidationRefactoringChange;
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.MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment;
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.javaeditor.ASTProvider;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;

/**
 * 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;

		/** The binding of the enclosing method */
		private final IMethodBinding fEnclosingMethod;

		/**
		 * 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
		 * @param enclosing the binding of the enclosing method
		 */
		public PullUpAstNodeMapper(final CompilationUnitRewrite sourceRewriter, final CompilationUnitRewrite targetRewriter, final ASTRewrite rewrite, final IType type, final TypeVariableMaplet[] mapping, final IMethodBinding enclosing) {
			super(rewrite, mapping);
			Assert.isNotNull(rewrite);
			Assert.isNotNull(type);
			fSourceRewriter= sourceRewriter;
			fTargetRewriter= targetRewriter;
			fSuperReferenceType= type;
			fEnclosingMethod= enclosing;
		}

		@Override
		public final void endVisit(final AnonymousClassDeclaration node) {
			fAnonymousClassDeclaration= false;
			super.endVisit(node);
		}

		@Override
		public final void endVisit(final TypeDeclarationStatement node) {
			fTypeDeclarationStatement= false;
			super.endVisit(node);
		}

		@Override
		public final boolean visit(final AnonymousClassDeclaration node) {
			fAnonymousClassDeclaration= true;
			return super.visit(node);
		}

		@Override
		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;
		}

		@Override
		public final boolean visit(final SuperMethodInvocation node) {
			if (!fAnonymousClassDeclaration && !fTypeDeclarationStatement) {
				final IBinding superBinding= node.getName().resolveBinding();
				if (superBinding instanceof IMethodBinding) {
					final IMethodBinding extended= (IMethodBinding) superBinding;
					if (fEnclosingMethod != null && fEnclosingMethod.overrides(extended))
						return true;
					final ITypeBinding declaringBinding= extended.getDeclaringClass();
					if (declaringBinding != null) {
						final IType type= (IType) declaringBinding.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<Expression> arguments= node.arguments();
				if (arguments != null && arguments.size() > 0) {
					final ListRewrite rewriter= fRewrite.getListRewrite(invocation, MethodInvocation.ARGUMENTS_PROPERTY);
					ListRewrite superRewriter= fRewrite.getListRewrite(node, SuperMethodInvocation.ARGUMENTS_PROPERTY);
					ASTNode copyTarget= superRewriter.createCopyTarget(arguments.get(0), arguments.get(arguments.size() - 1));
					rewriter.insertLast(copyTarget, null);
				}
				fRewrite.replace(node, invocation, null);
				if (!fSourceRewriter.getCu().equals(fTargetRewriter.getCu()))
					fSourceRewriter.getImportRemover().registerRemovedNode(node);
				return true;
			}
			return false;
		}

		@Override
		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 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<IMember, Set<IMember>> mapping, final IMember key, final IMember matchingMember) {
		Set<IMember> matchingSet;
		if (mapping.containsKey(key)) {
			matchingSet= mapping.get(key);
		} else {
			matchingSet= new HashSet<IMember>();
			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<IType> getAffectedSubTypes(final ITypeHierarchy hierarchy, final IType type) throws JavaModelException {
		 IType[] types= null;
		 final boolean isInterface= type.isInterface();
		if (isInterface) {
			 final Collection<IType> remove= new ArrayList<IType>();
			 final List<IType> list= new ArrayList<IType>(Arrays.asList(hierarchy.getSubtypes(type)));
			 for (final Iterator<IType> iterator= list.iterator(); iterator.hasNext();) {
	            final IType element= iterator.next();
	            if (element.isInterface())
	            	remove.add(element);
            }
			 list.removeAll(remove);
			 types= list.toArray(new IType[list.size()]);
		 } else
			 types= hierarchy.getSubclasses(type);
		final Set<IType> result= new HashSet<IType>();
		for (int index= 0; index < types.length; index++) {
			if (!isInterface && JdtFlags.isAbstract(types[index]))
				result.addAll(getAffectedSubTypes(hierarchy, types[index]));
			else
				result.add(types[index]);
		}
		return result;
	}

	private static IMember[] getMembers(final IMember[] members, final int type) {
		final List<IJavaElement> list= Arrays.asList(JavaElementUtil.getElementsOfType(members, type));
		return list.toArray(new IMember[list.size()]);
	}

	private static void mergeMaps(final Map<IMember, Set<IMember>> result, final Map<IMember, Set<IMember>> map) {
		for (final Iterator<IMember> iter= result.keySet().iterator(); iter.hasNext();) {
			final IMember key= iter.next();
			if (map.containsKey(key)) {
				final Set<IMember> resultSet= result.get(key);
				final Set<IMember> mapSet= map.get(key);
				resultSet.addAll(mapSet);
			}
		}
	}

	private static void upgradeMap(final Map<IMember, Set<IMember>> result, final Map<IMember, Set<IMember>> map) {
		for (final Iterator<IMember> iter= map.keySet().iterator(); iter.hasNext();) {
			final IMember key= iter.next();
			if (!result.containsKey(key)) {
				final Set<IMember> mapSet= map.get(key);
				final Set<IMember> resultSet= new HashSet<IMember>(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<IType> fCachedSkippedSuperTypes;

	/** The map of compilation units to compilation unit rewrites */
	protected Map<ICompilationUnit, CompilationUnitRewrite> 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
	 * @param settings
	 *            the code generation settings
	 */
	public PullUpRefactoringProcessor(final IMember[] members, final CodeGenerationSettings settings) {
		this(members, settings, false);
	}

	/**
	 * Creates a new pull up processor from refactoring arguments.
	 *
	 * @param arguments
	 *            the refactoring arguments
	 * @param status
	 *            the resulting status
	 */
	public PullUpRefactoringProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) {
		this(null, null, false);
		RefactoringStatus initializeStatus= initialize(arguments);
		status.merge(initializeStatus);
	}

	/**
	 * 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<IMember> 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, 6);

			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<IMember, IncomingMemberVisibilityAdjustment> 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);
		copyExtraDimensions(methodToCreateStubFor, newMethod);
		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()));
		ImportRewriteContext context= new ContextSensitiveImportRewriteContext(typeToCreateStubIn, rewriter.getImportRewrite());
		ImportRewriteUtil.addImports(rewriter, context, newMethod, new HashMap<Name, String>(), new HashMap<Name, String>(), 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<IType> affected, final CompilationUnit root, final CompilationUnitRewrite unitRewriter, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
		final IType declaringType= getDeclaringType();
		final IMethod[] methods= getAbstractMethods();
		try {
			monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, affected.size());
			for (final Iterator<IType> iter= affected.iterator(); iter.hasNext();) {
				final IType type= 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();
		}
	}

	@Override
	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<IMember> pulledUpList= Arrays.asList(fMembersToMove);
		final List<IMember> 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<IMember> pulledUpList= Arrays.asList(fMembersToMove);
		final List<IMethod> declaredAbstractList= Arrays.asList(fAbstractMethods);
		final List<IMember> 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<IMember> 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<IMember> notDeletedMembersInSubtypes) throws JavaModelException {
		if (fDestinationType.isInterface())
			return;
		final List<IMethod> toDeclareAbstract= Arrays.asList(fAbstractMethods);
		for (final Iterator<IMember> iter= notDeletedMembersInSubtypes.iterator(); iter.hasNext();) {
			final IMember member= 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;
	}

	@Override
	protected RefactoringStatus checkDeclaringType(final IProgressMonitor monitor) throws JavaModelException {
		final RefactoringStatus status= super.checkDeclaringType(monitor);
		if (getDeclaringType().getFullyQualifiedName('.').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<IMember, Set<IMember>> 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<IMember> iter= 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}
	 */
	@Override
	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<ICompilationUnit, CompilationUnitRewrite>(3);
			result.merge(checkProjectCompliance(getCompilationUnitRewrite(fCompilationUnitRewrites, getDeclaringType().getCompilationUnit()), getDestinationType(), fMembersToMove));
			fChangeManager= createChangeManager(new SubProgressMonitor(monitor, 1), result);

			Checks.addModifiedFilesToChecker(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), context);

			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;

					String superClassLabel= BasicElementLabels.getJavaElementName(declaring.getSuperclassName());
					switch (length) {
						case 0:
							break;
						case 1:
							status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable_not_available, new String[] { unmapped[0], superClassLabel}), JavaStatusContext.create(member));
							break;
						case 2:
							status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable2_not_available, new String[] { unmapped[0], unmapped[1], superClassLabel}), JavaStatusContext.create(member));
							break;
						case 3:
							status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variable3_not_available, new String[] { unmapped[0], unmapped[1], unmapped[2], superClassLabel}), JavaStatusContext.create(member));
							break;
						default:
							status.addError(Messages.format(RefactoringCoreMessages.PullUpRefactoring_Type_variables_not_available, new String[] { superClassLabel}), 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<IType> skippedTypes= getSkippedSuperTypes(new SubProgressMonitor(monitor, 1));
			final IType[] skipped= 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}
	 */
	@Override
	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<IMember> set) throws JavaModelException {
		final IMember[] destinationMembers= getCreatedDestinationMembers();
		final List<IMember> list= new ArrayList<IMember>(destinationMembers.length);
		list.addAll(Arrays.asList(destinationMembers));
		list.addAll(set);
		list.removeAll(Arrays.asList(fDeletedMethods));
		final IMember[] members= 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<IMember> notDeletedMembers= getNotDeletedMembers(new SubProgressMonitor(monitor, 1));
		final Set<IMember> notDeletedMembersInTargetType= new HashSet<IMember>();
		final Set<IMember> notDeletedMembersInSubtypes= new HashSet<IMember>();
		for (final Iterator<IMember> iter= notDeletedMembers.iterator(); iter.hasNext();) {
			final IMember member= 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<IMember> notDeletedMembersInSubtypes) throws JavaModelException {
		final Map<IMember, Set<IMember>> 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];
			if (mapping.containsKey(method)) {
				final Set<IMember> set= mapping.get(method);
				if (set != null) {
					final String returnType= Signature.toString(Signature.getReturnType(method.getSignature()).toString());
					for (final Iterator<IMember> 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);
					}
				}
			}
		}
	}

	@Override
	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, oldMethod.resolveBinding()));
			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<IMember, IncomingMemberVisibilityAdjustment> 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);
		copyExtraDimensions(oldMethod, newMethod);
		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);
		copyTypeParameters(oldMethod, newMethod);
		ImportRewriteContext context= new ContextSensitiveImportRewriteContext(destination, targetRewrite.getImportRewrite());
		ImportRewriteUtil.addImports(targetRewrite, context, oldMethod, new HashMap<Name, String>(), new HashMap<Name, String>(), 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}
	 */
	@Override
	public Change createChange(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
		try {
			final Map<String, String> arguments= new HashMap<String, String>();
			String project= null;
			final IType declaring= getDeclaringType();
			final IJavaProject javaProject= declaring.getJavaProject();
			if (javaProject != null)
				project= javaProject.getElementName();
			int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | 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[] { JavaElementLabels.getElementLabel(fMembersToMove[0], JavaElementLabels.ALL_DEFAULT), JavaElementLabels.getElementLabel(fDestinationType, JavaElementLabels.ALL_DEFAULT)}) : Messages.format(RefactoringCoreMessages.PullUpRefactoring_descriptor_description_short_multiple, BasicElementLabels.getJavaElementName(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 JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
			comment.addSetting(Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_target_element_pattern, JavaElementLabels.getElementLabel(fDestinationType, JavaElementLabels.ALL_FULLY_QUALIFIED)));
			addSuperTypeSettings(comment, true);
			final PullUpDescriptor descriptor= RefactoringSignatureDescriptorFactory.createPullUpDescriptor(project, description, comment.asString(), arguments, flags);
			arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, 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(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (offset + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, fMembersToMove[offset]));
			arguments.put(ATTRIBUTE_DELETE, new Integer(fDeletedMethods.length).toString());
			for (int offset= 0; offset < fDeletedMethods.length; offset++)
				arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (offset + fMembersToMove.length + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, fDeletedMethods[offset]));
			arguments.put(ATTRIBUTE_ABSTRACT, new Integer(fAbstractMethods.length).toString());
			for (int offset= 0; offset < fAbstractMethods.length; offset++)
				arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (offset + fMembersToMove.length + fDeletedMethods.length + 1), JavaRefactoringDescriptorUtil.elementToHandle(project, 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<ICompilationUnit, ArrayList<IMember>> deleteMap= createMembersToDeleteMap(new SubProgressMonitor(monitor, 1));
			final Map<ICompilationUnit, ArrayList<IType>> affectedMap= createAffectedTypesMap(new SubProgressMonitor(monitor, 1));
			final ICompilationUnit[] units= getAffectedCompilationUnits(new SubProgressMonitor(monitor, 1));

			final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments= new HashMap<IMember, IncomingMemberVisibilityAdjustment>();
			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++) {
					ICompilationUnit unit= units[index];
					if (!(source.equals(unit) || target.equals(unit) || deleteMap.containsKey(unit) || affectedMap.containsKey(unit))) {
						sub.worked(10);
						continue;
					}
					CompilationUnitRewrite rewrite= getCompilationUnitRewrite(fCompilationUnitRewrites, unit);
					if (deleteMap.containsKey(unit)) {
						LinkedList<IMember> list= new LinkedList<IMember>(deleteMap.get(unit));
						if (destination.isInterface()) {
							for (final Iterator<IMember> iterator= list.iterator(); iterator.hasNext();) {
								final IMember member= 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());
						ImportRewriteContext context= new ContextSensitiveImportRewriteContext(declaration, rewrite.getImportRewrite());
						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, context, oldField.getParent(), new HashMap<Name, String>(), new HashMap<Name, String>(), 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, 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, context, oldMethod, new HashMap<Name, String>(), new HashMap<Name, String>(), 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, context, oldType, new HashMap<Name, String>(), new HashMap<Name, String>(), 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 (affectedMap.containsKey(unit))
						addNecessaryMethodStubs(affectedMap.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) {
				final Set<ICompilationUnit> set= fCompilationUnitRewrites.keySet();
				for (final Iterator<ICompilationUnit> iterator= set.iterator(); iterator.hasNext();) {
					ICompilationUnit unit= iterator.next();
					CompilationUnitRewrite rewrite= fCompilationUnitRewrites.get(unit);
					if (rewrite != null) {
						final CompilationUnitChange change= rewrite.createChange(false);
						if (change != null)
							manager.manage(unit, change);
					}
				}
				TextEdit edit= null;
				TextEditBasedChange change= null;
				final Map<ICompilationUnit, ICompilationUnit> workingcopies= new HashMap<ICompilationUnit, ICompilationUnit>();
				final IProgressMonitor subMonitor= new SubProgressMonitor(monitor, 1);
				try {
					subMonitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_checking, set.size());
					for (final Iterator<ICompilationUnit> iterator= set.iterator(); iterator.hasNext();) {
						ICompilationUnit unit= 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= workingcopies.get(sourceRewriter.getCu());
					if (current != null)
						rewriteTypeOccurrences(manager, sourceRewriter, current, new HashSet<String>(), status, new SubProgressMonitor(monitor, 16));
				} finally {
					subMonitor.done();
					ICompilationUnit[] cus= manager.getAllCompilationUnits();
					for (int index= 0; index < cus.length; index++) {
						ICompilationUnit unit= cus[index];
						CompilationUnitChange current= (CompilationUnitChange) manager.get(unit);
						if (change != null && current.getEdit() == null)
							manager.remove(unit);
					}
				}
			}
			registerChanges(manager);
			return manager;
		} finally {
			fCompilationUnitRewrites.clear();
			monitor.done();
		}
	}

	private Map<ICompilationUnit, ArrayList<IType>> createAffectedTypesMap(final IProgressMonitor monitor) throws JavaModelException {
		if (!(fCreateMethodStubs && getAbstractMethods().length > 0))
			return new HashMap<ICompilationUnit, ArrayList<IType>>(0);
		final Set<IType> affected= getAffectedSubTypes(getDestinationTypeHierarchy(monitor), getDestinationType());
		final Map<ICompilationUnit, ArrayList<IType>> result= new HashMap<ICompilationUnit, ArrayList<IType>>();
		for (final Iterator<IType> iterator= affected.iterator(); iterator.hasNext();) {
			final IType type= iterator.next();
			final ICompilationUnit unit= type.getCompilationUnit();
			if (!result.containsKey(unit))
				result.put(unit, new ArrayList<IType>(1));
			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= getDestinationType().getFullyQualifiedName('.');
				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));
				if (comment != null)
					return (Javadoc) rewrite.createStringPlaceholder(comment, ASTNode.JAVADOC);
			}
		}
		return null;
	}

	private Map<ICompilationUnit, ArrayList<IMember>> createMembersToDeleteMap(final IProgressMonitor monitor) throws JavaModelException {
		final IMember[] membersToDelete= getMembersToDelete(monitor);
		final Map<ICompilationUnit, ArrayList<IMember>> result= new HashMap<ICompilationUnit, ArrayList<IMember>>();
		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<IMember>(1));
			result.get(cu).add(member);
		}
		return result;
	}

	private MethodDeclaration createNewMethodDeclarationNode(final CompilationUnitRewrite sourceRewrite, final CompilationUnitRewrite targetRewrite, final IMethod sourceMethod, final MethodDeclaration oldMethod, final TypeVariableMaplet[] mapping, final Map<IMember, IncomingMemberVisibilityAdjustment> 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());
		copyExtraDimensions(oldMethod, newMethod);
		copyJavadocNode(rewrite, oldMethod, newMethod);
		int modifiers= getModifiersWithUpdatedVisibility(sourceMethod, sourceMethod.getFlags(), adjustments, monitor, true, status);
		if (fDeletedMethods.length == 0 || getDestinationType().isInterface()) {
			modifiers&= ~Flags.AccFinal;
		}

		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 Set<IMember> result= new LinkedHashSet<IMember>(toDeclareAbstract.length + abstractPulledUp.length + fMembersToMove.length);
		if (fDestinationType.isInterface()) {
			for (int i= 0; i < fMembersToMove.length; i++) {
				if (fMembersToMove[i].getElementType() == IJavaElement.METHOD) {
					result.add(fMembersToMove[i]);
				}
			}
		}
		result.addAll(Arrays.asList(toDeclareAbstract));
		result.addAll(Arrays.asList(abstractPulledUp));
		return result.toArray(new IMethod[result.size()]);
	}

	private IMethod[] getAbstractMethodsToPullUp() throws JavaModelException {
		final List<IMember> result= new ArrayList<IMember>(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 result.toArray(new IMethod[result.size()]);
	}

	public IMember[] getAdditionalRequiredMembersToPullUp(final IProgressMonitor monitor) throws JavaModelException {
		final IMember[] members= getCreatedDestinationMembers();
		List<IMember> queue;
		try {
			monitor.beginTask(RefactoringCoreMessages.PullUpRefactoring_calculating_required, getDeclaringType().getChildren().length);// maximum
			queue= new ArrayList<IMember>(members.length);
			queue.addAll(Arrays.asList(members));
			if (queue.isEmpty())
				return new IMember[0];
			int i= 0;
			IMember current;
			do {
				current= 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
		} finally {
			monitor.done();
		}
		return queue.toArray(new IMember[queue.size()]);
	}

	private ICompilationUnit[] getAffectedCompilationUnits(final IProgressMonitor monitor) throws JavaModelException {
		final IType[] allSubtypes= getDestinationTypeHierarchy(monitor).getAllSubtypes(getDestinationType());
		final Set<ICompilationUnit> result= new HashSet<ICompilationUnit>(allSubtypes.length);
		for (int i= 0; i < allSubtypes.length; i++) {
			ICompilationUnit cu= allSubtypes[i].getCompilationUnit();
			if (cu != null)
				result.add(cu);
		}
		result.add(getDestinationType().getCompilationUnit());
		return 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<IType> list= new ArrayList<IType>(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 list.toArray(new IType[list.size()]);
	}

	protected CompilationUnitRewrite getCompilationUnitRewrite(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final ICompilationUnit unit) {
		Assert.isNotNull(rewrites);
		Assert.isNotNull(unit);
		CompilationUnitRewrite rewrite= rewrites.get(unit);
		if (rewrite == null) {
			rewrite= new CompilationUnitRewrite(fOwner, unit);
			rewrites.put(unit, rewrite);
		}
		return rewrite;
	}

	private IMember[] getCreatedDestinationMembers() {
		final List<IMember> result= new ArrayList<IMember>(fMembersToMove.length + fAbstractMethods.length);
		result.addAll(Arrays.asList(fMembersToMove));
		result.addAll(Arrays.asList(fAbstractMethods));
		return 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}
	 */
	@Override
	public Object[] getElements() {
		return fMembersToMove;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getIdentifier() {
		return IDENTIFIER;
	}

	public IMember[] getMatchingElements(final IProgressMonitor monitor, final boolean includeAbstract) throws JavaModelException {
		try {
			final Set<IMember> result= new HashSet<IMember>();
			final IType destination= getDestinationType();
			final Map<IMember, Set<IMember>> matching= getMatchingMembers(getDestinationTypeHierarchy(monitor), getDestinationType(), includeAbstract);
			for (final Iterator<IMember> iterator= matching.keySet().iterator(); iterator.hasNext();) {
				final IMember key= iterator.next();
				Assert.isTrue(!key.getDeclaringType().equals(destination));
				result.addAll(matching.get(key));
			}
			return result.toArray(new IMember[result.size()]);
		} finally {
			monitor.done();
		}
	}

	private Map<IMember, Set<IMember>> getMatchingMembers(final ITypeHierarchy hierarchy, final IType type, final boolean includeAbstract) throws JavaModelException {
		final Map<IMember, Set<IMember>> result= new HashMap<IMember, Set<IMember>>();
		result.putAll(getMatchingMembersMapping(type));
		final IType[] subTypes= hierarchy.getAllSubtypes(type);
		for (int i= 0; i < subTypes.length; i++) {
			final Map<IMember, Set<IMember>> 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<IMember, Set<IMember>> getMatchingMembersMapping(final IType initial) throws JavaModelException {
		final Map<IMember, Set<IMember>> result= new HashMap<IMember, Set<IMember>>();
		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<IMember, IncomingMemberVisibilityAdjustment> 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<IMember> getNotDeletedMembers(final IProgressMonitor monitor) throws JavaModelException {
		final Set<IMember> matchingSet= new HashSet<IMember>();
		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}
	 */
	@Override
	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<IType> 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<IType>(2);
			IType current= hierarchy.getSuperclass(getDeclaringType());
			while (current != null && !current.equals(getDestinationType())) {
				fCachedSkippedSuperTypes.add(current);
				current= hierarchy.getSuperclass(current);
			}
			return fCachedSkippedSuperTypes;
		} finally {
			monitor.done();
		}
	}

	private RefactoringStatus initialize(final JavaRefactoringArguments extended) {
		String handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
		if (handle != null) {
			final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
			if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
				return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.PULL_UP);
			else
				fDestinationType= (IType) element;
		} else
			return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.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<IJavaElement> elements= new ArrayList<IJavaElement>();
		for (int index= 0; index < pullCount; index++) {
			final String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (index + 1);
			handle= extended.getAttribute(attribute);
			if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
				final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
				if (element == null || !element.exists())
					status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(element, getProcessorName(), IJavaRefactorings.PULL_UP));
				else
					elements.add(element);
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
		}
		fMembersToMove= elements.toArray(new IMember[elements.size()]);
		elements= new ArrayList<IJavaElement>();
		for (int index= 0; index < deleteCount; index++) {
			final String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (pullCount + index + 1);
			handle= extended.getAttribute(attribute);
			if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
				final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
				if (element == null || !element.exists())
					status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(element, getProcessorName(), IJavaRefactorings.PULL_UP));
				else
					elements.add(element);
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
		}
		fDeletedMethods= elements.toArray(new IMethod[elements.size()]);
		elements= new ArrayList<IJavaElement>();
		for (int index= 0; index < abstractCount; index++) {
			final String attribute= JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + (pullCount + abstractCount + index + 1);
			handle= extended.getAttribute(attribute);
			if (handle != null && !"".equals(handle)) { //$NON-NLS-1$
				final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
				if (element == null || !element.exists())
					status.merge(JavaRefactoringDescriptorUtil.createInputWarningStatus(element, getProcessorName(), IJavaRefactorings.PULL_UP));
				else
					elements.add(element);
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, attribute));
		}
		fAbstractMethods= 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;
		return new RefactoringStatus();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	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<IMember> 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<ICompilationUnit> iterator= fCompilationUnitRewrites.keySet().iterator(); iterator.hasNext();) {
			unit= iterator.next();
			rewrite= fCompilationUnitRewrites.get(unit);
			if (rewrite != null) {
				final CompilationUnitChange change= rewrite.createChange(true);
				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}
	 */
	@Override
	protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final ICompilationUnit unit, final CompilationUnit node, final Set<String> replacements, final IProgressMonitor monitor) throws CoreException {
		try {
			monitor.beginTask("", 100); //$NON-NLS-1$
			monitor.setTaskName(RefactoringCoreMessages.ExtractInterfaceProcessor_creating);
			CompilationUnitRewrite currentRewrite= null;
			final CompilationUnitRewrite existingRewrite= fCompilationUnitRewrites.get(unit.getPrimary());
			final boolean isTouched= existingRewrite != null;
			if (isTouched)
				currentRewrite= existingRewrite;
			else
				currentRewrite= new CompilationUnitRewrite(unit, node);
			final Collection<ITypeConstraintVariable> 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<ITypeConstraintVariable> iterator= collection.iterator(); iterator.hasNext();) {
						variable= 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(true);
				if (change != null)
					manager.manage(unit, change);
			}
		} finally {
			monitor.done();
		}
	}

	protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final CompilationUnitRewrite sourceRewrite, final ICompilationUnit copy, final Set<String> replacements, final RefactoringStatus status, final IProgressMonitor monitor) {
		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(ASTProvider.SHARED_AST_LEVEL);
			parser.setWorkingCopyOwner(fOwner);
			parser.setResolveBindings(true);
			parser.setProject(project);
			parser.setCompilerOptions(RefactoringASTParser.getCompilerOptions(project));
			parser.createASTs(new ICompilationUnit[] { copy}, new String[0], new ASTRequestor() {

				@Override
				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));
					}
				}

				@Override
				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);
	}
}