KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > refactoring > code > IntroduceFactoryRefactoring


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.corext.refactoring.code;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collection JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.StringTokenizer JavaDoc;
20
21 import org.eclipse.text.edits.MultiTextEdit;
22 import org.eclipse.text.edits.TextEditGroup;
23
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.NullProgressMonitor;
29 import org.eclipse.core.runtime.OperationCanceledException;
30 import org.eclipse.core.runtime.SubProgressMonitor;
31
32 import org.eclipse.ltk.core.refactoring.Change;
33 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
34 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
35 import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
36
37 import org.eclipse.jdt.core.ICompilationUnit;
38 import org.eclipse.jdt.core.IJavaElement;
39 import org.eclipse.jdt.core.IJavaProject;
40 import org.eclipse.jdt.core.IMethod;
41 import org.eclipse.jdt.core.IType;
42 import org.eclipse.jdt.core.JavaModelException;
43 import org.eclipse.jdt.core.dom.AST;
44 import org.eclipse.jdt.core.dom.ASTNode;
45 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
46 import org.eclipse.jdt.core.dom.Block;
47 import org.eclipse.jdt.core.dom.BodyDeclaration;
48 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
49 import org.eclipse.jdt.core.dom.CompilationUnit;
50 import org.eclipse.jdt.core.dom.ConstructorInvocation;
51 import org.eclipse.jdt.core.dom.Expression;
52 import org.eclipse.jdt.core.dom.ExpressionStatement;
53 import org.eclipse.jdt.core.dom.IMethodBinding;
54 import org.eclipse.jdt.core.dom.ITypeBinding;
55 import org.eclipse.jdt.core.dom.MethodDeclaration;
56 import org.eclipse.jdt.core.dom.MethodInvocation;
57 import org.eclipse.jdt.core.dom.Modifier;
58 import org.eclipse.jdt.core.dom.Name;
59 import org.eclipse.jdt.core.dom.ParameterizedType;
60 import org.eclipse.jdt.core.dom.ReturnStatement;
61 import org.eclipse.jdt.core.dom.SimpleName;
62 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
63 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
64 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
65 import org.eclipse.jdt.core.dom.Type;
66 import org.eclipse.jdt.core.dom.TypeParameter;
67 import org.eclipse.jdt.core.dom.VariableDeclaration;
68 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
69 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
70 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
71 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
72 import org.eclipse.jdt.core.search.IJavaSearchConstants;
73 import org.eclipse.jdt.core.search.IJavaSearchScope;
74 import org.eclipse.jdt.core.search.SearchMatch;
75 import org.eclipse.jdt.core.search.SearchPattern;
76
77 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
78 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
79 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
80 import org.eclipse.jdt.internal.corext.dom.Bindings;
81 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
82 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
83 import org.eclipse.jdt.internal.corext.refactoring.Checks;
84 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
85 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptor;
86 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
87 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
88 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
89 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine2;
90 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
91 import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
92 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
93 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationStateChange;
94 import org.eclipse.jdt.internal.corext.refactoring.typeconstraints.ASTCreator;
95 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
96 import org.eclipse.jdt.internal.corext.util.JdtFlags;
97 import org.eclipse.jdt.internal.corext.util.Messages;
98 import org.eclipse.jdt.internal.corext.util.SearchUtils;
99
100 import org.eclipse.jdt.ui.JavaElementLabels;
101
102 import org.eclipse.jdt.internal.ui.JavaUIStatus;
103 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
104
105 /**
106  * Refactoring class that permits the substitution of a factory method
107  * for direct calls to a given constructor.
108  * @author rfuhrer
109  */

110 public class IntroduceFactoryRefactoring extends ScriptableRefactoring {
111
112     private static final String JavaDoc ATTRIBUTE_PROTECT= "protect"; //$NON-NLS-1$
113

114     /**
115      * The handle for the compilation unit holding the selection that was
116      * passed into this refactoring.
117      */

118     private ICompilationUnit fCUHandle;
119
120     /**
121      * The AST for the compilation unit holding the selection that was
122      * passed into this refactoring.
123      */

124     private CompilationUnit fCU;
125
126     /**
127      * Handle for compilation unit in which the factory method/class/interface will be
128      * generated.
129      */

130     private ICompilationUnit fFactoryUnitHandle;
131
132     /**
133      * The start of the original textual selection in effect when this refactoring
134      * was initiated. If the refactoring was initiated from a structured selection
135      * (e.g. from the outline view), then this refers to the textual selection that
136      * corresponds to the structured selection item.
137      */

138     private int fSelectionStart;
139
140     /**
141      * The length of the original textual selection in effect when this refactoring
142      * was initiated. If the refactoring was initiated from a structured selection
143      * (e.g. from the outline view), then this refers to the textual selection that
144      * corresponds to the structured selection item.
145      */

146     private int fSelectionLength;
147
148     /**
149      * The AST node corresponding to the user's textual selection.
150      */

151     private ASTNode fSelectedNode;
152
153     /**
154      * The method binding for the selected constructor.
155      */

156     private IMethodBinding fCtorBinding;
157     
158     /**
159      * <code>TypeDeclaration</code> for class containing the constructor to be
160      * encapsulated.
161      */

162     private AbstractTypeDeclaration fCtorOwningClass;
163
164     /**
165      * The name to be given to the generated factory method.
166      */

167     private String JavaDoc fNewMethodName= null;
168
169     /**
170      * An array of <code>SearchResultGroup</code>'s of all call sites
171      * that refer to the constructor signature in question.
172      */

173     private SearchResultGroup[] fAllCallsTo;
174
175     /**
176      * The class that will own the factory method/class/interface.
177      */

178     private AbstractTypeDeclaration fFactoryOwningClass;
179
180     /**
181      * The newly-generated factory method.
182      */

183     private MethodDeclaration fFactoryMethod= null;
184
185     /**
186      * An array containing the names of the constructor's formal arguments,
187      * if available, otherwise "arg1" ... "argN".
188      */

189     private String JavaDoc[] fFormalArgNames= null;
190
191     /**
192      * An array of <code>ITypeBinding</code>'s that describes the types of
193      * the constructor arguments, in order.
194      */

195     private ITypeBinding[] fArgTypes;
196
197     /**
198      * True iff the given constructor has a varargs signature.
199      */

200     private boolean fCtorIsVarArgs;
201
202     /**
203      * If true, change the visibility of the constructor to protected to better
204      * encapsulate it.
205      */

206     private boolean fProtectConstructor= true;
207
208     /**
209      * An <code>ImportRewrite</code> that manages imports needed to satisfy
210      * newly-introduced type references in the <code>ICompilationUnit</code>
211      * currently being rewritten during <code>createChange()</code>.
212      */

213     private ImportRewrite fImportRewriter;
214
215     /**
216      * True iff there are call sites for the constructor to be encapsulated
217      * located in binary classes.
218      */

219     private boolean fCallSitesInBinaryUnits;
220
221     /**
222      * <code>CompilationUnit</code> in which the factory is to be created.
223      */

224     private CompilationUnit fFactoryCU;
225
226     /**
227      * The fully qualified name of the factory class. This is only used
228      * if invoked from a refactoring script.
229      */

230     private String JavaDoc fFactoryClassName;
231     
232     private int fConstructorVisibility= Modifier.PRIVATE;
233
234     /**
235      * Creates a new <code>IntroduceFactoryRefactoring</code> with the given selection
236      * on the given compilation unit.
237      * @param cu the <code>ICompilationUnit</code> in which the user selection was made, or <code>null</code> if invoked from scripting
238      * @param selectionStart the start of the textual selection in <code>cu</code>
239      * @param selectionLength the length of the textual selection in <code>cu</code>
240      */

241     public IntroduceFactoryRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength) {
242         Assert.isTrue(selectionStart >= 0);
243         Assert.isTrue(selectionLength >= 0);
244         fSelectionStart= selectionStart;
245         fSelectionLength= selectionLength;
246         fCUHandle= cu;
247         if (cu != null)
248             initialize();
249     }
250
251     private void initialize() {
252         fCU= ASTCreator.createAST(fCUHandle, null);
253     }
254
255     /**
256      * Finds and returns the <code>ASTNode</code> for the given source text
257      * selection, if it is an entire constructor call or the class name portion
258      * of a constructor call or constructor declaration, or null otherwise.
259      * @param unit The compilation unit in which the selection was made
260      * @param offset The textual offset of the start of the selection
261      * @param length The length of the selection in characters
262      * @return ClassInstanceCreation or MethodDeclaration
263      */

