/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.code;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEditGroup;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;

import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
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.ParameterizedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;

import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
import org.eclipse.jdt.internal.corext.dom.NodeFinder;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptor;
import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ASTCreator;
import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.SearchUtils;

import org.eclipse.jdt.ui.JavaElementLabels;

import org.eclipse.jdt.internal.ui.JavaUIStatus;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;

/**
 * Refactoring class that permits the substitution of a factory method
 * for direct calls to a given constructor.
 * @author rfuhrer
 */
public class IntroduceFactoryRefactoring extends ScriptableRefactoring {

	private static final String ATTRIBUTE_PROTECT= "protect"; //$NON-NLS-1$

	/**
	 * The handle for the compilation unit holding the selection that was
	 * passed into this refactoring.
	 */
	private ICompilationUnit fCUHandle;

	/**
	 * The AST for the compilation unit holding the selection that was
	 * passed into this refactoring.
	 */
	private CompilationUnit fCU;

	/**
	 * Handle for compilation unit in which the factory method/class/interface will be
	 * generated.
	 */
	private ICompilationUnit fFactoryUnitHandle;

	/**
	 * The start of the original textual selection in effect when this refactoring
	 * was initiated. If the refactoring was initiated from a structured selection
	 * (e.g. from the outline view), then this refers to the textual selection that
	 * corresponds to the structured selection item.
	 */
	private int fSelectionStart;

	/**
	 * The length of the original textual selection in effect when this refactoring
	 * was initiated. If the refactoring was initiated from a structured selection
	 * (e.g. from the outline view), then this refers to the textual selection that
	 * corresponds to the structured selection item.
	 */
	private int fSelectionLength;

	/**
	 * The AST node corresponding to the user's textual selection.
	 */
	private ASTNode fSelectedNode;

	/**
	 * The method binding for the selected constructor.
	 */
	private IMethodBinding fCtorBinding;
	
	/**
	 * <code>TypeDeclaration</code> for class containing the constructor to be
	 * encapsulated.
	 */
	private AbstractTypeDeclaration fCtorOwningClass;

	/**
	 * The name to be given to the generated factory method.
	 */
	private String fNewMethodName= null;

	/**
	 * An array of <code>SearchResultGroup</code>'s of all call sites
	 * that refer to the constructor signature in question.
	 */
	private SearchResultGroup[] fAllCallsTo;

	/**
	 * The class that will own the factory method/class/interface.
	 */
	private AbstractTypeDeclaration fFactoryOwningClass;

	/**
	 * The newly-generated factory method.
	 */
	private MethodDeclaration fFactoryMethod= null;

	/**
	 * An array containing the names of the constructor's formal arguments,
	 * if available, otherwise "arg1" ... "argN".
	 */
	private String[] fFormalArgNames= null;

	/**
	 * An array of <code>ITypeBinding</code>'s that describes the types of
	 * the constructor arguments, in order.
	 */
	private ITypeBinding[] fArgTypes;

	/**
	 * True iff the given constructor has a varargs signature.
	 */
	private boolean fCtorIsVarArgs;

	/**
	 * If true, change the visibility of the constructor to protected to better
	 * encapsulate it.
	 */
	private boolean fProtectConstructor= true;

	/**
	 * An <code>ImportRewrite</code> that manages imports needed to satisfy
	 * newly-introduced type references in the <code>ICompilationUnit</code>
	 * currently being rewritten during <code>createChange()</code>.
	 */
	private ImportRewrite fImportRewriter;

	/**
	 * True iff there are call sites for the constructor to be encapsulated
	 * located in binary classes.
	 */
	private boolean fCallSitesInBinaryUnits;

	/**
	 * <code>CompilationUnit</code> in which the factory is to be created.
	 */
	private CompilationUnit fFactoryCU;

	/**
	 * The fully qualified name of the factory class. This is only used
	 * if invoked from a refactoring script.
	 */
	private String fFactoryClassName;
	
	private int fConstructorVisibility= Modifier.PRIVATE;

