nz.ac.massey.cs.care.refactoring.executers.IntroduceFactoryRefactoring.java Source code

Java tutorial

Introduction

Here is the source code for nz.ac.massey.cs.care.refactoring.executers.IntroduceFactoryRefactoring.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2011 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 nz.ac.massey.cs.care.refactoring.executers;

import gr.uom.java.ast.ClassObject;

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

import nz.ac.massey.cs.care.util.Utils;

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.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEditGroup;

import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;

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.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
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.MethodRef;
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.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.Statement;
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.dom.rewrite.ListRewrite;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.core.refactoring.descriptors.IntroduceFactoryDescriptor;
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.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
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.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.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.BasicElementLabels;
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, Ali Shah
 */
@SuppressWarnings("restriction")
public class IntroduceFactoryRefactoring extends Refactoring {

    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;

    private String fReturnType;

    private ClassObject targetClass;

    /**
     * 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,
            ClassObject targetClass) {
        Assert.isTrue(selectionStart >= 0);
        Assert.isTrue(selectionLength >= 0);
        fSelectionStart = selectionStart;
        fSelectionLength = selectionLength;
        this.targetClass = targetClass;
        fCUHandle = cu;
        if (cu != null)
            initialize();
    }

    public IntroduceFactoryRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
        this(null, 0, 0, null);
        RefactoringStatus initializeStatus = initialize(arguments);
        status.merge(initializeStatus);
    }

    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;
        if (node.getNodeType() == ASTNode.BLOCK) {
            ClassInstanceCreation cic = getClassInstanceCreation(node);
            return cic;
        }

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

    private ClassInstanceCreation getClassInstanceCreation(ASTNode node) {
        Block b = (Block) node;
        final List<ClassInstanceCreation> list = new ArrayList<ClassInstanceCreation>();
        b.accept(new ASTVisitor() {
            public boolean visit(ClassInstanceCreation node) {
                String name = node.getType().toString();
                if (name.contains("."))
                    name = Utils.getSimpleName(name);
                if (name.equals(targetClass.getSimpleName())) {
                    list.add(node);
                }
                return true;
            }
        });
        return list.iterator().next();
    }

    /**
     * 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) {
                System.out.println(fCUHandle.getSource());
                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();

            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);

            if (fNewMethodName == null)
                return setNewMethodName("create" + fCtorBinding.getName());//$NON-NLS-1$
            else
                return new RefactoringStatus();
        } finally {
            pm.done();
        }
    }

    /*
     * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    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();
        }
    }

    /**
     * @param searchHits
     * @return 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.
     */
    private ICompilationUnit[] collectAffectedUnits(SearchResultGroup[] searchHits) {
        Collection<ICompilationUnit> result = new ArrayList<ICompilationUnit>();
        boolean hitInFactoryClass = false;

        for (int i = 0; i < searchHits.length; i++) {
            SearchResultGroup rg = searchHits[i];
            ICompilationUnit icu = rg.getCompilationUnit();
            //TODO if(icu.equals(sourceClass.get))
            result.add(icu);
            if (icu.equals(fFactoryUnitHandle))
                hitInFactoryClass = true;
        }
        if (!hitInFactoryClass)
            result.add(fFactoryUnitHandle);
        return result.toArray(new ICompilationUnit[result.size()]);
    }