264     private ASTNode getTargetNode(ICompilationUnit unit, int offset, int length) {
265         ASTNode node= ASTNodes.getNormalizedNode(NodeFinder.perform(fCU, offset, length));
266         if (node.getNodeType() == ASTNode.CLASS_INSTANCE_CREATION)
267             return node;
268         if (node.getNodeType() == ASTNode.METHOD_DECLARATION && ((MethodDeclaration)node).isConstructor())
269             return node;
270         // we have some sub node. Make sure its the right child of the parent
271
StructuralPropertyDescriptor location= node.getLocationInParent();
272         ASTNode parent= node.getParent();
273         if (location == ClassInstanceCreation.TYPE_PROPERTY) {
274             return parent;
275         } else if (location == MethodDeclaration.NAME_PROPERTY && ((MethodDeclaration)parent).isConstructor()) {
276             return parent;
277         }
278         return null;
279     }
280
281     /**
282      * Determines what kind of AST node was selected, and returns an error status
283      * if the kind of node is inappropriate for this refactoring.
284      * @param pm
285      * @return a RefactoringStatus indicating whether the selection is valid
286      * @throws JavaModelException
287      */

288     private RefactoringStatus checkSelection(IProgressMonitor pm) throws JavaModelException {
289         try {
290             pm.beginTask(RefactoringCoreMessages.IntroduceFactory_examiningSelection, 2);
291     
292             fSelectedNode= getTargetNode(fCUHandle, fSelectionStart, fSelectionLength);
293     
294             if (fSelectedNode == null)
295                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_notAConstructorInvocation);
296     
297             // getTargetNode() must return either a ClassInstanceCreation or a
298
// constructor MethodDeclaration; nothing else.
299
if (fSelectedNode instanceof ClassInstanceCreation) {
300                 ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation)fSelectedNode;
301                 fCtorBinding= classInstanceCreation.resolveConstructorBinding();
302             } else if (fSelectedNode instanceof MethodDeclaration) {
303                 MethodDeclaration methodDeclaration= (MethodDeclaration)fSelectedNode;
304                 fCtorBinding= methodDeclaration.resolveBinding();
305             }
306     
307             if (fCtorBinding == null)
308                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_unableToResolveConstructorBinding);
309
310             // If this constructor is of a generic type, get the generic version,
311
// not some instantiation thereof.
312
fCtorBinding= fCtorBinding.getMethodDeclaration();
313
314             if (fNewMethodName == null)
315                 fNewMethodName= "create" + fCtorBinding.getName();//$NON-NLS-1$
316

317             pm.worked(1);
318     
319             // We don't handle constructors of nested types at the moment
320
if (fCtorBinding.getDeclaringClass().isNested())
321                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_unsupportedNestedTypes);
322     
323             ITypeBinding ctorType= fCtorBinding.getDeclaringClass();
324             IType ctorOwningType= (IType) ctorType.getJavaElement();
325     
326             if (ctorOwningType.isBinary())
327                 // Can't modify binary CU; don't know what CU to put factory method
328
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_constructorInBinaryClass);
329             if (ctorOwningType.isEnum())
330                 // Doesn't make sense to encapsulate enum constructors
331
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_constructorInEnum);
332     
333             // Put the generated factory method inside the type that owns the constructor
334
fFactoryUnitHandle= ctorOwningType.getCompilationUnit();
335             fFactoryCU= getASTFor(fFactoryUnitHandle);
336     
337             Name ctorOwnerName= (Name) NodeFinder.perform(fFactoryCU, ctorOwningType.getNameRange());
338     
339             fCtorOwningClass= (AbstractTypeDeclaration) ASTNodes.getParent(ctorOwnerName, AbstractTypeDeclaration.class);
340             fFactoryOwningClass= fCtorOwningClass;
341     
342             pm.worked(1);
343     
344             return new RefactoringStatus();
345         } finally {
346             pm.done();
347         }
348     }
349
350     /*
351      * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor)
352      */

353     public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
354         try {
355             pm.beginTask(RefactoringCoreMessages.IntroduceFactory_checkingActivation, 1);
356     
357             if (!fCUHandle.isStructureKnown())
358                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_syntaxError);
359     
360             return checkSelection(new SubProgressMonitor(pm, 1));
361         } finally {
362             pm.done();
363         }
364     }
365
366     /**
367      * Returns the set of compilation units that will be affected by this
368      * particular invocation of this refactoring. This in general includes
369      * the class containing the constructor in question, as well as all
370      * call sites to the constructor.
371      * @return ICompilationUnit[]
372      */

373     private ICompilationUnit[] collectAffectedUnits(SearchResultGroup[] searchHits) {
374         Collection JavaDoc result= new ArrayList JavaDoc();
375         boolean hitInFactoryClass= false;
376
377         for(int i=0; i < searchHits.length; i++) {
378             SearchResultGroup rg= searchHits[i];
379             ICompilationUnit icu= rg.getCompilationUnit();
380
381             result.add(icu);
382             if (icu.equals(fFactoryUnitHandle))
383                 hitInFactoryClass= true;
384         }
385         if (!hitInFactoryClass)
386             result.add(fFactoryUnitHandle);
387         return (ICompilationUnit[]) result.toArray(new ICompilationUnit[result.size()]);
388     }
389
390     /**
391      * Returns a <code>SearchPattern</code> that finds all calls to the constructor
392      * identified by the argument <code>methodBinding</code>.
393      */