	/**
	 * Creates a new <code>IntroduceFactoryRefactoring</code> with the given selection
	 * on the given compilation unit.
	 * @param cu the <code>ICompilationUnit</code> in which the user selection was made, or <code>null</code> if invoked from scripting
	 * @param selectionStart the start of the textual selection in <code>cu</code>
	 * @param selectionLength the length of the textual selection in <code>cu</code>
	 */
	public IntroduceFactoryRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength) {
		Assert.isTrue(selectionStart  >= 0);
		Assert.isTrue(selectionLength >= 0);
		fSelectionStart= selectionStart;
		fSelectionLength= selectionLength;
		fCUHandle= cu;
		if (cu != null)
			initialize();
	}

	private void initialize() {
		fCU= ASTCreator.createAST(fCUHandle, null);
	}

	/**
	 * Finds and returns the <code>ASTNode</code> for the given source text
	 * selection, if it is an entire constructor call or the class name portion
	 * of a constructor call or constructor declaration, or null otherwise.
	 * @param unit The compilation unit in which the selection was made 
	 * @param offset The textual offset of the start of the selection
	 * @param length The length of the selection in characters
	 * @return ClassInstanceCreation or MethodDeclaration
	 */
	private ASTNode getTargetNode(ICompilationUnit unit, int offset, int length) {
		ASTNode node= ASTNodes.getNormalizedNode(NodeFinder.perform(fCU, offset, length));
		if (node.getNodeType() == ASTNode.CLASS_INSTANCE_CREATION)
			return node;
		if (node.getNodeType() == ASTNode.METHOD_DECLARATION && ((MethodDeclaration)node).isConstructor())
			return node;
		// we have some sub node. Make sure its the right child of the parent
		StructuralPropertyDescriptor location= node.getLocationInParent();
		ASTNode parent= node.getParent();
		if (location == ClassInstanceCreation.TYPE_PROPERTY) {
			return parent;
		} else if (location == MethodDeclaration.NAME_PROPERTY && ((MethodDeclaration)parent).isConstructor()) {
			return parent;
		}
		return null;
	}

	/**
	 * Determines what kind of AST node was selected, and returns an error status
	 * if the kind of node is inappropriate for this refactoring.
	 * @param pm
	 * @return a RefactoringStatus indicating whether the selection is valid
	 * @throws JavaModelException
	 */
	private RefactoringStatus checkSelection(IProgressMonitor pm) throws JavaModelException {
		try {
			pm.beginTask(RefactoringCoreMessages.IntroduceFactory_examiningSelection, 2); 
	
			fSelectedNode= getTargetNode(fCUHandle, fSelectionStart, fSelectionLength);
	
			if (fSelectedNode == null)
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_notAConstructorInvocation); 
	
			// getTargetNode() must return either a ClassInstanceCreation or a
			// constructor MethodDeclaration; nothing else.
			if (fSelectedNode instanceof ClassInstanceCreation) {
				ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation)fSelectedNode;
				fCtorBinding= classInstanceCreation.resolveConstructorBinding();
			} else if (fSelectedNode instanceof MethodDeclaration) {
				MethodDeclaration methodDeclaration= (MethodDeclaration)fSelectedNode;
				fCtorBinding= methodDeclaration.resolveBinding();
			}
	
			if (fCtorBinding == null)
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_unableToResolveConstructorBinding); 

			// If this constructor is of a generic type, get the generic version,
			// not some instantiation thereof.
			fCtorBinding= fCtorBinding.getMethodDeclaration();

			if (fNewMethodName == null)
				fNewMethodName= "create" + fCtorBinding.getName();//$NON-NLS-1$
	
			pm.worked(1);
	
			// We don't handle constructors of nested types at the moment
			if (fCtorBinding.getDeclaringClass().isNested())
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_unsupportedNestedTypes); 
	
			ITypeBinding	ctorType= fCtorBinding.getDeclaringClass();
			IType			ctorOwningType= (IType) ctorType.getJavaElement();
	
			if (ctorOwningType.isBinary())
				// Can't modify binary CU; don't know what CU to put factory method
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_constructorInBinaryClass); 
			if (ctorOwningType.isEnum())
				// Doesn't make sense to encapsulate enum constructors
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_constructorInEnum); 
	
			// Put the generated factory method inside the type that owns the constructor
			fFactoryUnitHandle= ctorOwningType.getCompilationUnit();
			fFactoryCU= getASTFor(fFactoryUnitHandle);
	
			Name	ctorOwnerName= (Name) NodeFinder.perform(fFactoryCU, ctorOwningType.getNameRange());
	
			fCtorOwningClass= (AbstractTypeDeclaration) ASTNodes.getParent(ctorOwnerName, AbstractTypeDeclaration.class);
			fFactoryOwningClass= fCtorOwningClass;
	
			pm.worked(1);
	
			return new RefactoringStatus();
		} finally {
			pm.done();
		}
	}

	/*
	 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
	 */
	public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
		try {
			pm.beginTask(RefactoringCoreMessages.IntroduceFactory_checkingActivation, 1); 
	
			if (!fCUHandle.isStructureKnown())
				return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_syntaxError); 
	
			return checkSelection(new SubProgressMonitor(pm, 1));
		} finally {
			pm.done();
		}
	}

	/**
	 * Returns the set of compilation units that will be affected by this
	 * particular invocation of this refactoring. This in general includes
	 * the class containing the constructor in question, as well as all
	 * call sites to the constructor.
	 * @return ICompilationUnit[]
	 */
	private ICompilationUnit[] collectAffectedUnits(SearchResultGroup[] searchHits) {
		Collection	result= new ArrayList();
		boolean hitInFactoryClass= false;

		for(int i=0; i < searchHits.length; i++) {
			SearchResultGroup	rg=  searchHits[i];
			ICompilationUnit	icu= rg.getCompilationUnit();

			result.add(icu);
			if (icu.equals(fFactoryUnitHandle))
				hitInFactoryClass= true;
		}
		if (!hitInFactoryClass)
			result.add(fFactoryUnitHandle);
		return (ICompilationUnit[]) result.toArray(new ICompilationUnit[result.size()]);
	}

	/**
	 * Returns a <code>SearchPattern</code> that finds all calls to the constructor
	 * identified by the argument <code>methodBinding</code>.
	 */
	private SearchPattern createSearchPattern(IMethod ctor, IMethodBinding methodBinding) {
		Assert.isNotNull(methodBinding,
				RefactoringCoreMessages.IntroduceFactory_noBindingForSelectedConstructor); 
		
		if (ctor != null)
			return SearchPattern.createPattern(ctor, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
		else { // perhaps a synthetic method? (but apparently not always... hmmm...)
			// Can't find an IMethod for this method, so build a string pattern instead
			StringBuffer	buf= new StringBuffer();

			buf.append(methodBinding.getDeclaringClass().getQualifiedName())
			   .append("(");//$NON-NLS-1$
			for(int i=0; i < fArgTypes.length; i++) {
				if (i != 0)
					buf.append(","); //$NON-NLS-1$
				buf.append(fArgTypes[i].getQualifiedName());
			}
			buf.append(")"); //$NON-NLS-1$
			return SearchPattern.createPattern(buf.toString(), IJavaSearchConstants.CONSTRUCTOR,
					IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
		}
	}
	
	private IJavaSearchScope createSearchScope(IMethod ctor, IMethodBinding binding) throws JavaModelException {
		if (ctor != null) {
			return RefactoringScopeFactory.create(ctor);
		} else {
			ITypeBinding type= Bindings.getTopLevelType(binding.getDeclaringClass());
			return RefactoringScopeFactory.create(type.getJavaElement());
		}
	}

	/**
	 * Returns an array of <code>SearchResultGroup</code>'s like the argument,
	 * but omitting those groups that have no corresponding compilation unit
	 * (i.e. are binary and therefore can't be modified).
	 */
	private SearchResultGroup[] excludeBinaryUnits(SearchResultGroup[] groups) {
		Collection/*<SearchResultGroup>*/	result= new ArrayList();

		for (int i = 0; i < groups.length; i++) {
			SearchResultGroup	rg=   groups[i];
			ICompilationUnit	unit= rg.getCompilationUnit();

			if (unit != null)	// Ignore hits within a binary unit
				result.add(rg);
			else
				fCallSitesInBinaryUnits= true;
		}
		return (SearchResultGroup[]) result.toArray(new SearchResultGroup[result.size()]);
	}

	/**
	 * Search for all calls to the given <code>IMethodBinding</code> in the project
	 * that contains the compilation unit <code>fCUHandle</code>.
	 * @param methodBinding
	 * @param pm
	 * @param status
	 * @return an array of <code>SearchResultGroup</code>'s that identify the search matches
	 * @throws JavaModelException
	 */
	private SearchResultGroup[] searchForCallsTo(IMethodBinding methodBinding, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
		IMethod method= (IMethod) methodBinding.getJavaElement();
		final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(createSearchPattern(method, methodBinding));
		engine.setFiltering(true, true);
		engine.setScope(createSearchScope(method, methodBinding));
		engine.setStatus(status);
		engine.searchPattern(new SubProgressMonitor(pm, 1));
		return (SearchResultGroup[]) engine.getResults();
	}

	/**
	 * Returns an array of <code>SearchResultGroup</code>'s containing all method
	 * calls in the Java project that invoke the constructor identified by the given
	 * <code>IMethodBinding</code>
	 * @param ctorBinding an <code>IMethodBinding</code> identifying a particular
	 * constructor signature to search for
	 * @param pm an <code>IProgressMonitor</code> to use during this potentially
	 * lengthy operation
	 * @param status
	 * @return an array of <code>SearchResultGroup</code>'s identifying all
	 * calls to the given constructor signature
	 */
	private SearchResultGroup[] findAllCallsTo(IMethodBinding ctorBinding, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
		SearchResultGroup[] groups= excludeBinaryUnits(searchForCallsTo(ctorBinding, pm, status));

		return groups;
	}

	private IType findNonPrimaryType(String fullyQualifiedName, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
		SearchPattern p= SearchPattern.createPattern(fullyQualifiedName, IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
		final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(p);

		engine.setFiltering(true, true);
		engine.setScope(RefactoringScopeFactory.create(fCtorBinding.getJavaElement().getJavaProject()));
		engine.setStatus(status);
		engine.searchPattern(new SubProgressMonitor(pm, 1));

		SearchResultGroup[] groups= (SearchResultGroup[]) engine.getResults();

		if (groups.length != 0) {
			for(int i= 0; i < groups.length; i++) {
				SearchMatch[] matches= groups[i].getSearchResults();
				for(int j= 0; j < matches.length; j++) {
					if (matches[j].getAccuracy() == SearchMatch.A_ACCURATE)
						return (IType) matches[j].getElement();
				}
			}
		}
		return null;
	}

	/*
	 * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
	 */
	public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
		try {
			pm.beginTask(RefactoringCoreMessages.IntroduceFactory_checking_preconditions, 1); 
			RefactoringStatus result= new RefactoringStatus();
			
			if (fFactoryClassName != null)
				result.merge(setFactoryClass(fFactoryClassName));
			if (result.hasFatalError())
				return result;
			fArgTypes= fCtorBinding.getParameterTypes();
			fCtorIsVarArgs= fCtorBinding.isVarargs();
			fAllCallsTo= findAllCallsTo(fCtorBinding, pm, result);
			fFormalArgNames= findCtorArgNames();
 
			ICompilationUnit[]	affectedFiles= collectAffectedUnits(fAllCallsTo);
			result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(affectedFiles), getValidationContext()));

			if (fCallSitesInBinaryUnits)
				result.merge(RefactoringStatus.createWarningStatus(RefactoringCoreMessages.IntroduceFactory_callSitesInBinaryClass)); 

			return result;
		} finally {
			pm.done();
		}
	}

	/**
	 * Returns an array containing the argument names for the constructor
	 * identified by <code>fCtorBinding</code>, if available, or default
	 * names if unavailable (e.g. if the constructor resides in a binary unit).
	 */
	private String[] findCtorArgNames() {
		int			numArgs= fCtorBinding.getParameterTypes().length;
		String[]	names= new String[numArgs];

		CompilationUnit		ctorUnit= (CompilationUnit) ASTNodes.getParent(fCtorOwningClass, CompilationUnit.class);
		MethodDeclaration	ctorDecl= (MethodDeclaration) ctorUnit.findDeclaringNode(fCtorBinding.getKey());

		if (ctorDecl != null) {
			List	formalArgs= ctorDecl.parameters();
			int		i= 0;

			for(Iterator iter= formalArgs.iterator(); iter.hasNext(); i++) {
				SingleVariableDeclaration	svd= (SingleVariableDeclaration) iter.next();

				names[i]= svd.getName().getIdentifier();
			}
			return names;
		}

		// Have no way of getting the formal argument names; just fake it.
		for(int i=0; i < numArgs; i++)
			names[i]= "arg" + (i+1); //$NON-NLS-1$

		return names;
	}

	/**
	 * Creates and returns a new MethodDeclaration that represents the factory
	 * method to be used in place of direct calls to the constructor in question.
	 * @param ast An AST used as a factory for various AST nodes
	 * @param ctorBinding binding for the constructor being wrapped
	 * @param unitRewriter the ASTRewrite to be used
	 */
	private MethodDeclaration createFactoryMethod(AST ast, IMethodBinding ctorBinding, ASTRewrite unitRewriter) {
		MethodDeclaration		newMethod= ast.newMethodDeclaration();
		SimpleName				newMethodName= ast.newSimpleName(fNewMethodName);
		ClassInstanceCreation	newCtorCall= ast.newClassInstanceCreation();
		ReturnStatement			ret= ast.newReturnStatement();
		Block		body= ast.newBlock();
		List		stmts= body.statements();
		String		retTypeName= ctorBinding.getName();

		createFactoryMethodSignature(ast, newMethod);

		newMethod.setName(newMethodName);
		newMethod.setBody(body);

        ITypeBinding[] ctorOwnerTypeParameters= fCtorBinding.getDeclaringClass().getTypeParameters();

        setMethodReturnType(newMethod, retTypeName, ctorOwnerTypeParameters, ast);

		newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.STATIC | Modifier.PUBLIC));

        setCtorTypeArguments(newCtorCall, retTypeName, ctorOwnerTypeParameters, ast);

        createFactoryMethodConstructorArgs(ast, newCtorCall);

		ret.setExpression(newCtorCall);
		stmts.add(ret);

		return newMethod;
	}

	/**
	 * Sets the type being instantiated in the given constructor call, including
     * specifying any necessary type arguments.
	 * @param newCtorCall the constructor call to modify
	 * @param ctorTypeName the simple name of the type being instantiated
	 * @param ctorOwnerTypeParameters the formal type parameters of the type being
	 * instantiated
	 * @param ast utility object used to create AST nodes
	 */
	private void setCtorTypeArguments(ClassInstanceCreation newCtorCall, String ctorTypeName, ITypeBinding[] ctorOwnerTypeParameters, AST ast) {
        if (ctorOwnerTypeParameters.length == 0) // easy, just a simple type
            newCtorCall.setType(ASTNodeFactory.newType(ast, ctorTypeName));
        else {
            Type baseType= ast.newSimpleType(ast.newSimpleName(ctorTypeName));
            ParameterizedType newInstantiatedType= ast.newParameterizedType(baseType);
            List/*<Type>*/ newInstTypeArgs= newInstantiatedType.typeArguments();

            for(int i= 0; i < ctorOwnerTypeParameters.length; i++) {
                Type typeArg= ASTNodeFactory.newType(ast, ctorOwnerTypeParameters[i].getName());

                newInstTypeArgs.add(typeArg);
            }
            newCtorCall.setType(newInstantiatedType);
        }
	}

	/**
	 * Sets the return type of the factory method, including any necessary type
	 * arguments. E.g., for constructor <code>Foo()</code> in <code>Foo&lt;T&gt;</code>,
	 * the factory method defines a method type parameter <code>&lt;T&gt;</code> and
	 * returns a <code>Foo&lt;T&gt;</code>.
	 * @param newMethod the method whose return type is to be set
	 * @param retTypeName the simple name of the return type (without type parameters)
	 * @param ctorOwnerTypeParameters the formal type parameters of the type that the
	 * factory method instantiates (whose constructor is being encapsulated)
	 * @param ast utility object used to create AST nodes
	 */
	private void setMethodReturnType(MethodDeclaration newMethod, String retTypeName, ITypeBinding[] ctorOwnerTypeParameters, AST ast) {
        if (ctorOwnerTypeParameters.length == 0)
            newMethod.setReturnType2(ast.newSimpleType(ast.newSimpleName(retTypeName)));
        else {
            Type baseType= ast.newSimpleType(ast.newSimpleName(retTypeName));
            ParameterizedType newRetType= ast.newParameterizedType(baseType);
            List/*<Type>*/ newRetTypeArgs= newRetType.typeArguments();

            for(int i= 0; i < ctorOwnerTypeParameters.length; i++) {
                Type retTypeArg= ASTNodeFactory.newType(ast, ctorOwnerTypeParameters[i].getName());

                newRetTypeArgs.add(retTypeArg);
            }
            newMethod.setReturnType2(newRetType);
        }
	}

	/**
	 * Creates and adds the necessary argument declarations to the given factory method.<br>
	 * An argument is needed for each original constructor argument for which the
	 * evaluation of the actual arguments across all calls was not able to be
	 * pushed inside the factory method (e.g. arguments with side-effects, references
	 * to fields if the factory method is to be static or reside in a factory class,
	 * or arguments that varied across the set of constructor calls).<br>
	 * <code>fArgTypes</code> identifies such arguments by a <code>null</code> value.
	 * @param ast utility object used to create AST nodes
	 * @param newMethod the <code>MethodDeclaration</code> for the factory method
	 */
	private void createFactoryMethodSignature(AST ast, MethodDeclaration newMethod) {
		List argDecls= newMethod.parameters();

		for(int i=0; i < fArgTypes.length; i++) {
			SingleVariableDeclaration argDecl= ast.newSingleVariableDeclaration();
			Type argType;

			if (i == (fArgTypes.length - 1) && fCtorIsVarArgs) {
				// The trailing varargs arg has an extra array dimension, compared to
				// what we need to pass to setType()...
				argType= typeNodeForTypeBinding(fArgTypes[i].getElementType(),
						fArgTypes[i].getDimensions()-1, ast);
				argDecl.setVarargs(true);
			} else
				argType= typeNodeForTypeBinding(fArgTypes[i], 0, ast);

			argDecl.setName(ast.newSimpleName(fFormalArgNames[i]));
			argDecl.setType(argType);
			argDecls.add(argDecl);
		}

		ITypeBinding[] ctorExcepts= fCtorBinding.getExceptionTypes();
		List exceptions= newMethod.thrownExceptions();

		for(int i=0; i < ctorExcepts.length; i++) {
			String excName= fImportRewriter.addImport(ctorExcepts[i]);

			exceptions.add(ASTNodeFactory.newName(ast, excName));
		}

        copyTypeParameters(ast, newMethod);
	}

	/**
	 * Copies the constructor's parent type's type parameters, if any, as
	 * method type parameters of the new static factory method. (Recall
	 * that static methods can't refer to type arguments of the enclosing
	 * class, since they have no instance to serve as a context.)<br>
	 * Makes sure to copy the bounds from the owning type, to ensure that the
	 * return type of the factory method satisfies the bounds of the type
	 * being instantiated.<br>
	 * E.g., for ctor Foo() in the type Foo<T extends Number>, be sure that
	 * the factory method is declared as<br>
	 * <code>static <T extends Number> Foo<T> createFoo()</code><br>
	 * and not simply<br>
	 * <code>static <T> Foo<T> createFoo()</code><br>
	 * or the compiler will bark.
	 * @param ast utility object needed to create ASTNode's for the new method
	 * @param newMethod the method onto which to copy the type parameters
	 */
	private void copyTypeParameters(AST ast, MethodDeclaration newMethod) {
		ITypeBinding[] ctorOwnerTypeParms= fCtorBinding.getDeclaringClass().getTypeParameters();
		List/*<TypeParameter>*/ factoryMethodTypeParms= newMethod.typeParameters();
		for(int i= 0; i < ctorOwnerTypeParms.length; i++) {
            TypeParameter newParm= ast.newTypeParameter();
            ITypeBinding[] parmTypeBounds= ctorOwnerTypeParms[i].getTypeBounds();
            List/*<Type>*/ newParmBounds= newParm.typeBounds();

            newParm.setName(ast.newSimpleName(ctorOwnerTypeParms[i].getName()));
            for(int b=0; b < parmTypeBounds.length; b++) {
            	if (parmTypeBounds[b].isClass() && parmTypeBounds[b].getSuperclass() == null)
            		continue;

            	Type newBound= fImportRewriter.addImport(parmTypeBounds[b], ast);

                newParmBounds.add(newBound);
            }
			factoryMethodTypeParms.add(newParm);
		}
	}

	/**
	 * Returns a Type that describes the given ITypeBinding. If the binding
	 * refers to an object type, use the import rewriter to determine whether
	 * the reference requires a new import, or instead needs to be qualified.<br>
	 * Like ASTNodeFactory.newType(), but for the handling of imports.
	 * @param extraDims number of extra array dimensions to add to the resulting type
	 */
	private Type typeNodeForTypeBinding(ITypeBinding argType, int extraDims, AST ast) {
		if (extraDims > 0) {
			return ast.newArrayType(typeNodeForTypeBinding(argType, 0, ast), extraDims);
			
		} else if (argType.isArray()) {
			Type elementType= typeNodeForTypeBinding(argType.getElementType(), extraDims, ast);
			return ast.newArrayType(elementType, argType.getDimensions());
			
		} else {
			return fImportRewriter.addImport(argType, ast);
		}
	}

	/**
	 * Create the list of actual arguments to the constructor call that is
	 * encapsulated inside the factory method, and associate the arguments
	 * with the given constructor call object.
	 * @param ast utility object used to create AST nodes
	 * @param newCtorCall the newly-generated constructor call to be wrapped inside
	 * the factory method
	 */
	private void createFactoryMethodConstructorArgs(AST ast, ClassInstanceCreation newCtorCall) {
		List	argList= newCtorCall.arguments();

		for(int i=0; i < fArgTypes.length; i++) {
			ASTNode	ctorArg= ast.newSimpleName(fFormalArgNames[i]);

			argList.add(ctorArg);
		}
	}

	/**
	 * Creates and returns a new MethodInvocation node to represent a call to
	 * the factory method that replaces a direct constructor call.<br>
	 * The original constructor call is marked as replaced by the new method
	 * call with the ASTRewrite instance fCtorCallRewriter.
	 * @param ast utility object used to create AST nodes
	 * @param ctorCall the ClassInstanceCreation to be marked as replaced
	 */
	private MethodInvocation createFactoryMethodCall(AST ast, ClassInstanceCreation ctorCall,
													 ASTRewrite unitRewriter, TextEditGroup gd) {
		MethodInvocation	factoryMethodCall= ast.newMethodInvocation();

		List	actualFactoryArgs= factoryMethodCall.arguments();
		List	actualCtorArgs= ctorCall.arguments();

		// Need to use a qualified name for the factory method if we're not
		// in the context of the class holding the factory.
		AbstractTypeDeclaration	callOwner= (AbstractTypeDeclaration) ASTNodes.getParent(ctorCall, AbstractTypeDeclaration.class);
		ITypeBinding callOwnerBinding= callOwner.resolveBinding();

		if (callOwnerBinding == null ||
			!Bindings.equals(callOwner.resolveBinding(), fFactoryOwningClass.resolveBinding())) {
			String qualifier= fImportRewriter.addImport(fFactoryOwningClass.resolveBinding());
			factoryMethodCall.setExpression(ASTNodeFactory.newName(ast, qualifier));
		}
		
		factoryMethodCall.setName(ast.newSimpleName(fNewMethodName));

		for(int i=0; i < actualCtorArgs.size(); i++) {
			Expression	actualCtorArg= (Expression) actualCtorArgs.get(i);
			ASTNode		movedArg= unitRewriter.createMoveTarget(actualCtorArg);

			actualFactoryArgs.add(movedArg);
//			unitRewriter.createMove(actualCtorArg);
//			ASTNode		rewrittenArg= rewriteArgument(actualCtorArg);
//			actualFactoryArgs.add(rewrittenArg);
		}

		unitRewriter.replace(ctorCall, factoryMethodCall, gd);

		return factoryMethodCall;
	}

	/**
	 * Returns true iff the given <code>ICompilationUnit</code> is the unit
	 * containing the original constructor.
	 * @param unit
	 */
	private boolean isConstructorUnit(ICompilationUnit unit) {
		return unit.equals(ASTCreator.getCu(fCtorOwningClass));
	}

	/**
	 * Returns true iff we should actually change the original constructor's
	 * visibility to <code>protected</code>. This takes into account the user-
	 * requested mode and whether the constructor's compilation unit is in
	 * source form.
	 */
	private boolean shouldProtectConstructor() {
		return fProtectConstructor && fCtorOwningClass != null;
	}

	/**
	 * Creates and adds the necessary change to make the constructor method protected.
	 * Returns false iff the constructor didn't exist (i.e. was implicit)
	 */
	private boolean protectConstructor(CompilationUnit unitAST, ASTRewrite unitRewriter, TextEditGroup declGD) {
		MethodDeclaration constructor= (MethodDeclaration) unitAST.findDeclaringNode(fCtorBinding.getKey());

		// No need to rewrite the modifiers if the visibility is what we already want it to be.
		if (constructor == null || (JdtFlags.getVisibilityCode(constructor)) == fConstructorVisibility)
			return false;
		ModifierRewrite.create(unitRewriter, constructor).setVisibility(fConstructorVisibility, declGD);
		return true;
	}

	/**
	 * Add all changes necessary on the <code>ICompilationUnit</code> in the given
	 * <code>SearchResultGroup</code> to implement the refactoring transformation
	 * to the given <code>CompilationUnitChange</code>.
	 * @param rg the <code>SearchResultGroup</code> for which changes should be created
	 * @param unitChange the CompilationUnitChange object for the compilation unit in question
	 * @throws CoreException
	 */
	private boolean addAllChangesFor(SearchResultGroup rg, ICompilationUnit	unitHandle, CompilationUnitChange unitChange) throws CoreException {
//		ICompilationUnit	unitHandle= rg.getCompilationUnit();
		Assert.isTrue(rg == null || rg.getCompilationUnit() == unitHandle);
		CompilationUnit		unit= getASTFor(unitHandle);
		ASTRewrite			unitRewriter= ASTRewrite.create(unit.getAST());
		MultiTextEdit		root= new MultiTextEdit();
		boolean				someChange= false;

		unitChange.setEdit(root);
		fImportRewriter= StubUtility.createImportRewrite(unit, true);

		// First create the factory method
		if (unitHandle.equals(fFactoryUnitHandle)) {
			TextEditGroup	factoryGD= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_addFactoryMethod); 

			createFactoryChange(unitRewriter, unit, factoryGD);
			unitChange.addTextEditGroup(factoryGD);
			someChange= true;
		}

		// Now rewrite all the constructor calls to use the factory method
		if (rg != null)
			if (replaceConstructorCalls(rg, unit, unitRewriter, unitChange))
				someChange= true;

		// Finally, make the constructor private, if requested.
		if (shouldProtectConstructor() && isConstructorUnit(unitHandle)) {
			TextEditGroup	declGD= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_protectConstructor); 

			if (protectConstructor(unit, unitRewriter, declGD)) {
				unitChange.addTextEditGroup(declGD);
				someChange= true;
			}
		}

		if (someChange) {
			root.addChild(unitRewriter.rewriteAST());
			root.addChild(fImportRewriter.rewriteImports(null));
		}

		return someChange;
	}

	/**
	 * Returns an AST for the given compilation unit handle.<br>
	 * If this is the unit containing the selection or the unit in which the factory
	 * is to reside, checks the appropriate field (<code>fCU</code> or <code>fFactoryCU</code>,
	 * respectively) and initializes the field with a new AST only if not already done.
	 */
	private CompilationUnit getASTFor(ICompilationUnit unitHandle) {
		if (unitHandle.equals(fCUHandle)) { // is this the unit containing the selection?
			if (fCU == null) {
				fCU= ASTCreator.createAST(unitHandle, null);
				if (fCU.equals(fFactoryUnitHandle)) // if selection unit and factory unit are the same...
					fFactoryCU= fCU; // ...make sure the factory unit gets initialized
			}
			return fCU;
		} else if (unitHandle.equals(fFactoryUnitHandle)) { // is this the "factory unit"?
			if (fFactoryCU == null)
				fFactoryCU= ASTCreator.createAST(unitHandle, null);
			return fFactoryCU;
		} else
			return ASTCreator.createAST(unitHandle, null);
	}

	/**
	 * Use the given <code>ASTRewrite</code> to replace direct calls to the constructor
	 * with calls to the newly-created factory method.
	 * @param rg the <code>SearchResultGroup</code> indicating all of the constructor references
	 * @param unit the <code>CompilationUnit</code> to be rewritten
	 * @param unitRewriter the rewriter
	 * @param unitChange the compilation unit change
	 * @throws CoreException
	 * @return true iff at least one constructor call site was rewritten.
	 */
	private boolean replaceConstructorCalls(SearchResultGroup rg, CompilationUnit unit,
											ASTRewrite unitRewriter, CompilationUnitChange unitChange)
	throws CoreException {
		Assert.isTrue(ASTCreator.getCu(unit).equals(rg.getCompilationUnit()));
		SearchMatch[]	hits= rg.getSearchResults();
		AST	ctorCallAST= unit.getAST();
		boolean someCallPatched= false;

		for(int i=0; i < hits.length; i++) {
			ClassInstanceCreation	creation= getCtorCallAt(hits[i].getOffset(), hits[i].getLength(), unit);

			if (creation != null) {
				TextEditGroup gd= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_replaceCalls); 

				createFactoryMethodCall(ctorCallAST, creation, unitRewriter, gd);
				unitChange.addTextEditGroup(gd);
				someCallPatched= true;
			}
		}
		return someCallPatched;
	}

	/**
	 * Look "in the vicinity" of the given range to find the <code>ClassInstanceCreation</code>
	 * node that this search hit identified. Necessary because the <code>SearchEngine</code>
	 * doesn't always cough up text extents that <code>NodeFinder.perform()</code> agrees with.
	 * @param start
	 * @param length
	 * @param unitAST
	 * @return may return null if this is really a constructor->constructor call (e.g. "this(...)")
	 */
	private ClassInstanceCreation getCtorCallAt(int start, int length, CompilationUnit unitAST) throws CoreException {
		ICompilationUnit unitHandle= ASTCreator.getCu(unitAST);
		ASTNode node= NodeFinder.perform(unitAST, start, length);

		if (node == null)
			throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
					Messages.format(RefactoringCoreMessages.IntroduceFactory_noASTNodeForConstructorSearchHit, 
							new Object[] { Integer.toString(start), Integer.toString(start + length),
								unitHandle.getSource().substring(start, start + length),
								unitHandle.getElementName() }),
					null));

		if (node instanceof ClassInstanceCreation) {
			return (ClassInstanceCreation) node;
		} else if (node instanceof VariableDeclaration) {
			Expression	init= ((VariableDeclaration) node).getInitializer();

			if (init instanceof ClassInstanceCreation) {
				return (ClassInstanceCreation) init;
			} else if (init != null)
				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
						Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedInitializerNodeType, 
								new Object[] { init.toString(), unitHandle.getElementName() }),
						null));
			else
				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
						Messages.format(RefactoringCoreMessages.IntroduceFactory_noConstructorCallNodeInsideFoundVarbleDecl, 
								new Object[] { node.toString() }),
						null));
		} else if (node instanceof ConstructorInvocation) {
			// This is a call we can bypass; it's from one constructor flavor
			// to another flavor on the same class.
			return null;
		} else if (node instanceof SuperConstructorInvocation) {
			// This is a call we can bypass; it's from one constructor flavor
			// to another flavor on the same class.
			fConstructorVisibility= Modifier.PROTECTED;
			return null;
		} else if (node instanceof ExpressionStatement) {
			Expression	expr= ((ExpressionStatement) node).getExpression();

			if (expr instanceof ClassInstanceCreation)
				return (ClassInstanceCreation) expr;
			else
				throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
						Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit, 
								new Object[] { expr.toString(), unitHandle.getElementName() }),
						null));
		} else if (node instanceof SimpleName && (node.getParent() instanceof MethodDeclaration || node.getParent() instanceof AbstractTypeDeclaration)) {
			// We seem to have been given a hit for an implicit call to the base-class constructor.
			// Do nothing with this (implicit) call, but have to make sure we make the derived class
			// doesn't lose access to the base-class constructor (so make it 'protected', not 'private').
			fConstructorVisibility= Modifier.PROTECTED;
			return null;
		} else
			throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
					Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit, 
							new Object[] { node.getClass().getName() + "('" + node.toString() + "')", unitHandle.getElementName() }), //$NON-NLS-1$ //$NON-NLS-2$
					null));
	}

	/**
	 * Perform the AST rewriting necessary on the given <code>CompilationUnit</code>
	 * to create the factory method. The method will reside on the type identified by
	 * <code>fFactoryOwningClass</code>.
	 * @param unitRewriter
	 * @param unit
	 * @param gd the <code>GroupDescription</code> to associate with the changes made
	 */
	private void createFactoryChange(ASTRewrite unitRewriter, CompilationUnit unit, TextEditGroup gd) {
		// ================================================================================
		// First add the factory itself (method, class, and interface as needed/directed by user)
		AST				ast= unit.getAST();

		fFactoryMethod= createFactoryMethod(ast, fCtorBinding, unitRewriter);

		AbstractTypeDeclaration	factoryOwner= (AbstractTypeDeclaration) unit.findDeclaringNode(fFactoryOwningClass.resolveBinding().getKey());
		fImportRewriter.addImport(fCtorOwningClass.resolveBinding());

		int	idx= ASTNodes.getInsertionIndex(fFactoryMethod, factoryOwner.bodyDeclarations());

		if (idx < 0) idx= 0; // Guard against bug in getInsertionIndex()
		unitRewriter.getListRewrite(factoryOwner, factoryOwner.getBodyDeclarationsProperty()).insertAt(fFactoryMethod, idx, gd);
	}

	public Change createChange(IProgressMonitor pm) throws CoreException {
		try {
			pm.beginTask(RefactoringCoreMessages.IntroduceFactory_createChanges, fAllCallsTo.length);
			final ITypeBinding binding= fFactoryOwningClass.resolveBinding();
			final Map arguments= new HashMap();
			String project= null;
			IJavaProject javaProject= fCUHandle.getJavaProject();
			if (javaProject != null)
				project= javaProject.getElementName();
			int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
			if (binding.isNested() && !binding.isMember())
				flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
			final String description= Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_descriptor_description_short, fCtorOwningClass.getName());
			final String header= Messages.format(RefactoringCoreMessages.IntroduceFactory_descriptor_description, new String[] { fNewMethodName, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(fCtorBinding, JavaElementLabels.ALL_FULLY_QUALIFIED)});
			final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
			comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(fCtorBinding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
			comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_factory_pattern, fNewMethodName));
			comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_owner_pattern, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
			if (fProtectConstructor)
				comment.addSetting(RefactoringCoreMessages.IntroduceFactoryRefactoring_declare_private);
			final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaRefactorings.INTRODUCE_FACTORY, project, description, comment.asString(), arguments, flags);
			arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCUHandle));
			arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fNewMethodName);
			arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1, descriptor.elementToHandle(binding.getJavaElement()));
			arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
			arguments.put(ATTRIBUTE_PROTECT, Boolean.valueOf(fProtectConstructor).toString());
			final DynamicValidationStateChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.IntroduceFactory_name);
			boolean hitInFactoryClass= false;
			boolean hitInCtorClass= false;
			for (int i= 0; i < fAllCallsTo.length; i++) {
				SearchResultGroup rg= fAllCallsTo[i];
				ICompilationUnit unitHandle= rg.getCompilationUnit();
				CompilationUnitChange cuChange= new CompilationUnitChange(getName(), unitHandle);

				if (addAllChangesFor(rg, unitHandle, cuChange))
					result.add(cuChange);

				if (unitHandle.equals(fFactoryUnitHandle))
					hitInFactoryClass= true;
				if (unitHandle.equals(ASTCreator.getCu(fCtorOwningClass)))
					hitInCtorClass= true;

				pm.worked(1);
				if (pm.isCanceled())
					throw new OperationCanceledException();
			}
			if (!hitInFactoryClass) { // Handle factory class if no search hits there
				CompilationUnitChange cuChange= new CompilationUnitChange(getName(), fFactoryUnitHandle);
				addAllChangesFor(null, fFactoryUnitHandle, cuChange);
				result.add(cuChange);
			}
			if (!hitInCtorClass && !fFactoryUnitHandle.equals(ASTCreator.getCu(fCtorOwningClass))) { // Handle constructor-owning class if no search hits there
				CompilationUnitChange cuChange= new CompilationUnitChange(getName(), ASTCreator.getCu(fCtorOwningClass));
				addAllChangesFor(null, ASTCreator.getCu(fCtorOwningClass), cuChange);
				result.add(cuChange);
			}
			return result;
		} finally {
			pm.done();
		}
	}

	/*
	 * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName()
	 */
	public String getName() {
		return RefactoringCoreMessages.IntroduceFactory_name;
	}

	/**
	 * Returns the name to be used for the generated factory method.
	 */
	public String getNewMethodName() {
		return fNewMethodName;
	}

	/**
	 * Sets the name to be used for the generated factory method.<br>
	 * Returns a <code>RefactoringStatus</code> that indicates whether the
	 * given name is valid for the new factory method.
	 * @param newMethodName the name to be used for the generated factory method
	 */
	public RefactoringStatus setNewMethodName(String newMethodName) {
		Assert.isNotNull(newMethodName);
		fNewMethodName = newMethodName;

		RefactoringStatus	stat= Checks.checkMethodName(newMethodName);

		stat.merge(isUniqueMethodName(newMethodName));

		return stat;
	}

	/**
	 * Returns a <code>RefactoringStatus</code> that identifies whether the
	 * the name <code>newMethodName</code> is available to use as the name of
	 * the new factory method within the factory-owner class (either a to-be-
	 * created factory class or the constructor-owning class, depending on the
	 * user options).
	 * @param methodName
	 */
	private RefactoringStatus isUniqueMethodName(String methodName) {
		boolean	conflict= hasMethod(fFactoryOwningClass, methodName);

		return conflict ? RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_duplicateMethodName + methodName) : new RefactoringStatus(); 
	}

	/**
	 * Returns true iff the given <code>AbstractTypeDeclaration</code> has a method with
	 * the given name.
	 * @param type
	 * @param name
	 */
	private boolean hasMethod(AbstractTypeDeclaration type, String name) {
		List	decls= type.bodyDeclarations();

		for (Iterator iter = decls.iterator(); iter.hasNext();) {
			BodyDeclaration decl = (BodyDeclaration) iter.next();
			if (decl instanceof MethodDeclaration) {
				if (((MethodDeclaration) decl).getName().getIdentifier().equals(name))
					return true;
			}
		}
		return false;
	}

	/**
	 * Returns true iff the selected constructor can be protected.
	 */
	public boolean canProtectConstructor() {
		return !fCtorBinding.isSynthetic() && fFactoryCU.findDeclaringNode(fCtorBinding.getKey()) != null;
	}

	/**
	 * If the argument is true, change the visibility of the constructor to
	 * <code>protected</code>, thereby encapsulating it.
	 * @param protectConstructor
	 */
	public void setProtectConstructor(boolean protectConstructor) {
		fProtectConstructor = protectConstructor;
	}

	/**
	 * Returns the project on behalf of which this refactoring was invoked.
	 */
	public IJavaProject getProject() {
		return fCUHandle.getJavaProject();
	}

	/**
	 * Sets the class on which the generated factory method is to be placed.
	 * @param fullyQualifiedTypeName an <code>IType</code> referring to an existing class
	 */
	public RefactoringStatus setFactoryClass(String fullyQualifiedTypeName) {
		IType factoryType;

		try {
			factoryType= findFactoryClass(fullyQualifiedTypeName);
			if (factoryType == null)
				return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceFactory_noSuchClass, fullyQualifiedTypeName)); 

			if (factoryType.isAnnotation())
				return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryMethodOnAnnotation); 
			if (factoryType.isInterface())
				return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryMethodOnInterface); 
		} catch (JavaModelException e) {
			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantCheckForInterface); 
		}

		ICompilationUnit	factoryUnitHandle= factoryType.getCompilationUnit();

		if (factoryType.isBinary())
			return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryInBinaryClass); 
		else {
			try {
				if (!fFactoryUnitHandle.equals(factoryUnitHandle)) {
					fFactoryCU= getASTFor(factoryUnitHandle);
					fFactoryUnitHandle= factoryUnitHandle;
				}
				fFactoryOwningClass= (AbstractTypeDeclaration) ASTNodes.getParent(NodeFinder.perform(fFactoryCU, factoryType.getNameRange()), AbstractTypeDeclaration.class);

				String factoryPkg= factoryType.getPackageFragment().getElementName();
				String ctorPkg= fCtorOwningClass.resolveBinding().getPackage().getName();

				if (!factoryPkg.equals(ctorPkg))
					fConstructorVisibility= Modifier.PUBLIC;
				else if (fFactoryOwningClass != fCtorOwningClass)
					fConstructorVisibility= 0; // No such thing as Modifier.PACKAGE...


				if (fFactoryOwningClass != fCtorOwningClass)
					fConstructorVisibility= 0; // No such thing as Modifier.PACKAGE...

			} catch (JavaModelException e) {
				return RefactoringStatus.createFatalErrorStatus(e.getMessage());
			}
			return new RefactoringStatus();
		}
	}

	/**
	 * Finds the factory class associated with the fully qualified name.
	 * @param fullyQualifiedTypeName the fully qualified type name
	 * @return the factory class, or <code>null</code> if not found
	 * @throws JavaModelException if an error occurs while finding the factory class
	 */
	private IType findFactoryClass(String fullyQualifiedTypeName) throws JavaModelException {
		IType factoryType= getProject().findType(fullyQualifiedTypeName);
		if (factoryType == null) // presumably a non-primary type; try the search engine
			factoryType= findNonPrimaryType(fullyQualifiedTypeName, new NullProgressMonitor(), new RefactoringStatus());
		return factoryType;
	}

	/**
	 * Returns the name of the class on which the generated factory method is
	 * to be placed.
	 */
	public String getFactoryClassName() {
		return fFactoryOwningClass.resolveBinding().getQualifiedName();
	}

	public RefactoringStatus initialize(final RefactoringArguments arguments) {
		if (arguments instanceof JavaRefactoringArguments) {
			final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
			final String selection= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION);
			if (selection != null) {
				int offset= -1;
				int length= -1;
				final StringTokenizer tokenizer= new StringTokenizer(selection);
				if (tokenizer.hasMoreTokens())
					offset= Integer.valueOf(tokenizer.nextToken()).intValue();
				if (tokenizer.hasMoreTokens())
					length= Integer.valueOf(tokenizer.nextToken()).intValue();
				if (offset >= 0 && length >= 0) {
					fSelectionStart= offset;
					fSelectionLength= length;
				} else
					return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION}));
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION));
			String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT);
			if (handle != null) {
				final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
				if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
					return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_FACTORY);
				else {
					fCUHandle= (ICompilationUnit) element;
		        	initialize();
				}
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
			handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1);
			if (handle != null) {
				final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
				if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
					return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_FACTORY);
				else {
					final IType type= (IType) element;
					fFactoryClassName= type.getFullyQualifiedName();
				}
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
			final String name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME);
			if (name != null && !"".equals(name)) //$NON-NLS-1$
				fNewMethodName= name;
			else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME));
			final String protect= extended.getAttribute(ATTRIBUTE_PROTECT);
			if (protect != null) {
				fProtectConstructor= Boolean.valueOf(protect).booleanValue();
			} else
				return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_PROTECT));
		} else
			return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
		return new RefactoringStatus();
	}
}