    /**
     * @param ctor
     * @param methodBinding
     * @return 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());
        }
    }

    /**
     * @param groups
     * @return 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<SearchResultGroup>();

        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
                //modified by Ali
                //Only search within the original compilation unit;
                if (unit.getElementName().equals(fCUHandle.getElementName())) {
                    result.add(rg);
                } else
                    fCallSitesInBinaryUnits = true;
        }
        return 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
     * @throws JavaModelException
     */
    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)
     */
    @Override
    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();
            //         IMethod method= (IMethod) fCtorBinding.getJavaElement();
            //         ICompilationUnit icu =  method.getCompilationUnit();
            ICompilationUnit[] affectedFiles = collectAffectedUnits(fAllCallsTo);//new ICompilationUnit[]{icu};
            result.merge(
                    Checks.validateModifiesFiles(ResourceUtil.getFiles(affectedFiles), getValidationContext()));
            fCallSitesInBinaryUnits = false; //temporarily
            if (fCallSitesInBinaryUnits)
                result.merge(RefactoringStatus
                        .createWarningStatus(RefactoringCoreMessages.IntroduceFactory_callSitesInBinaryClass));

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

    /**
     * @return 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<SingleVariableDeclaration> formalArgs = ctorDecl.parameters();
            int i = 0;

            for (Iterator<SingleVariableDeclaration> iter = formalArgs.iterator(); iter.hasNext(); i++) {
                SingleVariableDeclaration svd = 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;
    }

    public void setReturnType(String qualifiedName) {
        this.fReturnType = qualifiedName;
    }

    /**
     * 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
     * @return the new method declaration
     */
    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<Statement> stmts = body.statements();
        String retTypeName = null;
        if (fReturnType != null) {
            retTypeName = fReturnType;
        } else {
            retTypeName = "java.lang.Object";//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, ctorBinding.getName(), 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) {
            if (retTypeName.contains(".")) {
                newMethod.setReturnType2(ast.newSimpleType(ast.newName(retTypeName)));
            } else {
                newMethod.setReturnType2(ast.newSimpleType(ast.newSimpleName(retTypeName)));
            }
        } else {
            Type baseType = null;
            if (retTypeName.contains(".")) {
                baseType = ast.newSimpleType(ast.newName(retTypeName));
            } else {
                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<SingleVariableDeclaration> 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<Name> 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);
        }
    }

    /**
     * @param argType
     * @param extraDims number of extra array dimensions to add to the resulting type
     * @param ast
     * @return 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.
     */
    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<Expression> argList = newCtorCall.arguments();

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

            argList.add((Expression) ctorArg);
        }
    }

    /**
     * Updates the constructor call.
     *
     * @param ctorCall the ClassInstanceCreation to be marked as replaced
     * @param unitRewriter the AST rewriter
     * @param gd the edit group to use
     */
    private void rewriteFactoryMethodCall(ClassInstanceCreation ctorCall, ASTRewrite unitRewriter,
            TextEditGroup gd) {
        AST ast = unitRewriter.getAST();
        MethodInvocation factoryMethodCall = ast.newMethodInvocation();

        ASTNode ctorCallParent = ctorCall.getParent();
        StructuralPropertyDescriptor ctorCallLocation = ctorCall.getLocationInParent();
        if (ctorCallLocation instanceof ChildListPropertyDescriptor) {
            ListRewrite ctorCallParentListRewrite = unitRewriter.getListRewrite(ctorCallParent,
                    (ChildListPropertyDescriptor) ctorCallLocation);
            int index = ctorCallParentListRewrite.getOriginalList().indexOf(ctorCall);
            ctorCall = (ClassInstanceCreation) ctorCallParentListRewrite.getRewrittenList().get(index);
        } else {
            ctorCall = (ClassInstanceCreation) unitRewriter.get(ctorCallParent, ctorCallLocation);
        }

        ListRewrite actualFactoryArgs = unitRewriter.getListRewrite(factoryMethodCall,
                MethodInvocation.ARGUMENTS_PROPERTY);
        ListRewrite actualCtorArgs = unitRewriter.getListRewrite(ctorCall,
                ClassInstanceCreation.ARGUMENTS_PROPERTY);

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

        List<Expression> actualCtorArgsList = actualCtorArgs.getRewrittenList();
        for (int i = 0; i < actualCtorArgsList.size(); i++) {
            Expression actualCtorArg = actualCtorArgsList.get(i);

            ASTNode movedArg;
            if (ASTNodes.isExistingNode(actualCtorArg)) {
                movedArg = unitRewriter.createMoveTarget(actualCtorArg);
            } else {
                unitRewriter.remove(actualCtorArg, null);
                movedArg = actualCtorArg;
            }

            actualFactoryArgs.insertLast(movedArg, gd);
        }

        unitRewriter.replace(ctorCall, factoryMethodCall, gd);
    }

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

    /**
     * @return 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.
     * @param unitAST
     * @param unitRewriter
     * @param declGD
     * @return 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 unitHandle
     * @param unitChange the CompilationUnitChange object for the compilation unit in question
     * @return <code>true</code> iff a change has been added
     * @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;
    }

    /**
     * @param unitHandle
     * @return 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();
        Arrays.sort(hits, new Comparator<SearchMatch>() {
            /**
             * Sort by descending offset, such that nested constructor calls are processed first.
             * This is necessary, since they can only be moved into the factory method invocation
             * after they have been rewritten.
             */
            public int compare(SearchMatch m1, SearchMatch m2) {
                return m2.getOffset() - m1.getOffset();
            }
        });

        boolean someCallPatched = false;

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

            if (ctrCall instanceof ClassInstanceCreation) {
                TextEditGroup gd = new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_replaceCalls);

                rewriteFactoryMethodCall((ClassInstanceCreation) ctrCall, unitRewriter, gd);
                unitChange.addTextEditGroup(gd);
                someCallPatched = true;
            } else if (ctrCall instanceof MethodRef) {
                TextEditGroup gd = new TextEditGroup(
                        RefactoringCoreMessages.IntroduceFactoryRefactoring_replaceJavadocReference);

                rewriteJavadocReference((MethodRef) ctrCall, unitRewriter, gd);
                unitChange.addTextEditGroup(gd);
                someCallPatched = true;
            }
        }
        return someCallPatched;
    }

    private void rewriteJavadocReference(MethodRef javadocRef, ASTRewrite unitRewriter, TextEditGroup gd) {
        AST ast = unitRewriter.getAST();
        unitRewriter.replace(javadocRef.getName(), ast.newSimpleName(fNewMethodName), gd);
    }

    /**
     * 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 return a {@link ClassInstanceCreation} or a {@link MethodRef} or <code>null</code> if this is really a constructor->constructor call (e.g. "this(...)")
     * @throws CoreException
     */
    private ASTNode 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),
                                    BasicElementLabels.getJavaCodeString(
                                            unitHandle.getSource().substring(start, start + length)),
                                    BasicElementLabels.getFileName(unitHandle) }),
                    null));

        if (node instanceof ClassInstanceCreation) {
            if (((ClassInstanceCreation) node).getAnonymousClassDeclaration() != null) {
                // Cannot replace anonymous inner class, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=250660
                fConstructorVisibility = Modifier.PROTECTED;
                return null;
            }
            return node;
        } else if (node instanceof VariableDeclaration) {
            Expression init = ((VariableDeclaration) node).getInitializer();

            if (init instanceof ClassInstanceCreation) {
                return init;
            } else if (init != null)
                throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
                        Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedInitializerNodeType,
                                new Object[] { BasicElementLabels.getJavaCodeString(init.toString()),
                                        BasicElementLabels.getFileName(unitHandle) }),
                        null));
            else
                throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
                        Messages.format(
                                RefactoringCoreMessages.IntroduceFactory_noConstructorCallNodeInsideFoundVarbleDecl,
                                BasicElementLabels.getJavaCodeString(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 expr;
            else
                throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, Messages.format(
                        RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit,
                        new Object[] { BasicElementLabels.getJavaCodeString(expr.toString()),
                                BasicElementLabels.getFileName(unitHandle) }),
                        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 if (node instanceof MethodRef) {
            return node;
        } else
            throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
                    Messages.format(
                            RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit,
                            new Object[] {
                                    BasicElementLabels.getJavaElementName(
                                            node.getClass().getName() + "('" + node.toString() + "')"), //$NON-NLS-1$//$NON-NLS-2$
                                    BasicElementLabels.getFileName(unitHandle) }), 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);
    }

    /* (non-Javadoc)
     * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    public Change createChange(IProgressMonitor pm) throws CoreException {
        try {
            pm.beginTask(RefactoringCoreMessages.IntroduceFactory_createChanges, fAllCallsTo.length);
            final ITypeBinding binding = fFactoryOwningClass.resolveBinding();
            final Map<String, String> arguments = new HashMap<String, String>();
            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,
                    BasicElementLabels.getJavaElementName(fCtorOwningClass.getName().getIdentifier()));
            final String header = Messages.format(RefactoringCoreMessages.IntroduceFactory_descriptor_description,
                    new String[] { BasicElementLabels.getJavaElementName(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,
                    BasicElementLabels.getJavaElementName(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 IntroduceFactoryDescriptor descriptor = RefactoringSignatureDescriptorFactory
                    .createIntroduceFactoryDescriptor(project, description, comment.asString(), arguments, flags);
            arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT,
                    JavaRefactoringDescriptorUtil.elementToHandle(project, fCUHandle));
            arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fNewMethodName);
            arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1,
                    JavaRefactoringDescriptorUtil.elementToHandle(project, binding.getJavaElement()));
            arguments.put(JavaRefactoringDescriptorUtil.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();
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
     */
    @Override
    public String getName() {
        return RefactoringCoreMessages.IntroduceFactory_name;
    }

    /**
     * Returns the name to be used for the generated factory method.
     * @return the new method name
     */
    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
     * @return the resulting status
     */
    public RefactoringStatus setNewMethodName(String newMethodName) {
        Assert.isNotNull(newMethodName);
        fNewMethodName = newMethodName;

        RefactoringStatus stat = Checks.checkMethodName(newMethodName, fCUHandle);

        stat.merge(isUniqueMethodName(newMethodName));

        return stat;
    }

    /**
     * @param methodName
     * @return 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).
     */
    private RefactoringStatus isUniqueMethodName(String methodName) {
        ITypeBinding declaringClass = fCtorBinding.getDeclaringClass();
        if (Bindings.findMethodInType(declaringClass, methodName, fCtorBinding.getParameterTypes()) != null) {
            String format = Messages.format(RefactoringCoreMessages.IntroduceFactory_duplicateMethodName,
                    BasicElementLabels.getJavaElementName(methodName));
            return RefactoringStatus.createErrorStatus(format);
        }
        return new RefactoringStatus();
    }

    /**
     * Returns true iff the selected constructor can be protected.
     * @return return <code>true</code> if the constructor can be made 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.
     * @return returns the Java project
     */
    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
     * @return return the resulting status
     */
    public RefactoringStatus setFactoryClass(String fullyQualifiedTypeName) {
        IType factoryType;

        try {
            factoryType = findFactoryClass(fullyQualifiedTypeName);
            if (factoryType == null)
                return RefactoringStatus
                        .createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceFactory_noSuchClass,
                                BasicElementLabels.getJavaElementName(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.
     * @return return the factory class name
     */
    public String getFactoryClassName() {
        return fFactoryOwningClass.resolveBinding().getQualifiedName();
    }

    private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
        final String selection = arguments.getAttribute(JavaRefactoringDescriptorUtil.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, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION }));
        } else
            return RefactoringStatus.createFatalErrorStatus(
                    Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
                            JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
        String handle = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
        if (handle != null) {
            final IJavaElement element = JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(),
                    handle, false);
            if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
                return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(),
                        IJavaRefactorings.INTRODUCE_FACTORY);
            else {
                fCUHandle = (ICompilationUnit) element;
                initialize();
            }
        } else
            return RefactoringStatus.createFatalErrorStatus(
                    Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
                            JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
        handle = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1);
        if (handle != null) {
            final IJavaElement element = JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(),
                    handle, false);
            if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
                return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(),
                        IJavaRefactorings.INTRODUCE_FACTORY);
            else {
                final IType type = (IType) element;
                fFactoryClassName = type.getFullyQualifiedName();
            }
        } else
            return RefactoringStatus.createFatalErrorStatus(
                    Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
                            JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
        final String name = arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
        if (name != null && !"".equals(name)) //$NON-NLS-1$
            fNewMethodName = name;
        else
            return RefactoringStatus.createFatalErrorStatus(
                    Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
                            JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
        final String protect = arguments.getAttribute(ATTRIBUTE_PROTECT);
        if (protect != null) {
            fProtectConstructor = Boolean.valueOf(protect).booleanValue();
        } else
            return RefactoringStatus.createFatalErrorStatus(Messages.format(
                    RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_PROTECT));
        return new RefactoringStatus();
    }
}