394     private SearchPattern createSearchPattern(IMethod ctor, IMethodBinding methodBinding) {
395         Assert.isNotNull(methodBinding,
396                 RefactoringCoreMessages.IntroduceFactory_noBindingForSelectedConstructor);
397         
398         if (ctor != null)
399             return SearchPattern.createPattern(ctor, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
400         else { // perhaps a synthetic method? (but apparently not always... hmmm...)
401
// Can't find an IMethod for this method, so build a string pattern instead
402
StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
403
404             buf.append(methodBinding.getDeclaringClass().getQualifiedName())
405                .append("(");//$NON-NLS-1$
406
for(int i=0; i < fArgTypes.length; i++) {
407                 if (i != 0)
408                     buf.append(","); //$NON-NLS-1$
409
buf.append(fArgTypes[i].getQualifiedName());
410             }
411             buf.append(")"); //$NON-NLS-1$
412
return SearchPattern.createPattern(buf.toString(), IJavaSearchConstants.CONSTRUCTOR,
413                     IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
414         }
415     }
416     
417     private IJavaSearchScope createSearchScope(IMethod ctor, IMethodBinding binding) throws JavaModelException {
418         if (ctor != null) {
419             return RefactoringScopeFactory.create(ctor);
420         } else {
421             ITypeBinding type= Bindings.getTopLevelType(binding.getDeclaringClass());
422             return RefactoringScopeFactory.create(type.getJavaElement());
423         }
424     }
425
426     /**
427      * Returns an array of <code>SearchResultGroup</code>'s like the argument,
428      * but omitting those groups that have no corresponding compilation unit
429      * (i.e. are binary and therefore can't be modified).
430      */

431     private SearchResultGroup[] excludeBinaryUnits(SearchResultGroup[] groups) {
432         Collection JavaDoc/*<SearchResultGroup>*/ result= new ArrayList JavaDoc();
433
434         for (int i = 0; i < groups.length; i++) {
435             SearchResultGroup rg= groups[i];
436             ICompilationUnit unit= rg.getCompilationUnit();
437
438             if (unit != null) // Ignore hits within a binary unit
439
result.add(rg);
440             else
441                 fCallSitesInBinaryUnits= true;
442         }
443         return (SearchResultGroup[]) result.toArray(new SearchResultGroup[result.size()]);
444     }
445
446     /**
447      * Search for all calls to the given <code>IMethodBinding</code> in the project
448      * that contains the compilation unit <code>fCUHandle</code>.
449      * @param methodBinding
450      * @param pm
451      * @param status
452      * @return an array of <code>SearchResultGroup</code>'s that identify the search matches
453      * @throws JavaModelException
454      */

455     private SearchResultGroup[] searchForCallsTo(IMethodBinding methodBinding, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
456         IMethod method= (IMethod) methodBinding.getJavaElement();
457         final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(createSearchPattern(method, methodBinding));
458         engine.setFiltering(true, true);
459         engine.setScope(createSearchScope(method, methodBinding));
460         engine.setStatus(status);
461         engine.searchPattern(new SubProgressMonitor(pm, 1));
462         return (SearchResultGroup[]) engine.getResults();
463     }
464
465     /**
466      * Returns an array of <code>SearchResultGroup</code>'s containing all method
467      * calls in the Java project that invoke the constructor identified by the given
468      * <code>IMethodBinding</code>
469      * @param ctorBinding an <code>IMethodBinding</code> identifying a particular
470      * constructor signature to search for
471      * @param pm an <code>IProgressMonitor</code> to use during this potentially
472      * lengthy operation
473      * @param status
474      * @return an array of <code>SearchResultGroup</code>'s identifying all
475      * calls to the given constructor signature
476      */

477     private SearchResultGroup[] findAllCallsTo(IMethodBinding ctorBinding, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
478         SearchResultGroup[] groups= excludeBinaryUnits(searchForCallsTo(ctorBinding, pm, status));
479
480         return groups;
481     }
482
483     private IType findNonPrimaryType(String JavaDoc fullyQualifiedName, IProgressMonitor pm, RefactoringStatus status) throws JavaModelException {
484         SearchPattern p= SearchPattern.createPattern(fullyQualifiedName, IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
485         final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(p);
486
487         engine.setFiltering(true, true);
488         engine.setScope(RefactoringScopeFactory.create(fCtorBinding.getJavaElement().getJavaProject()));
489         engine.setStatus(status);
490         engine.searchPattern(new SubProgressMonitor(pm, 1));
491
492         SearchResultGroup[] groups= (SearchResultGroup[]) engine.getResults();
493
494         if (groups.length != 0) {
495             for(int i= 0; i < groups.length; i++) {
496                 SearchMatch[] matches= groups[i].getSearchResults();
497                 for(int j= 0; j < matches.length; j++) {
498                     if (matches[j].getAccuracy() == SearchMatch.A_ACCURATE)
499                         return (IType) matches[j].getElement();
500                 }
501             }
502         }
503         return null;
504     }
505
506     /*
507      * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
508      */

509     public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
510         try {
511             pm.beginTask(RefactoringCoreMessages.IntroduceFactory_checking_preconditions, 1);
512             RefactoringStatus result= new RefactoringStatus();
513             
514             if (fFactoryClassName != null)
515                 result.merge(setFactoryClass(fFactoryClassName));
516             if (result.hasFatalError())
517                 return result;
518             fArgTypes= fCtorBinding.getParameterTypes();
519             fCtorIsVarArgs= fCtorBinding.isVarargs();
520             fAllCallsTo= findAllCallsTo(fCtorBinding, pm, result);
521             fFormalArgNames= findCtorArgNames();
522  
523             ICompilationUnit[] affectedFiles= collectAffectedUnits(fAllCallsTo);
524             result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(affectedFiles), getValidationContext()));
525
526             if (fCallSitesInBinaryUnits)
527                 result.merge(RefactoringStatus.createWarningStatus(RefactoringCoreMessages.IntroduceFactory_callSitesInBinaryClass));
528
529             return result;
530         } finally {
531             pm.done();
532         }
533     }
534
535     /**
536      * Returns an array containing the argument names for the constructor
537      * identified by <code>fCtorBinding</code>, if available, or default
538      * names if unavailable (e.g. if the constructor resides in a binary unit).
539      */

540     private String JavaDoc[] findCtorArgNames() {
541         int numArgs= fCtorBinding.getParameterTypes().length;
542         String JavaDoc[] names= new String JavaDoc[numArgs];
543
544         CompilationUnit ctorUnit= (CompilationUnit) ASTNodes.getParent(fCtorOwningClass, CompilationUnit.class);
545         MethodDeclaration ctorDecl= (MethodDeclaration) ctorUnit.findDeclaringNode(fCtorBinding.getKey());
546
547         if (ctorDecl != null) {
548             List JavaDoc formalArgs= ctorDecl.parameters();
549             int i= 0;
550
551             for(Iterator JavaDoc iter= formalArgs.iterator(); iter.hasNext(); i++) {
552                 SingleVariableDeclaration svd= (SingleVariableDeclaration) iter.next();
553
554                 names[i]= svd.getName().getIdentifier();
555             }
556             return names;
557         }
558
559         // Have no way of getting the formal argument names; just fake it.
560
for(int i=0; i < numArgs; i++)
561             names[i]= "arg" + (i+1); //$NON-NLS-1$
562

563         return names;
564     }
565
566     /**
567      * Creates and returns a new MethodDeclaration that represents the factory
568      * method to be used in place of direct calls to the constructor in question.
569      * @param ast An AST used as a factory for various AST nodes
570      * @param ctorBinding binding for the constructor being wrapped
571      * @param unitRewriter the ASTRewrite to be used
572      */

573     private MethodDeclaration createFactoryMethod(AST ast, IMethodBinding ctorBinding, ASTRewrite unitRewriter) {
574         MethodDeclaration newMethod= ast.newMethodDeclaration();
575         SimpleName newMethodName= ast.newSimpleName(fNewMethodName);
576         ClassInstanceCreation newCtorCall= ast.newClassInstanceCreation();
577         ReturnStatement ret= ast.newReturnStatement();
578         Block body= ast.newBlock();
579         List JavaDoc stmts= body.statements();
580         String JavaDoc retTypeName= ctorBinding.getName();
581
582         createFactoryMethodSignature(ast, newMethod);
583
584         newMethod.setName(newMethodName);
585         newMethod.setBody(body);
586
587         ITypeBinding[] ctorOwnerTypeParameters= fCtorBinding.getDeclaringClass().getTypeParameters();
588
589         setMethodReturnType(newMethod, retTypeName, ctorOwnerTypeParameters, ast);
590
591         newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.STATIC | Modifier.PUBLIC));
592
593         setCtorTypeArguments(newCtorCall, retTypeName, ctorOwnerTypeParameters, ast);
594
595         createFactoryMethodConstructorArgs(ast, newCtorCall);
596
597         ret.setExpression(newCtorCall);
598         stmts.add(ret);
599
600         return newMethod;
601     }
602
603     /**
604      * Sets the type being instantiated in the given constructor call, including
605      * specifying any necessary type arguments.
606      * @param newCtorCall the constructor call to modify
607      * @param ctorTypeName the simple name of the type being instantiated
608      * @param ctorOwnerTypeParameters the formal type parameters of the type being
609      * instantiated
610      * @param ast utility object used to create AST nodes
611      */

612     private void setCtorTypeArguments(ClassInstanceCreation newCtorCall, String JavaDoc ctorTypeName, ITypeBinding[] ctorOwnerTypeParameters, AST ast) {
613         if (ctorOwnerTypeParameters.length == 0) // easy, just a simple type
614
newCtorCall.setType(ASTNodeFactory.newType(ast, ctorTypeName));
615         else {
616             Type baseType= ast.newSimpleType(ast.newSimpleName(ctorTypeName));
617             ParameterizedType newInstantiatedType= ast.newParameterizedType(baseType);
618             List JavaDoc/*<Type>*/ newInstTypeArgs= newInstantiatedType.typeArguments();
619
620             for(int i= 0; i < ctorOwnerTypeParameters.length; i++) {
621                 Type typeArg= ASTNodeFactory.newType(ast, ctorOwnerTypeParameters[i].getName());
622
623                 newInstTypeArgs.add(typeArg);
624             }
625             newCtorCall.setType(newInstantiatedType);
626         }
627     }
628
629     /**
630      * Sets the return type of the factory method, including any necessary type
631      * arguments. E.g., for constructor <code>Foo()</code> in <code>Foo&lt;T&gt;</code>,
632      * the factory method defines a method type parameter <code>&lt;T&gt;</code> and
633      * returns a <code>Foo&lt;T&gt;</code>.
634      * @param newMethod the method whose return type is to be set
635      * @param retTypeName the simple name of the return type (without type parameters)
636      * @param ctorOwnerTypeParameters the formal type parameters of the type that the
637      * factory method instantiates (whose constructor is being encapsulated)
638      * @param ast utility object used to create AST nodes
639      */

640     private void setMethodReturnType(MethodDeclaration newMethod, String JavaDoc retTypeName, ITypeBinding[] ctorOwnerTypeParameters, AST ast) {
641         if (ctorOwnerTypeParameters.length == 0)
642             newMethod.setReturnType2(ast.newSimpleType(ast.newSimpleName(retTypeName)));
643         else {
644             Type baseType= ast.newSimpleType(ast.newSimpleName(retTypeName));
645             ParameterizedType newRetType= ast.newParameterizedType(baseType);
646             List JavaDoc/*<Type>*/ newRetTypeArgs= newRetType.typeArguments();
647
648             for(int i= 0; i < ctorOwnerTypeParameters.length; i++) {
649                 Type retTypeArg= ASTNodeFactory.newType(ast, ctorOwnerTypeParameters[i].getName());
650
651                 newRetTypeArgs.add(retTypeArg);
652             }
653             newMethod.setReturnType2(newRetType);
654         }
655     }
656
657     /**
658      * Creates and adds the necessary argument declarations to the given factory method.<br>
659      * An argument is needed for each original constructor argument for which the
660      * evaluation of the actual arguments across all calls was not able to be
661      * pushed inside the factory method (e.g. arguments with side-effects, references
662      * to fields if the factory method is to be static or reside in a factory class,
663      * or arguments that varied across the set of constructor calls).<br>
664      * <code>fArgTypes</code> identifies such arguments by a <code>null</code> value.
665      * @param ast utility object used to create AST nodes
666      * @param newMethod the <code>MethodDeclaration</code> for the factory method
667      */

668     private void createFactoryMethodSignature(AST ast, MethodDeclaration newMethod) {
669         List JavaDoc argDecls= newMethod.parameters();
670
671         for(int i=0; i < fArgTypes.length; i++) {
672             SingleVariableDeclaration argDecl= ast.newSingleVariableDeclaration();
673             Type argType;
674
675             if (i == (fArgTypes.length - 1) && fCtorIsVarArgs) {
676                 // The trailing varargs arg has an extra array dimension, compared to
677
// what we need to pass to setType()...
678
argType= typeNodeForTypeBinding(fArgTypes[i].getElementType(),
679                         fArgTypes[i].getDimensions()-1, ast);
680                 argDecl.setVarargs(true);
681             } else
682                 argType= typeNodeForTypeBinding(fArgTypes[i], 0, ast);
683
684             argDecl.setName(ast.newSimpleName(fFormalArgNames[i]));
685             argDecl.setType(argType);
686             argDecls.add(argDecl);
687         }
688
689         ITypeBinding[] ctorExcepts= fCtorBinding.getExceptionTypes();
690         List JavaDoc exceptions= newMethod.thrownExceptions();
691
692         for(int i=0; i < ctorExcepts.length; i++) {
693             String JavaDoc excName= fImportRewriter.addImport(ctorExcepts[i]);
694
695             exceptions.add(ASTNodeFactory.newName(ast, excName));
696         }
697
698         copyTypeParameters(ast, newMethod);
699     }
700
701     /**
702      * Copies the constructor's parent type's type parameters, if any, as
703      * method type parameters of the new static factory method. (Recall
704      * that static methods can't refer to type arguments of the enclosing
705      * class, since they have no instance to serve as a context.)<br>
706      * Makes sure to copy the bounds from the owning type, to ensure that the
707      * return type of the factory method satisfies the bounds of the type
708      * being instantiated.<br>
709      * E.g., for ctor Foo() in the type Foo<T extends Number>, be sure that
710      * the factory method is declared as<br>
711      * <code>static <T extends Number> Foo<T> createFoo()</code><br>
712      * and not simply<br>
713      * <code>static <T> Foo<T> createFoo()</code><br>
714      * or the compiler will bark.
715      * @param ast utility object needed to create ASTNode's for the new method
716      * @param newMethod the method onto which to copy the type parameters
717      */

718     private void copyTypeParameters(AST ast, MethodDeclaration newMethod) {
719         ITypeBinding[] ctorOwnerTypeParms= fCtorBinding.getDeclaringClass().getTypeParameters();
720         List JavaDoc/*<TypeParameter>*/ factoryMethodTypeParms= newMethod.typeParameters();
721         for(int i= 0; i < ctorOwnerTypeParms.length; i++) {
722             TypeParameter newParm= ast.newTypeParameter();
723             ITypeBinding[] parmTypeBounds= ctorOwnerTypeParms[i].getTypeBounds();
724             List JavaDoc/*<Type>*/ newParmBounds= newParm.typeBounds();
725
726             newParm.setName(ast.newSimpleName(ctorOwnerTypeParms[i].getName()));
727             for(int b=0; b < parmTypeBounds.length; b++) {
728                 if (parmTypeBounds[b].isClass() && parmTypeBounds[b].getSuperclass() == null)
729                     continue;
730
731                 Type newBound= fImportRewriter.addImport(parmTypeBounds[b], ast);
732
733                 newParmBounds.add(newBound);
734             }
735             factoryMethodTypeParms.add(newParm);
736         }
737     }
738
739     /**
740      * Returns a Type that describes the given ITypeBinding. If the binding
741      * refers to an object type, use the import rewriter to determine whether
742      * the reference requires a new import, or instead needs to be qualified.<br>
743      * Like ASTNodeFactory.newType(), but for the handling of imports.
744      * @param extraDims number of extra array dimensions to add to the resulting type
745      */

746     private Type typeNodeForTypeBinding(ITypeBinding argType, int extraDims, AST ast) {
747         if (extraDims > 0) {
748             return ast.newArrayType(typeNodeForTypeBinding(argType, 0, ast), extraDims);
749             
750         } else if (argType.isArray()) {
751             Type elementType= typeNodeForTypeBinding(argType.getElementType(), extraDims, ast);
752             return ast.newArrayType(elementType, argType.getDimensions());
753             
754         } else {
755             return fImportRewriter.addImport(argType, ast);
756         }
757     }
758
759     /**
760      * Create the list of actual arguments to the constructor call that is
761      * encapsulated inside the factory method, and associate the arguments
762      * with the given constructor call object.
763      * @param ast utility object used to create AST nodes
764      * @param newCtorCall the newly-generated constructor call to be wrapped inside
765      * the factory method
766      */

767     private void createFactoryMethodConstructorArgs(AST ast, ClassInstanceCreation newCtorCall) {
768         List JavaDoc argList= newCtorCall.arguments();
769
770         for(int i=0; i < fArgTypes.length; i++) {
771             ASTNode ctorArg= ast.newSimpleName(fFormalArgNames[i]);
772
773             argList.add(ctorArg);
774         }
775     }
776
777     /**
778      * Creates and returns a new MethodInvocation node to represent a call to
779      * the factory method that replaces a direct constructor call.<br>
780      * The original constructor call is marked as replaced by the new method
781      * call with the ASTRewrite instance fCtorCallRewriter.
782      * @param ast utility object used to create AST nodes
783      * @param ctorCall the ClassInstanceCreation to be marked as replaced
784      */

785     private MethodInvocation createFactoryMethodCall(AST ast, ClassInstanceCreation ctorCall,
786                                                      ASTRewrite unitRewriter, TextEditGroup gd) {
787         MethodInvocation factoryMethodCall= ast.newMethodInvocation();
788
789         List JavaDoc actualFactoryArgs= factoryMethodCall.arguments();
790         List JavaDoc actualCtorArgs= ctorCall.arguments();
791
792         // Need to use a qualified name for the factory method if we're not
793
// in the context of the class holding the factory.
794
AbstractTypeDeclaration callOwner= (AbstractTypeDeclaration) ASTNodes.getParent(ctorCall, AbstractTypeDeclaration.class);
795         ITypeBinding callOwnerBinding= callOwner.resolveBinding();
796
797         if (callOwnerBinding == null ||
798             !Bindings.equals(callOwner.resolveBinding(), fFactoryOwningClass.resolveBinding())) {
799             String JavaDoc qualifier= fImportRewriter.addImport(fFactoryOwningClass.resolveBinding());
800             factoryMethodCall.setExpression(ASTNodeFactory.newName(ast, qualifier));
801         }
802         
803         factoryMethodCall.setName(ast.newSimpleName(fNewMethodName));
804
805         for(int i=0; i < actualCtorArgs.size(); i++) {
806             Expression actualCtorArg= (Expression) actualCtorArgs.get(i);
807             ASTNode movedArg= unitRewriter.createMoveTarget(actualCtorArg);
808
809             actualFactoryArgs.add(movedArg);
810 // unitRewriter.createMove(actualCtorArg);
811
// ASTNode rewrittenArg= rewriteArgument(actualCtorArg);
812
// actualFactoryArgs.add(rewrittenArg);
813
}
814
815         unitRewriter.replace(ctorCall, factoryMethodCall, gd);
816
817         return factoryMethodCall;
818     }
819
820     /**
821      * Returns true iff the given <code>ICompilationUnit</code> is the unit
822      * containing the original constructor.
823      * @param unit
824      */

825     private boolean isConstructorUnit(ICompilationUnit unit) {
826         return unit.equals(ASTCreator.getCu(fCtorOwningClass));
827     }
828
829     /**
830      * Returns true iff we should actually change the original constructor's
831      * visibility to <code>protected</code>. This takes into account the user-
832      * requested mode and whether the constructor's compilation unit is in
833      * source form.
834      */

835     private boolean shouldProtectConstructor() {
836         return fProtectConstructor && fCtorOwningClass != null;
837     }
838
839     /**
840      * Creates and adds the necessary change to make the constructor method protected.
841      * Returns false iff the constructor didn't exist (i.e. was implicit)
842      */

843     private boolean protectConstructor(CompilationUnit unitAST, ASTRewrite unitRewriter, TextEditGroup declGD) {
844         MethodDeclaration constructor= (MethodDeclaration) unitAST.findDeclaringNode(fCtorBinding.getKey());
845
846         // No need to rewrite the modifiers if the visibility is what we already want it to be.
847
if (constructor == null || (JdtFlags.getVisibilityCode(constructor)) == fConstructorVisibility)
848             return false;
849         ModifierRewrite.create(unitRewriter, constructor).setVisibility(fConstructorVisibility, declGD);
850         return true;
851     }
852
853     /**
854      * Add all changes necessary on the <code>ICompilationUnit</code> in the given
855      * <code>SearchResultGroup</code> to implement the refactoring transformation
856      * to the given <code>CompilationUnitChange</code>.
857      * @param rg the <code>SearchResultGroup</code> for which changes should be created
858      * @param unitChange the CompilationUnitChange object for the compilation unit in question
859      * @throws CoreException
860      */

861     private boolean addAllChangesFor(SearchResultGroup rg, ICompilationUnit unitHandle, CompilationUnitChange unitChange) throws CoreException {
862 // ICompilationUnit unitHandle= rg.getCompilationUnit();
863
Assert.isTrue(rg == null || rg.getCompilationUnit() == unitHandle);
864         CompilationUnit unit= getASTFor(unitHandle);
865         ASTRewrite unitRewriter= ASTRewrite.create(unit.getAST());
866         MultiTextEdit root= new MultiTextEdit();
867         boolean someChange= false;
868
869         unitChange.setEdit(root);
870         fImportRewriter= StubUtility.createImportRewrite(unit, true);
871
872         // First create the factory method
873
if (unitHandle.equals(fFactoryUnitHandle)) {
874             TextEditGroup factoryGD= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_addFactoryMethod);
875
876             createFactoryChange(unitRewriter, unit, factoryGD);
877             unitChange.addTextEditGroup(factoryGD);
878             someChange= true;
879         }
880
881         // Now rewrite all the constructor calls to use the factory method
882
if (rg != null)
883             if (replaceConstructorCalls(rg, unit, unitRewriter, unitChange))
884                 someChange= true;
885
886         // Finally, make the constructor private, if requested.
887
if (shouldProtectConstructor() && isConstructorUnit(unitHandle)) {
888             TextEditGroup declGD= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_protectConstructor);
889
890             if (protectConstructor(unit, unitRewriter, declGD)) {
891                 unitChange.addTextEditGroup(declGD);
892                 someChange= true;
893             }
894         }
895
896         if (someChange) {
897             root.addChild(unitRewriter.rewriteAST());
898             root.addChild(fImportRewriter.rewriteImports(null));
899         }
900
901         return someChange;
902     }
903
904     /**
905      * Returns an AST for the given compilation unit handle.<br>
906      * If this is the unit containing the selection or the unit in which the factory
907      * is to reside, checks the appropriate field (<code>fCU</code> or <code>fFactoryCU</code>,
908      * respectively) and initializes the field with a new AST only if not already done.
909      */

910     private CompilationUnit getASTFor(ICompilationUnit unitHandle) {
911         if (unitHandle.equals(fCUHandle)) { // is this the unit containing the selection?
912
if (fCU == null) {
913                 fCU= ASTCreator.createAST(unitHandle, null);
914                 if (fCU.equals(fFactoryUnitHandle)) // if selection unit and factory unit are the same...
915
fFactoryCU= fCU; // ...make sure the factory unit gets initialized
916
}
917             return fCU;
918         } else if (unitHandle.equals(fFactoryUnitHandle)) { // is this the "factory unit"?
919
if (fFactoryCU == null)
920                 fFactoryCU= ASTCreator.createAST(unitHandle, null);
921             return fFactoryCU;
922         } else
923             return ASTCreator.createAST(unitHandle, null);
924     }
925
926     /**
927      * Use the given <code>ASTRewrite</code> to replace direct calls to the constructor
928      * with calls to the newly-created factory method.
929      * @param rg the <code>SearchResultGroup</code> indicating all of the constructor references
930      * @param unit the <code>CompilationUnit</code> to be rewritten
931      * @param unitRewriter the rewriter
932      * @param unitChange the compilation unit change
933      * @throws CoreException
934      * @return true iff at least one constructor call site was rewritten.
935      */

936     private boolean replaceConstructorCalls(SearchResultGroup rg, CompilationUnit unit,
937                                             ASTRewrite unitRewriter, CompilationUnitChange unitChange)
938     throws CoreException {
939         Assert.isTrue(ASTCreator.getCu(unit).equals(rg.getCompilationUnit()));
940         SearchMatch[] hits= rg.getSearchResults();
941         AST ctorCallAST= unit.getAST();
942         boolean someCallPatched= false;
943
944         for(int i=0; i < hits.length; i++) {
945             ClassInstanceCreation creation= getCtorCallAt(hits[i].getOffset(), hits[i].getLength(), unit);
946
947             if (creation != null) {
948                 TextEditGroup gd= new TextEditGroup(RefactoringCoreMessages.IntroduceFactory_replaceCalls);
949
950                 createFactoryMethodCall(ctorCallAST, creation, unitRewriter, gd);
951                 unitChange.addTextEditGroup(gd);
952                 someCallPatched= true;
953             }
954         }
955         return someCallPatched;
956     }
957
958     /**
959      * Look "in the vicinity" of the given range to find the <code>ClassInstanceCreation</code>
960      * node that this search hit identified. Necessary because the <code>SearchEngine</code>
961      * doesn't always cough up text extents that <code>NodeFinder.perform()</code> agrees with.
962      * @param start
963      * @param length
964      * @param unitAST
965      * @return may return null if this is really a constructor->constructor call (e.g. "this(...)")
966      */

967     private ClassInstanceCreation getCtorCallAt(int start, int length, CompilationUnit unitAST) throws CoreException {
968         ICompilationUnit unitHandle= ASTCreator.getCu(unitAST);
969         ASTNode node= NodeFinder.perform(unitAST, start, length);
970
971         if (node == null)
972             throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
973                     Messages.format(RefactoringCoreMessages.IntroduceFactory_noASTNodeForConstructorSearchHit,
974                             new Object JavaDoc[] { Integer.toString(start), Integer.toString(start + length),
975                                 unitHandle.getSource().substring(start, start + length),
976                                 unitHandle.getElementName() }),
977                     null));
978
979         if (node instanceof ClassInstanceCreation) {
980             return (ClassInstanceCreation) node;
981         } else if (node instanceof VariableDeclaration) {
982             Expression init= ((VariableDeclaration) node).getInitializer();
983
984             if (init instanceof ClassInstanceCreation) {
985                 return (ClassInstanceCreation) init;
986             } else if (init != null)
987                 throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
988                         Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedInitializerNodeType,
989                                 new Object JavaDoc[] { init.toString(), unitHandle.getElementName() }),
990                         null));
991             else
992                 throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
993                         Messages.format(RefactoringCoreMessages.IntroduceFactory_noConstructorCallNodeInsideFoundVarbleDecl,
994                                 new Object JavaDoc[] { node.toString() }),
995                         null));
996         } else if (node instanceof ConstructorInvocation) {
997             // This is a call we can bypass; it's from one constructor flavor
998
// to another flavor on the same class.
999
return null;
1000        } else if (node instanceof SuperConstructorInvocation) {
1001            // This is a call we can bypass; it's from one constructor flavor
1002
// to another flavor on the same class.
1003
fConstructorVisibility= Modifier.PROTECTED;
1004            return null;
1005        } else if (node instanceof ExpressionStatement) {
1006            Expression expr= ((ExpressionStatement) node).getExpression();
1007
1008            if (expr instanceof ClassInstanceCreation)
1009                return (ClassInstanceCreation) expr;
1010            else
1011                throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
1012                        Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit,
1013                                new Object JavaDoc[] { expr.toString(), unitHandle.getElementName() }),
1014                        null));
1015        } else if (node instanceof SimpleName && (node.getParent() instanceof MethodDeclaration || node.getParent() instanceof AbstractTypeDeclaration)) {
1016            // We seem to have been given a hit for an implicit call to the base-class constructor.
1017
// Do nothing with this (implicit) call, but have to make sure we make the derived class
1018
// doesn't lose access to the base-class constructor (so make it 'protected', not 'private').
1019
fConstructorVisibility= Modifier.PROTECTED;
1020            return null;
1021        } else
1022            throw new CoreException(JavaUIStatus.createError(IStatus.ERROR,
1023                    Messages.format(RefactoringCoreMessages.IntroduceFactory_unexpectedASTNodeTypeForConstructorSearchHit,
1024                            new Object JavaDoc[] { node.getClass().getName() + "('" + node.toString() + "')", unitHandle.getElementName() }), //$NON-NLS-1$ //$NON-NLS-2$
1025
null));
1026    }
1027
1028    /**
1029     * Perform the AST rewriting necessary on the given <code>CompilationUnit</code>
1030     * to create the factory method. The method will reside on the type identified by
1031     * <code>fFactoryOwningClass</code>.
1032     * @param unitRewriter
1033     * @param unit
1034     * @param gd the <code>GroupDescription</code> to associate with the changes made
1035     */

1036    private void createFactoryChange(ASTRewrite unitRewriter, CompilationUnit unit, TextEditGroup gd) {
1037        // ================================================================================
1038
// First add the factory itself (method, class, and interface as needed/directed by user)
1039
AST ast= unit.getAST();
1040
1041        fFactoryMethod= createFactoryMethod(ast, fCtorBinding, unitRewriter);
1042
1043        AbstractTypeDeclaration factoryOwner= (AbstractTypeDeclaration) unit.findDeclaringNode(fFactoryOwningClass.resolveBinding().getKey());
1044        fImportRewriter.addImport(fCtorOwningClass.resolveBinding());
1045
1046        int idx= ASTNodes.getInsertionIndex(fFactoryMethod, factoryOwner.bodyDeclarations());
1047
1048        if (idx < 0) idx= 0; // Guard against bug in getInsertionIndex()
1049
unitRewriter.getListRewrite(factoryOwner, factoryOwner.getBodyDeclarationsProperty()).insertAt(fFactoryMethod, idx, gd);
1050    }
1051
1052    public Change createChange(IProgressMonitor pm) throws CoreException {
1053        try {
1054            pm.beginTask(RefactoringCoreMessages.IntroduceFactory_createChanges, fAllCallsTo.length);
1055            final ITypeBinding binding= fFactoryOwningClass.resolveBinding();
1056            final Map JavaDoc arguments= new HashMap JavaDoc();
1057            String JavaDoc project= null;
1058            IJavaProject javaProject= fCUHandle.getJavaProject();
1059            if (javaProject != null)
1060                project= javaProject.getElementName();
1061            int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
1062            if (binding.isNested() && !binding.isMember())
1063                flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
1064            final String JavaDoc description= Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_descriptor_description_short, fCtorOwningClass.getName());
1065            final String JavaDoc header= Messages.format(RefactoringCoreMessages.IntroduceFactory_descriptor_description, new String JavaDoc[] { fNewMethodName, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(fCtorBinding, JavaElementLabels.ALL_FULLY_QUALIFIED)});
1066            final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
1067            comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(fCtorBinding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
1068            comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_factory_pattern, fNewMethodName));
1069            comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceFactoryRefactoring_owner_pattern, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED)));
1070            if (fProtectConstructor)
1071                comment.addSetting(RefactoringCoreMessages.IntroduceFactoryRefactoring_declare_private);
1072            final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaRefactorings.INTRODUCE_FACTORY, project, description, comment.asString(), arguments, flags);
1073            arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCUHandle));
1074            arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fNewMethodName);
1075            arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1, descriptor.elementToHandle(binding.getJavaElement()));
1076            arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, new Integer JavaDoc(fSelectionStart).toString() + " " + new Integer JavaDoc(fSelectionLength).toString()); //$NON-NLS-1$
1077
arguments.put(ATTRIBUTE_PROTECT, Boolean.valueOf(fProtectConstructor).toString());
1078            final DynamicValidationStateChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.IntroduceFactory_name);
1079            boolean hitInFactoryClass= false;
1080            boolean hitInCtorClass= false;
1081            for (int i= 0; i < fAllCallsTo.length; i++) {
1082                SearchResultGroup rg= fAllCallsTo[i];
1083                ICompilationUnit unitHandle= rg.getCompilationUnit();
1084                CompilationUnitChange cuChange= new CompilationUnitChange(getName(), unitHandle);
1085
1086                if (addAllChangesFor(rg, unitHandle, cuChange))
1087                    result.add(cuChange);
1088
1089                if (unitHandle.equals(fFactoryUnitHandle))
1090                    hitInFactoryClass= true;
1091                if (unitHandle.equals(ASTCreator.getCu(fCtorOwningClass)))
1092                    hitInCtorClass= true;
1093
1094                pm.worked(1);
1095                if (pm.isCanceled())
1096                    throw new OperationCanceledException();
1097            }
1098            if (!hitInFactoryClass) { // Handle factory class if no search hits there
1099
CompilationUnitChange cuChange= new CompilationUnitChange(getName(), fFactoryUnitHandle);
1100                addAllChangesFor(null, fFactoryUnitHandle, cuChange);
1101                result.add(cuChange);
1102            }
1103            if (!hitInCtorClass && !fFactoryUnitHandle.equals(ASTCreator.getCu(fCtorOwningClass))) { // Handle constructor-owning class if no search hits there
1104
CompilationUnitChange cuChange= new CompilationUnitChange(getName(), ASTCreator.getCu(fCtorOwningClass));
1105                addAllChangesFor(null, ASTCreator.getCu(fCtorOwningClass), cuChange);
1106                result.add(cuChange);
1107            }
1108            return result;
1109        } finally {
1110            pm.done();
1111        }
1112    }
1113
1114    /*
1115     * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName()
1116     */

1117    public String JavaDoc getName() {
1118        return RefactoringCoreMessages.IntroduceFactory_name;
1119    }
1120
1121    /**
1122     * Returns the name to be used for the generated factory method.
1123     */

1124    public String JavaDoc getNewMethodName() {
1125        return fNewMethodName;
1126    }
1127
1128    /**
1129     * Sets the name to be used for the generated factory method.<br>
1130     * Returns a <code>RefactoringStatus</code> that indicates whether the
1131     * given name is valid for the new factory method.
1132     * @param newMethodName the name to be used for the generated factory method
1133     */

1134    public RefactoringStatus setNewMethodName(String JavaDoc newMethodName) {
1135        Assert.isNotNull(newMethodName);
1136        fNewMethodName = newMethodName;
1137
1138        RefactoringStatus stat= Checks.checkMethodName(newMethodName);
1139
1140        stat.merge(isUniqueMethodName(newMethodName));
1141
1142        return stat;
1143    }
1144
1145    /**
1146     * Returns a <code>RefactoringStatus</code> that identifies whether the
1147     * the name <code>newMethodName</code> is available to use as the name of
1148     * the new factory method within the factory-owner class (either a to-be-
1149     * created factory class or the constructor-owning class, depending on the
1150     * user options).
1151     * @param methodName
1152     */

1153    private RefactoringStatus isUniqueMethodName(String JavaDoc methodName) {
1154        boolean conflict= hasMethod(fFactoryOwningClass, methodName);
1155
1156        return conflict ? RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_duplicateMethodName + methodName) : new RefactoringStatus();
1157    }
1158
1159    /**
1160     * Returns true iff the given <code>AbstractTypeDeclaration</code> has a method with
1161     * the given name.
1162     * @param type
1163     * @param name
1164     */

1165    private boolean hasMethod(AbstractTypeDeclaration type, String JavaDoc name) {
1166        List JavaDoc decls= type.bodyDeclarations();
1167
1168        for (Iterator JavaDoc iter = decls.iterator(); iter.hasNext();) {
1169            BodyDeclaration decl = (BodyDeclaration) iter.next();
1170            if (decl instanceof MethodDeclaration) {
1171                if (((MethodDeclaration) decl).getName().getIdentifier().equals(name))
1172                    return true;
1173            }
1174        }
1175        return false;
1176    }
1177
1178    /**
1179     * Returns true iff the selected constructor can be protected.
1180     */

1181    public boolean canProtectConstructor() {
1182        return !fCtorBinding.isSynthetic() && fFactoryCU.findDeclaringNode(fCtorBinding.getKey()) != null;
1183    }
1184
1185    /**
1186     * If the argument is true, change the visibility of the constructor to
1187     * <code>protected</code>, thereby encapsulating it.
1188     * @param protectConstructor
1189     */

1190    public void setProtectConstructor(boolean protectConstructor) {
1191        fProtectConstructor = protectConstructor;
1192    }
1193
1194    /**
1195     * Returns the project on behalf of which this refactoring was invoked.
1196     */

1197    public IJavaProject getProject() {
1198        return fCUHandle.getJavaProject();
1199    }
1200
1201    /**
1202     * Sets the class on which the generated factory method is to be placed.
1203     * @param fullyQualifiedTypeName an <code>IType</code> referring to an existing class
1204     */

1205    public RefactoringStatus setFactoryClass(String JavaDoc fullyQualifiedTypeName) {
1206        IType factoryType;
1207
1208        try {
1209            factoryType= findFactoryClass(fullyQualifiedTypeName);
1210            if (factoryType == null)
1211                return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceFactory_noSuchClass, fullyQualifiedTypeName));
1212
1213            if (factoryType.isAnnotation())
1214                return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryMethodOnAnnotation);
1215            if (factoryType.isInterface())
1216                return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryMethodOnInterface);
1217        } catch (JavaModelException e) {
1218            return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantCheckForInterface);
1219        }
1220
1221        ICompilationUnit factoryUnitHandle= factoryType.getCompilationUnit();
1222
1223        if (factoryType.isBinary())
1224            return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceFactory_cantPutFactoryInBinaryClass);
1225        else {
1226            try {
1227                if (!fFactoryUnitHandle.equals(factoryUnitHandle)) {
1228                    fFactoryCU= getASTFor(factoryUnitHandle);
1229                    fFactoryUnitHandle= factoryUnitHandle;
1230                }
1231                fFactoryOwningClass= (AbstractTypeDeclaration) ASTNodes.getParent(NodeFinder.perform(fFactoryCU, factoryType.getNameRange()), AbstractTypeDeclaration.class);
1232
1233                String JavaDoc factoryPkg= factoryType.getPackageFragment().getElementName();
1234                String JavaDoc ctorPkg= fCtorOwningClass.resolveBinding().getPackage().getName();
1235
1236                if (!factoryPkg.equals(ctorPkg))
1237                    fConstructorVisibility= Modifier.PUBLIC;
1238                else if (fFactoryOwningClass != fCtorOwningClass)
1239                    fConstructorVisibility= 0; // No such thing as Modifier.PACKAGE...
1240

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

1245            } catch (JavaModelException e) {
1246                return RefactoringStatus.createFatalErrorStatus(e.getMessage());
1247            }
1248            return new RefactoringStatus();
1249        }
1250    }
1251
1252    /**
1253     * Finds the factory class associated with the fully qualified name.
1254     * @param fullyQualifiedTypeName the fully qualified type name
1255     * @return the factory class, or <code>null</code> if not found
1256     * @throws JavaModelException if an error occurs while finding the factory class
1257     */

1258    private IType findFactoryClass(String JavaDoc fullyQualifiedTypeName) throws JavaModelException {
1259        IType factoryType= getProject().findType(fullyQualifiedTypeName);
1260        if (factoryType == null) // presumably a non-primary type; try the search engine
1261
factoryType= findNonPrimaryType(fullyQualifiedTypeName, new NullProgressMonitor(), new RefactoringStatus());
1262        return factoryType;
1263    }
1264
1265    /**
1266     * Returns the name of the class on which the generated factory method is
1267     * to be placed.
1268     */

1269    public String JavaDoc getFactoryClassName() {
1270        return fFactoryOwningClass.resolveBinding().getQualifiedName();
1271    }
1272
1273    public RefactoringStatus initialize(final RefactoringArguments arguments) {
1274        if (arguments instanceof JavaRefactoringArguments) {
1275            final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
1276            final String JavaDoc selection= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION);
1277            if (selection != null) {
1278                int offset= -1;
1279                int length= -1;
1280                final StringTokenizer JavaDoc tokenizer= new StringTokenizer JavaDoc(selection);
1281                if (tokenizer.hasMoreTokens())
1282                    offset= Integer.valueOf(tokenizer.nextToken()).intValue();
1283                if (tokenizer.hasMoreTokens())
1284                    length= Integer.valueOf(tokenizer.nextToken()).intValue();
1285                if (offset >= 0 && length >= 0) {
1286                    fSelectionStart= offset;
1287                    fSelectionLength= length;
1288                } else
1289                    return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object JavaDoc[] { selection, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION}));
1290            } else
1291                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION));
1292            String JavaDoc handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT);
1293            if (handle != null) {
1294                final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
1295                if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT)
1296                    return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_FACTORY);
1297                else {
1298                    fCUHandle= (ICompilationUnit) element;
1299                    initialize();
1300                }
1301            } else
1302                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
1303            handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1);
1304            if (handle != null) {
1305                final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
1306                if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
1307                    return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_FACTORY);
1308                else {
1309                    final IType type= (IType) element;
1310                    fFactoryClassName= type.getFullyQualifiedName();
1311                }
1312            } else
1313                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
1314            final String JavaDoc name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME);
1315            if (name != null && !"".equals(name)) //$NON-NLS-1$
1316
fNewMethodName= name;
1317            else
1318                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME));
1319            final String JavaDoc protect= extended.getAttribute(ATTRIBUTE_PROTECT);
1320            if (protect != null) {
1321                fProtectConstructor= Boolean.valueOf(protect).booleanValue();
1322            } else
1323                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_PROTECT));
1324        } else
1325            return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1326        return new RefactoringStatus();
1327    }
1328}
1329
Popular Tags