0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * N.Metchev@teamphone.com - contributed fixes for
0011: * - convert anonymous to nested should sometimes declare class as static [refactoring]
0012: * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=43360)
0013: * - Convert anonymous to nested: should show error if field form outer anonymous type is references [refactoring]
0014: * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48282)
0015: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.code;
0016:
0017: import java.util.ArrayList;
0018: import java.util.Arrays;
0019: import java.util.Collections;
0020: import java.util.HashMap;
0021: import java.util.HashSet;
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Map;
0025: import java.util.Set;
0026: import java.util.StringTokenizer;
0027:
0028: import org.eclipse.core.runtime.Assert;
0029: import org.eclipse.core.runtime.CoreException;
0030: import org.eclipse.core.runtime.IProgressMonitor;
0031:
0032: import org.eclipse.ltk.core.refactoring.Change;
0033: import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
0034: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0035: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0036: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
0037:
0038: import org.eclipse.jdt.core.ICompilationUnit;
0039: import org.eclipse.jdt.core.IJavaElement;
0040: import org.eclipse.jdt.core.IJavaProject;
0041: import org.eclipse.jdt.core.JavaModelException;
0042: import org.eclipse.jdt.core.dom.AST;
0043: import org.eclipse.jdt.core.dom.ASTNode;
0044: import org.eclipse.jdt.core.dom.ASTVisitor;
0045: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
0046: import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
0047: import org.eclipse.jdt.core.dom.ArrayCreation;
0048: import org.eclipse.jdt.core.dom.ArrayInitializer;
0049: import org.eclipse.jdt.core.dom.ArrayType;
0050: import org.eclipse.jdt.core.dom.Assignment;
0051: import org.eclipse.jdt.core.dom.BodyDeclaration;
0052: import org.eclipse.jdt.core.dom.ClassInstanceCreation;
0053: import org.eclipse.jdt.core.dom.CompilationUnit;
0054: import org.eclipse.jdt.core.dom.Expression;
0055: import org.eclipse.jdt.core.dom.FieldAccess;
0056: import org.eclipse.jdt.core.dom.FieldDeclaration;
0057: import org.eclipse.jdt.core.dom.IBinding;
0058: import org.eclipse.jdt.core.dom.IMethodBinding;
0059: import org.eclipse.jdt.core.dom.ITypeBinding;
0060: import org.eclipse.jdt.core.dom.IVariableBinding;
0061: import org.eclipse.jdt.core.dom.Initializer;
0062: import org.eclipse.jdt.core.dom.Javadoc;
0063: import org.eclipse.jdt.core.dom.MethodDeclaration;
0064: import org.eclipse.jdt.core.dom.Modifier;
0065: import org.eclipse.jdt.core.dom.Name;
0066: import org.eclipse.jdt.core.dom.ParameterizedType;
0067: import org.eclipse.jdt.core.dom.QualifiedName;
0068: import org.eclipse.jdt.core.dom.SimpleName;
0069: import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
0070: import org.eclipse.jdt.core.dom.Statement;
0071: import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
0072: import org.eclipse.jdt.core.dom.SuperFieldAccess;
0073: import org.eclipse.jdt.core.dom.Type;
0074: import org.eclipse.jdt.core.dom.TypeDeclaration;
0075: import org.eclipse.jdt.core.dom.TypeParameter;
0076: import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
0077: import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
0078: import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
0079: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
0080: import org.eclipse.jdt.core.refactoring.descriptors.ConvertAnonymousDescriptor;
0081: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0082:
0083: import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
0084: import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
0085: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
0086: import org.eclipse.jdt.internal.corext.dom.Bindings;
0087: import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
0088: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
0089: import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel;
0090: import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroup;
0091: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0092: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0093: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
0094: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
0095: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0096: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
0097: import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
0098: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
0099: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
0100: import org.eclipse.jdt.internal.corext.util.JdtFlags;
0101: import org.eclipse.jdt.internal.corext.util.Messages;
0102:
0103: import org.eclipse.jdt.ui.CodeGeneration;
0104: import org.eclipse.jdt.ui.JavaElementLabels;
0105:
0106: import org.eclipse.jdt.internal.ui.text.correction.ModifierCorrectionSubProcessor;
0107: import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
0108:
0109: public class ConvertAnonymousToNestedRefactoring extends
0110: ScriptableRefactoring {
0111:
0112: private static final String ATTRIBUTE_VISIBILITY = "visibility"; //$NON-NLS-1$
0113: private static final String ATTRIBUTE_FINAL = "final"; //$NON-NLS-1$
0114: private static final String ATTRIBUTE_STATIC = "static"; //$NON-NLS-1$
0115:
0116: private static final String KEY_TYPE_NAME = "type_name"; //$NON-NLS-1$
0117: private static final String KEY_PARAM_NAME_EXT = "param_name_ext"; //$NON-NLS-1$
0118: private static final String KEY_PARAM_NAME_CONST = "param_name_const"; //$NON-NLS-1$
0119: private static final String KEY_FIELD_NAME_EXT = "field_name_ext"; //$NON-NLS-1$
0120:
0121: public static class TypeVariableFinder extends ASTVisitor {
0122:
0123: private final Map fBindings = new HashMap();
0124: private final List fFound = new ArrayList();
0125:
0126: public final boolean visit(final SimpleName node) {
0127: Assert.isNotNull(node);
0128: final ITypeBinding binding = node.resolveTypeBinding();
0129: if (binding != null && binding.isTypeVariable()
0130: && !fBindings.containsKey(binding.getKey())) {
0131: fBindings.put(binding.getKey(), binding);
0132: fFound.add(binding);
0133: }
0134: return true;
0135: }
0136:
0137: public final ITypeBinding[] getResult() {
0138: final ITypeBinding[] result = new ITypeBinding[fFound
0139: .size()];
0140: fFound.toArray(result);
0141: return result;
0142: }
0143: }
0144:
0145: private int fSelectionStart;
0146: private int fSelectionLength;
0147: private ICompilationUnit fCu;
0148:
0149: private int fVisibility; /* see Modifier */
0150: private boolean fDeclareFinal = true;
0151: private boolean fDeclareStatic;
0152: private String fClassName = ""; //$NON-NLS-1$
0153:
0154: private CompilationUnit fCompilationUnitNode;
0155: private AnonymousClassDeclaration fAnonymousInnerClassNode;
0156: private Set fClassNamesUsed;
0157: private boolean fSelfInitializing = false;
0158:
0159: private LinkedProposalModel fLinkedProposalModel;
0160:
0161: /**
0162: * Creates a new convert anonymous to nested refactoring
0163: * @param unit the compilation unit, or <code>null</code> if invoked by scripting
0164: * @param selectionStart
0165: * @param selectionLength
0166: */
0167: public ConvertAnonymousToNestedRefactoring(ICompilationUnit unit,
0168: int selectionStart, int selectionLength) {
0169: Assert.isTrue(selectionStart >= 0);
0170: Assert.isTrue(selectionLength >= 0);
0171: Assert.isTrue(unit == null || unit.exists());
0172: fSelectionStart = selectionStart;
0173: fSelectionLength = selectionLength;
0174: fCu = unit;
0175: fAnonymousInnerClassNode = null;
0176: fCompilationUnitNode = null;
0177: }
0178:
0179: public ConvertAnonymousToNestedRefactoring(
0180: AnonymousClassDeclaration declaration) {
0181: Assert.isTrue(declaration != null);
0182:
0183: ASTNode astRoot = declaration.getRoot();
0184: Assert.isTrue(astRoot instanceof CompilationUnit);
0185: fCompilationUnitNode = (CompilationUnit) astRoot;
0186:
0187: IJavaElement javaElement = fCompilationUnitNode
0188: .getJavaElement();
0189: Assert.isTrue(javaElement instanceof ICompilationUnit);
0190:
0191: fCu = (ICompilationUnit) javaElement;
0192: fSelectionStart = declaration.getStartPosition();
0193: fSelectionLength = declaration.getLength();
0194: }
0195:
0196: public void setLinkedProposalModel(
0197: LinkedProposalModel linkedProposalModel) {
0198: fLinkedProposalModel = linkedProposalModel;
0199: }
0200:
0201: public int[] getAvailableVisibilities() {
0202: if (isLocalInnerType()) {
0203: return new int[] { Modifier.NONE };
0204: } else {
0205: return new int[] { Modifier.PUBLIC, Modifier.PROTECTED,
0206: Modifier.NONE, Modifier.PRIVATE };
0207: }
0208: }
0209:
0210: public boolean isLocalInnerType() {
0211: return ASTNodes.getParent(ASTNodes
0212: .getParent(fAnonymousInnerClassNode,
0213: AbstractTypeDeclaration.class),
0214: ASTNode.ANONYMOUS_CLASS_DECLARATION) != null;
0215: }
0216:
0217: public int getVisibility() {
0218: return fVisibility;
0219: }
0220:
0221: public void setVisibility(int visibility) {
0222: Assert.isTrue(visibility == Modifier.PRIVATE
0223: || visibility == Modifier.NONE
0224: || visibility == Modifier.PROTECTED
0225: || visibility == Modifier.PUBLIC);
0226: fVisibility = visibility;
0227: }
0228:
0229: public void setClassName(String className) {
0230: Assert.isNotNull(className);
0231: fClassName = className;
0232: }
0233:
0234: public boolean canEnableSettingFinal() {
0235: return true;
0236: }
0237:
0238: public boolean getDeclareFinal() {
0239: return fDeclareFinal;
0240: }
0241:
0242: public boolean getDeclareStatic() {
0243: return fDeclareStatic;
0244: }
0245:
0246: public void setDeclareFinal(boolean declareFinal) {
0247: fDeclareFinal = declareFinal;
0248: }
0249:
0250: public void setDeclareStatic(boolean declareStatic) {
0251: fDeclareStatic = declareStatic;
0252: }
0253:
0254: public String getName() {
0255: return RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name;
0256: }
0257:
0258: private boolean useThisForFieldAccess() {
0259: return StubUtility.useThisForFieldAccess(fCu.getJavaProject());
0260: }
0261:
0262: private boolean doAddComments() {
0263: return StubUtility.doAddComments(fCu.getJavaProject());
0264: }
0265:
0266: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0267: throws CoreException {
0268: RefactoringStatus result = Checks.validateModifiesFiles(
0269: ResourceUtil.getFiles(new ICompilationUnit[] { fCu }),
0270: getValidationContext());
0271: if (result.hasFatalError())
0272: return result;
0273:
0274: initAST(pm);
0275:
0276: if (fAnonymousInnerClassNode == null)
0277: return RefactoringStatus
0278: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_place_caret);
0279: if (!fSelfInitializing)
0280: initializeDefaults();
0281: if (getSuperConstructorBinding() == null)
0282: return RefactoringStatus
0283: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors);
0284: if (getSuperTypeBinding().isLocal())
0285: return RefactoringStatus
0286: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_extends_local_class);
0287: return new RefactoringStatus();
0288: }
0289:
0290: private void initializeDefaults() {
0291: fVisibility = isLocalInnerType() ? Modifier.NONE
0292: : Modifier.PRIVATE;
0293: fDeclareStatic = mustInnerClassBeStatic();
0294: }
0295:
0296: private void initAST(IProgressMonitor pm) {
0297: if (fCompilationUnitNode == null) {
0298: fCompilationUnitNode = RefactoringASTParser
0299: .parseWithASTProvider(fCu, true, pm);
0300: }
0301: if (fAnonymousInnerClassNode == null) {
0302: fAnonymousInnerClassNode = getAnonymousInnerClass(NodeFinder
0303: .perform(fCompilationUnitNode, fSelectionStart,
0304: fSelectionLength));
0305: }
0306: if (fAnonymousInnerClassNode != null) {
0307: final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) ASTNodes
0308: .getParent(fAnonymousInnerClassNode,
0309: AbstractTypeDeclaration.class);
0310: if (declaration instanceof TypeDeclaration) {
0311: final AbstractTypeDeclaration[] nested = ((TypeDeclaration) declaration)
0312: .getTypes();
0313: fClassNamesUsed = new HashSet(nested.length);
0314: for (int index = 0; index < nested.length; index++)
0315: fClassNamesUsed.add(nested[index].getName()
0316: .getIdentifier());
0317: } else
0318: fClassNamesUsed = Collections.EMPTY_SET;
0319: }
0320: }
0321:
0322: private static AnonymousClassDeclaration getAnonymousInnerClass(
0323: ASTNode node) {
0324: if (node == null)
0325: return null;
0326: if (node instanceof AnonymousClassDeclaration)
0327: return (AnonymousClassDeclaration) node;
0328: if (node instanceof ClassInstanceCreation) {
0329: AnonymousClassDeclaration anon = ((ClassInstanceCreation) node)
0330: .getAnonymousClassDeclaration();
0331: if (anon != null)
0332: return anon;
0333: }
0334: node = ASTNodes.getNormalizedNode(node);
0335: if (node.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) {
0336: AnonymousClassDeclaration anon = ((ClassInstanceCreation) node
0337: .getParent()).getAnonymousClassDeclaration();
0338: if (anon != null)
0339: return anon;
0340: }
0341: return (AnonymousClassDeclaration) ASTNodes.getParent(node,
0342: AnonymousClassDeclaration.class);
0343: }
0344:
0345: public RefactoringStatus validateInput() {
0346: RefactoringStatus result = Checks
0347: .checkTypeName(fClassName, fCu);
0348: if (result.hasFatalError())
0349: return result;
0350:
0351: if (fClassNamesUsed.contains(fClassName))
0352: return RefactoringStatus
0353: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_type_exists);
0354: IMethodBinding super ConstructorBinding = getSuperConstructorBinding();
0355: if (super ConstructorBinding == null)
0356: return RefactoringStatus
0357: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors);
0358: if (fClassName.equals(super ConstructorBinding
0359: .getDeclaringClass().getName()))
0360: return RefactoringStatus
0361: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_another_name);
0362: if (classNameHidesEnclosingType())
0363: return RefactoringStatus
0364: .createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name_hides);
0365: return result;
0366: }
0367:
0368: private boolean accessesAnonymousFields() {
0369: List anonymousInnerFieldTypes = getAllEnclosingAnonymousTypesField();
0370: List accessedField = getAllAccessedFields();
0371: final Iterator it = anonymousInnerFieldTypes.iterator();
0372: while (it.hasNext()) {
0373: final IVariableBinding variableBinding = (IVariableBinding) it
0374: .next();
0375: final Iterator it2 = accessedField.iterator();
0376: while (it2.hasNext()) {
0377: IVariableBinding variableBinding2 = (IVariableBinding) it2
0378: .next();
0379: if (Bindings.equals(variableBinding, variableBinding2)) {
0380: return true;
0381: }
0382: }
0383: }
0384: return false;
0385: }
0386:
0387: private List getAllAccessedFields() {
0388: final List accessedFields = new ArrayList();
0389:
0390: ASTVisitor visitor = new ASTVisitor() {
0391:
0392: public boolean visit(FieldAccess node) {
0393: final IVariableBinding binding = node
0394: .resolveFieldBinding();
0395: if (binding != null && !binding.isEnumConstant())
0396: accessedFields.add(binding);
0397: return super .visit(node);
0398: }
0399:
0400: public boolean visit(QualifiedName node) {
0401: final IBinding binding = node.resolveBinding();
0402: if (binding != null
0403: && binding instanceof IVariableBinding) {
0404: IVariableBinding variable = (IVariableBinding) binding;
0405: if (!variable.isEnumConstant()
0406: && variable.isField())
0407: accessedFields.add(binding);
0408: }
0409: return super .visit(node);
0410: }
0411:
0412: public boolean visit(SimpleName node) {
0413: final IBinding binding = node.resolveBinding();
0414: if (binding != null
0415: && binding instanceof IVariableBinding) {
0416: IVariableBinding variable = (IVariableBinding) binding;
0417: if (!variable.isEnumConstant()
0418: && variable.isField())
0419: accessedFields.add(binding);
0420: }
0421: return super .visit(node);
0422: }
0423:
0424: public boolean visit(SuperFieldAccess node) {
0425: final IVariableBinding binding = node
0426: .resolveFieldBinding();
0427: if (binding != null && !binding.isEnumConstant())
0428: accessedFields.add(binding);
0429: return super .visit(node);
0430: }
0431: };
0432: fAnonymousInnerClassNode.accept(visitor);
0433:
0434: return accessedFields;
0435: }
0436:
0437: private List getAllEnclosingAnonymousTypesField() {
0438: final List ans = new ArrayList();
0439: final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) ASTNodes
0440: .getParent(fAnonymousInnerClassNode,
0441: AbstractTypeDeclaration.class);
0442: AnonymousClassDeclaration anonymous = (AnonymousClassDeclaration) ASTNodes
0443: .getParent(fAnonymousInnerClassNode,
0444: ASTNode.ANONYMOUS_CLASS_DECLARATION);
0445: while (anonymous != null) {
0446: if (ASTNodes.isParent(anonymous, declaration)) {
0447: ITypeBinding binding = anonymous.resolveBinding();
0448: if (binding != null) {
0449: ans.addAll(Arrays.asList(binding
0450: .getDeclaredFields()));
0451: }
0452: } else {
0453: break;
0454: }
0455: anonymous = (AnonymousClassDeclaration) ASTNodes.getParent(
0456: anonymous, ASTNode.ANONYMOUS_CLASS_DECLARATION);
0457: }
0458: return ans;
0459: }
0460:
0461: private boolean classNameHidesEnclosingType() {
0462: ITypeBinding type = ((AbstractTypeDeclaration) ASTNodes
0463: .getParent(fAnonymousInnerClassNode,
0464: AbstractTypeDeclaration.class))
0465: .resolveBinding();
0466: while (type != null) {
0467: if (fClassName.equals(type.getName()))
0468: return true;
0469: type = type.getDeclaringClass();
0470: }
0471: return false;
0472: }
0473:
0474: /*
0475: * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor)
0476: */
0477: public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
0478: throws CoreException {
0479: try {
0480: RefactoringStatus status = validateInput();
0481: if (accessesAnonymousFields())
0482: status
0483: .merge(RefactoringStatus
0484: .createErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_anonymous_field_access));
0485: return status;
0486: } finally {
0487: pm.done();
0488: }
0489: }
0490:
0491: public CompilationUnitChange createCompilationUnitChange(
0492: IProgressMonitor pm) throws CoreException {
0493: final CompilationUnitRewrite rewrite = new CompilationUnitRewrite(
0494: fCu, fCompilationUnitNode);
0495: final ITypeBinding[] typeParameters = getTypeParameters();
0496: addNestedClass(rewrite, typeParameters);
0497: modifyConstructorCall(rewrite, typeParameters);
0498: return rewrite
0499: .createChange(
0500: RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name,
0501: false, pm);
0502: }
0503:
0504: /*
0505: * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
0506: */
0507: public Change createChange(IProgressMonitor pm)
0508: throws CoreException {
0509: final CompilationUnitChange result = createCompilationUnitChange(pm);
0510: result.setDescriptor(createRefactoringDescriptor());
0511: return result;
0512: }
0513:
0514: private ITypeBinding[] getTypeParameters() {
0515: final List parameters = new ArrayList(4);
0516: final ClassInstanceCreation creation = (ClassInstanceCreation) fAnonymousInnerClassNode
0517: .getParent();
0518: if (fDeclareStatic) {
0519: final TypeVariableFinder finder = new TypeVariableFinder();
0520: creation.accept(finder);
0521: return finder.getResult();
0522: } else {
0523: final MethodDeclaration declaration = getEnclosingMethodDeclaration(creation);
0524: if (declaration != null) {
0525: ITypeBinding binding = null;
0526: TypeParameter parameter = null;
0527: for (final Iterator iterator = declaration
0528: .typeParameters().iterator(); iterator
0529: .hasNext();) {
0530: parameter = (TypeParameter) iterator.next();
0531: binding = parameter.resolveBinding();
0532: if (binding != null)
0533: parameters.add(binding);
0534: }
0535: }
0536: }
0537: final TypeVariableFinder finder = new TypeVariableFinder();
0538: creation.accept(finder);
0539: final ITypeBinding[] variables = finder.getResult();
0540: final List remove = new ArrayList(4);
0541: boolean match = false;
0542: ITypeBinding binding = null;
0543: ITypeBinding variable = null;
0544: for (final Iterator iterator = parameters.iterator(); iterator
0545: .hasNext();) {
0546: match = false;
0547: binding = (ITypeBinding) iterator.next();
0548: for (int index = 0; index < variables.length; index++) {
0549: variable = variables[index];
0550: if (variable.equals(binding))
0551: match = true;
0552: }
0553: if (!match)
0554: remove.add(binding);
0555: }
0556: parameters.removeAll(remove);
0557: final ITypeBinding[] result = new ITypeBinding[parameters
0558: .size()];
0559: parameters.toArray(result);
0560: return result;
0561: }
0562:
0563: private MethodDeclaration getEnclosingMethodDeclaration(ASTNode node) {
0564: ASTNode parent = node.getParent();
0565: if (parent != null) {
0566: if (parent instanceof AbstractTypeDeclaration)
0567: return null;
0568: else if (parent instanceof MethodDeclaration)
0569: return (MethodDeclaration) parent;
0570: return getEnclosingMethodDeclaration(parent);
0571: }
0572: return null;
0573: }
0574:
0575: private RefactoringChangeDescriptor createRefactoringDescriptor() {
0576: final ITypeBinding binding = fAnonymousInnerClassNode
0577: .resolveBinding();
0578: final String[] labels = new String[] {
0579: BindingLabelProvider.getBindingLabel(binding,
0580: JavaElementLabels.ALL_FULLY_QUALIFIED),
0581: BindingLabelProvider.getBindingLabel(binding
0582: .getDeclaringMethod(),
0583: JavaElementLabels.ALL_FULLY_QUALIFIED) };
0584: final Map arguments = new HashMap();
0585: final String projectName = fCu.getJavaProject()
0586: .getElementName();
0587: final int flags = RefactoringDescriptor.STRUCTURAL_CHANGE
0588: | JavaRefactoringDescriptor.JAR_REFACTORING
0589: | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
0590: final String description = RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description_short;
0591: final String header = Messages
0592: .format(
0593: RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description,
0594: labels);
0595: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
0596: projectName, this , header);
0597: comment
0598: .addSetting(Messages
0599: .format(
0600: RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_original_pattern,
0601: BindingLabelProvider
0602: .getBindingLabel(
0603: binding,
0604: JavaElementLabels.ALL_FULLY_QUALIFIED)));
0605: comment
0606: .addSetting(Messages
0607: .format(
0608: RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_class_name_pattern,
0609: fClassName));
0610: String visibility = JdtFlags.getVisibilityString(fVisibility);
0611: if (visibility.length() == 0)
0612: visibility = RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_default_visibility;
0613: comment
0614: .addSetting(Messages
0615: .format(
0616: RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_visibility_pattern,
0617: visibility));
0618: if (fDeclareFinal && fDeclareStatic)
0619: comment
0620: .addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final_static);
0621: else if (fDeclareFinal)
0622: comment
0623: .addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final);
0624: else if (fDeclareStatic)
0625: comment
0626: .addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_static);
0627: arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT,
0628: JavaRefactoringDescriptorUtil.elementToHandle(
0629: projectName, fCu));
0630: arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME,
0631: fClassName);
0632: arguments.put(
0633: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION,
0634: new Integer(fSelectionStart).toString() + ' '
0635: + new Integer(fSelectionLength).toString());
0636: arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal)
0637: .toString());
0638: arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic)
0639: .toString());
0640: arguments.put(ATTRIBUTE_VISIBILITY, new Integer(fVisibility)
0641: .toString());
0642:
0643: ConvertAnonymousDescriptor descriptor = new ConvertAnonymousDescriptor(
0644: projectName, description, comment.asString(),
0645: arguments, flags);
0646: return new RefactoringChangeDescriptor(descriptor);
0647: }
0648:
0649: private void modifyConstructorCall(CompilationUnitRewrite rewrite,
0650: ITypeBinding[] parameters) {
0651: rewrite.getASTRewrite().replace(
0652: fAnonymousInnerClassNode.getParent(),
0653: createNewClassInstanceCreation(rewrite, parameters),
0654: null);
0655: }
0656:
0657: private ASTNode createNewClassInstanceCreation(
0658: CompilationUnitRewrite rewrite, ITypeBinding[] parameters) {
0659: AST ast = fAnonymousInnerClassNode.getAST();
0660: ClassInstanceCreation newClassCreation = ast
0661: .newClassInstanceCreation();
0662: newClassCreation.setAnonymousClassDeclaration(null);
0663: Type type = null;
0664: SimpleName newNameNode = ast.newSimpleName(fClassName);
0665: if (parameters.length > 0) {
0666: final ParameterizedType parameterized = ast
0667: .newParameterizedType(ast
0668: .newSimpleType(newNameNode));
0669: for (int index = 0; index < parameters.length; index++)
0670: parameterized.typeArguments().add(
0671: ast.newSimpleType(ast
0672: .newSimpleName(parameters[index]
0673: .getName())));
0674: type = parameterized;
0675: } else
0676: type = ast.newSimpleType(newNameNode);
0677: newClassCreation.setType(type);
0678: copyArguments(rewrite, newClassCreation);
0679: addArgumentsForLocalsUsedInInnerClass(rewrite, newClassCreation);
0680:
0681: addLinkedPosition(KEY_TYPE_NAME, newNameNode, rewrite
0682: .getASTRewrite(), true);
0683:
0684: return newClassCreation;
0685: }
0686:
0687: private void addArgumentsForLocalsUsedInInnerClass(
0688: CompilationUnitRewrite rewrite,
0689: ClassInstanceCreation newClassCreation) {
0690: IVariableBinding[] usedLocals = getUsedLocalVariables();
0691: for (int i = 0; i < usedLocals.length; i++) {
0692: final AST ast = fAnonymousInnerClassNode.getAST();
0693: final IVariableBinding binding = usedLocals[i];
0694: Name name = null;
0695: if (binding.isEnumConstant())
0696: name = ast.newQualifiedName(ast.newSimpleName(binding
0697: .getDeclaringClass().getName()), ast
0698: .newSimpleName(binding.getName()));
0699: else
0700: name = ast.newSimpleName(binding.getName());
0701: newClassCreation.arguments().add(name);
0702: }
0703: }
0704:
0705: private void copyArguments(CompilationUnitRewrite rewrite,
0706: ClassInstanceCreation newClassCreation) {
0707: Iterator iter = ((ClassInstanceCreation) fAnonymousInnerClassNode
0708: .getParent()).arguments().iterator();
0709: if (!iter.hasNext())
0710: return;
0711:
0712: IMethodBinding super ConstructorBinding = getSuperConstructorBinding();
0713: ITypeBinding[] parameterTypes = super ConstructorBinding
0714: .getParameterTypes();
0715:
0716: List arguments = newClassCreation.arguments();
0717: ASTRewrite astRewrite = rewrite.getASTRewrite();
0718: int last = parameterTypes.length - 1;
0719:
0720: for (int i = 0; i < last; i++) {
0721: arguments.add(astRewrite.createCopyTarget((Expression) iter
0722: .next()));
0723: }
0724: if (super ConstructorBinding.isVarargs()) {
0725: AST ast = astRewrite.getAST();
0726: ArrayCreation arrayCreation = ast.newArrayCreation();
0727: arrayCreation.setType((ArrayType) rewrite
0728: .getImportRewrite().addImport(parameterTypes[last],
0729: ast));
0730: ArrayInitializer initializer = ast.newArrayInitializer();
0731: arrayCreation.setInitializer(initializer);
0732: arguments.add(arrayCreation);
0733: arguments = initializer.expressions();
0734: }
0735: while (iter.hasNext()) {
0736: arguments.add(astRewrite.createCopyTarget((Expression) iter
0737: .next()));
0738: }
0739: }
0740:
0741: private void addNestedClass(CompilationUnitRewrite rewrite,
0742: ITypeBinding[] typeParameters) throws CoreException {
0743: final AbstractTypeDeclaration declarations = (AbstractTypeDeclaration) ASTNodes
0744: .getParent(fAnonymousInnerClassNode,
0745: AbstractTypeDeclaration.class);
0746: int index = findIndexOfFistNestedClass(declarations
0747: .bodyDeclarations());
0748: if (index == -1)
0749: index = 0;
0750: rewrite.getASTRewrite().getListRewrite(declarations,
0751: declarations.getBodyDeclarationsProperty()).insertAt(
0752: createNewNestedClass(rewrite, typeParameters), index,
0753: null);
0754: }
0755:
0756: private static int findIndexOfFistNestedClass(List bodyDeclarations) {
0757: for (int i = 0, n = bodyDeclarations.size(); i < n; i++) {
0758: BodyDeclaration each = (BodyDeclaration) bodyDeclarations
0759: .get(i);
0760: if (isNestedType(each))
0761: return i;
0762: }
0763: return -1;
0764: }
0765:
0766: private static boolean isNestedType(BodyDeclaration each) {
0767: if (!(each instanceof AbstractTypeDeclaration))
0768: return false;
0769: return (each.getParent() instanceof AbstractTypeDeclaration);
0770: }
0771:
0772: private AbstractTypeDeclaration createNewNestedClass(
0773: CompilationUnitRewrite rewrite,
0774: ITypeBinding[] typeParameters) throws CoreException {
0775: final AST ast = fAnonymousInnerClassNode.getAST();
0776:
0777: final TypeDeclaration newDeclaration = ast.newTypeDeclaration();
0778: newDeclaration.setInterface(false);
0779: newDeclaration.setJavadoc(null);
0780: newDeclaration.modifiers().addAll(
0781: ASTNodeFactory.newModifiers(ast,
0782: createModifiersForNestedClass()));
0783: newDeclaration.setName(ast.newSimpleName(fClassName));
0784:
0785: TypeParameter parameter = null;
0786: for (int index = 0; index < typeParameters.length; index++) {
0787: parameter = ast.newTypeParameter();
0788: parameter.setName(ast.newSimpleName(typeParameters[index]
0789: .getName()));
0790: newDeclaration.typeParameters().add(parameter);
0791: }
0792: setSuperType(newDeclaration);
0793:
0794: IJavaProject project = fCu.getJavaProject();
0795:
0796: IVariableBinding[] bindings = getUsedLocalVariables();
0797: ArrayList fieldNames = new ArrayList();
0798: for (int i = 0; i < bindings.length; i++) {
0799: String name = StubUtility.removePrefixAndSuffixForVariable(
0800: project, bindings[i]);
0801: String[] fieldNameProposals = StubUtility
0802: .getVariableNameSuggestions(
0803: StubUtility.INSTANCE_FIELD, project, name,
0804: 0, fieldNames, true);
0805: fieldNames.add(fieldNameProposals[0]);
0806:
0807: if (fLinkedProposalModel != null) {
0808: LinkedProposalPositionGroup positionGroup = fLinkedProposalModel
0809: .getPositionGroup(KEY_FIELD_NAME_EXT + i, true);
0810: for (int k = 0; k < fieldNameProposals.length; k++) {
0811: positionGroup.addProposal(fieldNameProposals[k],
0812: null, fieldNameProposals.length - k);
0813: }
0814: }
0815: }
0816: String[] allFieldNames = (String[]) fieldNames
0817: .toArray(new String[fieldNames.size()]);
0818:
0819: List newBodyDeclarations = newDeclaration.bodyDeclarations();
0820:
0821: createFieldsForAccessedLocals(rewrite, bindings, allFieldNames,
0822: newBodyDeclarations);
0823:
0824: MethodDeclaration newConstructorDecl = createNewConstructor(
0825: rewrite, bindings, allFieldNames);
0826: if (newConstructorDecl != null) {
0827: newBodyDeclarations.add(newConstructorDecl);
0828: }
0829:
0830: updateAndMoveBodyDeclarations(rewrite, bindings, allFieldNames,
0831: newBodyDeclarations, newConstructorDecl);
0832:
0833: if (doAddComments()) {
0834: String[] parameterNames = new String[typeParameters.length];
0835: for (int index = 0; index < parameterNames.length; index++) {
0836: parameterNames[index] = typeParameters[index].getName();
0837: }
0838: String string = CodeGeneration.getTypeComment(rewrite
0839: .getCu(), fClassName, parameterNames, StubUtility
0840: .getLineDelimiterUsed(fCu));
0841: if (string != null) {
0842: Javadoc javadoc = (Javadoc) rewrite.getASTRewrite()
0843: .createStringPlaceholder(string,
0844: ASTNode.JAVADOC);
0845: newDeclaration.setJavadoc(javadoc);
0846: }
0847: }
0848: if (fLinkedProposalModel != null) {
0849: addLinkedPosition(KEY_TYPE_NAME, newDeclaration.getName(),
0850: rewrite.getASTRewrite(), false);
0851: ModifierCorrectionSubProcessor
0852: .installLinkedVisibilityProposals(
0853: fLinkedProposalModel, rewrite
0854: .getASTRewrite(), newDeclaration
0855: .modifiers(), false);
0856: }
0857:
0858: return newDeclaration;
0859: }
0860:
0861: private void updateAndMoveBodyDeclarations(
0862: CompilationUnitRewrite rewriter,
0863: IVariableBinding[] bindings, String[] fieldNames,
0864: List newBodyDeclarations,
0865: MethodDeclaration newConstructorDecl)
0866: throws JavaModelException {
0867: final ASTRewrite astRewrite = rewriter.getASTRewrite();
0868: final AST ast = astRewrite.getAST();
0869:
0870: final boolean useThisAccess = useThisForFieldAccess();
0871:
0872: int fieldInsertIndex = newConstructorDecl != null ? newBodyDeclarations
0873: .lastIndexOf(newConstructorDecl)
0874: : newBodyDeclarations.size();
0875:
0876: for (Iterator iterator = fAnonymousInnerClassNode
0877: .bodyDeclarations().iterator(); iterator.hasNext();) {
0878: BodyDeclaration body = (BodyDeclaration) iterator.next();
0879:
0880: for (int i = 0; i < bindings.length; i++) {
0881: SimpleName[] names = LinkedNodeFinder.findByBinding(
0882: body, bindings[i]);
0883: String fieldName = fieldNames[i];
0884: for (int k = 0; k < names.length; k++) {
0885: SimpleName newNode = ast.newSimpleName(fieldName);
0886: if (useThisAccess) {
0887: FieldAccess access = ast.newFieldAccess();
0888: access.setExpression(ast.newThisExpression());
0889: access.setName(newNode);
0890: astRewrite.replace(names[k], access, null);
0891: } else {
0892: astRewrite.replace(names[k], newNode, null);
0893: }
0894: addLinkedPosition(KEY_FIELD_NAME_EXT + i, newNode,
0895: astRewrite, false);
0896: }
0897: }
0898: if (body instanceof Initializer
0899: || body instanceof FieldDeclaration) {
0900: newBodyDeclarations.add(fieldInsertIndex++, astRewrite
0901: .createMoveTarget(body));
0902: } else {
0903: newBodyDeclarations.add(astRewrite
0904: .createMoveTarget(body));
0905: }
0906: }
0907:
0908: if (newConstructorDecl != null) {
0909: // move initialization of existing fields to constructor if an outer is referenced
0910: List bodyStatements = newConstructorDecl.getBody()
0911: .statements();
0912:
0913: List fieldsToInitializeInConstructor = getFieldsToInitializeInConstructor();
0914: for (Iterator iter = fieldsToInitializeInConstructor
0915: .iterator(); iter.hasNext();) {
0916: VariableDeclarationFragment fragment = (VariableDeclarationFragment) iter
0917: .next();
0918: Expression initializer = fragment.getInitializer();
0919: Expression replacement = (Expression) astRewrite
0920: .get(
0921: fragment,
0922: VariableDeclarationFragment.INITIALIZER_PROPERTY);
0923: if (replacement == initializer) {
0924: replacement = (Expression) astRewrite
0925: .createMoveTarget(initializer);
0926: }
0927: astRewrite.remove(initializer, null);
0928: SimpleName fieldNameNode = ast.newSimpleName(fragment
0929: .getName().getIdentifier());
0930: bodyStatements.add(newFieldAssignment(ast,
0931: fieldNameNode, replacement, useThisAccess));
0932: }
0933: }
0934: }
0935:
0936: private void createFieldsForAccessedLocals(
0937: CompilationUnitRewrite rewrite,
0938: IVariableBinding[] varBindings, String[] fieldNames,
0939: List newBodyDeclarations) throws JavaModelException {
0940: final ImportRewrite importRewrite = rewrite.getImportRewrite();
0941: final ASTRewrite astRewrite = rewrite.getASTRewrite();
0942: final AST ast = astRewrite.getAST();
0943:
0944: for (int i = 0; i < varBindings.length; i++) {
0945: VariableDeclarationFragment fragment = ast
0946: .newVariableDeclarationFragment();
0947: fragment.setExtraDimensions(0);
0948: fragment.setInitializer(null);
0949: fragment.setName(ast.newSimpleName(fieldNames[i]));
0950: FieldDeclaration field = ast.newFieldDeclaration(fragment);
0951: ITypeBinding varType = varBindings[i].getType();
0952: field.setType(importRewrite.addImport(varType, ast));
0953: field.modifiers().addAll(
0954: ASTNodeFactory.newModifiers(ast, Modifier.PRIVATE
0955: | Modifier.FINAL));
0956: if (doAddComments()) {
0957: try {
0958: String string = CodeGeneration.getFieldComment(
0959: rewrite.getCu(), varType.getName(),
0960: fieldNames[i], StubUtility
0961: .getLineDelimiterUsed(fCu));
0962: if (string != null) {
0963: Javadoc javadoc = (Javadoc) astRewrite
0964: .createStringPlaceholder(string,
0965: ASTNode.JAVADOC);
0966: field.setJavadoc(javadoc);
0967: }
0968: } catch (CoreException exception) {
0969: throw new JavaModelException(exception);
0970: }
0971: }
0972:
0973: newBodyDeclarations.add(field);
0974:
0975: addLinkedPosition(KEY_FIELD_NAME_EXT + i, fragment
0976: .getName(), astRewrite, false);
0977: }
0978: }
0979:
0980: private void addLinkedPosition(String key, ASTNode nodeToTrack,
0981: ASTRewrite rewrite, boolean isFirst) {
0982: if (fLinkedProposalModel != null) {
0983: fLinkedProposalModel.getPositionGroup(key, true)
0984: .addPosition(rewrite.track(nodeToTrack), isFirst);
0985: }
0986: }
0987:
0988: private IVariableBinding[] getUsedLocalVariables() {
0989: final Set result = new HashSet(0);
0990: collectRefrencedVariables(fAnonymousInnerClassNode, result);
0991: ArrayList usedLocals = new ArrayList();
0992: for (Iterator iterator = result.iterator(); iterator.hasNext();) {
0993: IVariableBinding next = (IVariableBinding) iterator.next();
0994: if (isBindingToTemp(next)) {
0995: usedLocals.add(next);
0996: }
0997: }
0998: return (IVariableBinding[]) usedLocals
0999: .toArray(new IVariableBinding[usedLocals.size()]);
1000: }
1001:
1002: private void collectRefrencedVariables(ASTNode root,
1003: final Set result) {
1004: root.accept(new ASTVisitor() {
1005: public boolean visit(SimpleName node) {
1006: IBinding binding = node.resolveBinding();
1007: if (binding instanceof IVariableBinding)
1008: result.add(binding);
1009: return true;
1010: }
1011: });
1012: }
1013:
1014: private boolean isBindingToTemp(IVariableBinding variable) {
1015: if (variable.isField())
1016: return false;
1017: if (!Modifier.isFinal(variable.getModifiers()))
1018: return false;
1019: ASTNode declaringNode = fCompilationUnitNode
1020: .findDeclaringNode(variable);
1021: if (declaringNode == null)
1022: return false;
1023: if (ASTNodes.isParent(declaringNode, fAnonymousInnerClassNode))
1024: return false;
1025: return true;
1026: }
1027:
1028: private MethodDeclaration createNewConstructor(
1029: CompilationUnitRewrite rewrite,
1030: IVariableBinding[] bindings, String[] fieldNames)
1031: throws JavaModelException {
1032: ClassInstanceCreation instanceCreation = (ClassInstanceCreation) fAnonymousInnerClassNode
1033: .getParent();
1034:
1035: if (instanceCreation.arguments().isEmpty()
1036: && bindings.length == 0)
1037: return null;
1038:
1039: IJavaProject project = fCu.getJavaProject();
1040: AST ast = rewrite.getAST();
1041: ImportRewrite importRewrite = rewrite.getImportRewrite();
1042: ASTRewrite astRewrite = rewrite.getASTRewrite();
1043:
1044: MethodDeclaration newConstructor = ast.newMethodDeclaration();
1045: newConstructor.setConstructor(true);
1046: newConstructor.setExtraDimensions(0);
1047: newConstructor.setJavadoc(null);
1048: newConstructor.modifiers().addAll(
1049: ASTNodeFactory.newModifiers(ast, fVisibility));
1050: newConstructor.setName(ast.newSimpleName(fClassName));
1051: addLinkedPosition(KEY_TYPE_NAME, newConstructor.getName(),
1052: astRewrite, false);
1053:
1054: newConstructor.setBody(ast.newBlock());
1055:
1056: List newStatements = newConstructor.getBody().statements();
1057:
1058: List newParameters = newConstructor.parameters();
1059: List newParameterNames = new ArrayList();
1060:
1061: // add parameters for elements passed with the instance creation
1062: if (!instanceCreation.arguments().isEmpty()) {
1063: IMethodBinding constructorBinding = getSuperConstructorBinding();
1064: if (constructorBinding != null) {
1065: SuperConstructorInvocation super ConstructorInvocation = ast
1066: .newSuperConstructorInvocation();
1067: ITypeBinding[] parameterTypes = constructorBinding
1068: .getParameterTypes();
1069: String[][] parameterNames = StubUtility
1070: .suggestArgumentNamesWithProposals(project,
1071: constructorBinding);
1072: for (int i = 0; i < parameterNames.length; i++) {
1073: String[] nameProposals = parameterNames[i];
1074: String paramName = nameProposals[0];
1075:
1076: SingleVariableDeclaration param = newParameterDeclaration(
1077: ast, importRewrite, paramName,
1078: parameterTypes[i]);
1079: newParameters.add(param);
1080: newParameterNames.add(paramName);
1081:
1082: SimpleName newSIArgument = ast
1083: .newSimpleName(paramName);
1084: super ConstructorInvocation.arguments().add(
1085: newSIArgument);
1086:
1087: if (fLinkedProposalModel != null) {
1088: LinkedProposalPositionGroup positionGroup = fLinkedProposalModel
1089: .getPositionGroup(KEY_PARAM_NAME_CONST
1090: + String.valueOf(i), true);
1091: positionGroup.addPosition(astRewrite
1092: .track(param.getName()), false);
1093: positionGroup.addPosition(astRewrite
1094: .track(newSIArgument), false);
1095: for (int k = 0; k < nameProposals.length; k++) {
1096: positionGroup.addProposal(nameProposals[k],
1097: null, nameProposals.length - k);
1098: }
1099: }
1100: }
1101: newStatements.add(super ConstructorInvocation);
1102: }
1103: }
1104: // add parameters for all outer variables used
1105: boolean useThisAccess = useThisForFieldAccess();
1106: for (int i = 0; i < bindings.length; i++) {
1107: String baseName = StubUtility
1108: .removePrefixAndSuffixForVariable(project,
1109: bindings[i]);
1110: String[] paramNameProposals = StubUtility
1111: .getVariableNameSuggestions(StubUtility.PARAMETER,
1112: project, baseName, 0, newParameterNames,
1113: true);
1114: String paramName = paramNameProposals[0];
1115:
1116: SingleVariableDeclaration param = newParameterDeclaration(
1117: ast, importRewrite, paramName, bindings[i]
1118: .getType());
1119: newParameters.add(param);
1120: newParameterNames.add(paramName);
1121:
1122: String fieldName = fieldNames[i];
1123: SimpleName fieldNameNode = ast.newSimpleName(fieldName);
1124: SimpleName paramNameNode = ast.newSimpleName(paramName);
1125: newStatements.add(newFieldAssignment(ast, fieldNameNode,
1126: paramNameNode, useThisAccess
1127: || newParameterNames.contains(fieldName)));
1128:
1129: if (fLinkedProposalModel != null) {
1130: LinkedProposalPositionGroup positionGroup = fLinkedProposalModel
1131: .getPositionGroup(KEY_PARAM_NAME_EXT
1132: + String.valueOf(i), true);
1133: positionGroup.addPosition(astRewrite.track(param
1134: .getName()), false);
1135: positionGroup.addPosition(astRewrite
1136: .track(paramNameNode), false);
1137: for (int k = 0; k < paramNameProposals.length; k++) {
1138: positionGroup.addProposal(paramNameProposals[k],
1139: null, paramNameProposals.length - k);
1140: }
1141:
1142: fLinkedProposalModel.getPositionGroup(
1143: KEY_FIELD_NAME_EXT + i, true).addPosition(
1144: astRewrite.track(fieldNameNode), false);
1145: }
1146: }
1147:
1148: addExceptionsToNewConstructor(newConstructor);
1149:
1150: if (doAddComments()) {
1151: try {
1152: String[] allParamNames = (String[]) newParameterNames
1153: .toArray(new String[newParameterNames.size()]);
1154: String string = CodeGeneration.getMethodComment(fCu,
1155: fClassName, fClassName, allParamNames,
1156: new String[0], null, new String[0], null,
1157: StubUtility.getLineDelimiterUsed(fCu));
1158: if (string != null) {
1159: Javadoc javadoc = (Javadoc) astRewrite
1160: .createStringPlaceholder(string,
1161: ASTNode.JAVADOC);
1162: newConstructor.setJavadoc(javadoc);
1163: }
1164: } catch (CoreException exception) {
1165: throw new JavaModelException(exception);
1166: }
1167: }
1168: return newConstructor;
1169: }
1170:
1171: private Statement newFieldAssignment(AST ast,
1172: SimpleName fieldNameNode, Expression initializer,
1173: boolean useThisAccess) {
1174: Assignment assignment = ast.newAssignment();
1175: if (useThisAccess) {
1176: FieldAccess access = ast.newFieldAccess();
1177: access.setExpression(ast.newThisExpression());
1178: access.setName(fieldNameNode);
1179: assignment.setLeftHandSide(access);
1180: } else {
1181: assignment.setLeftHandSide(fieldNameNode);
1182: }
1183: assignment.setOperator(Assignment.Operator.ASSIGN);
1184: assignment.setRightHandSide(initializer);
1185:
1186: return ast.newExpressionStatement(assignment);
1187: }
1188:
1189: // live List of VariableDeclarationFragments
1190: private List getFieldsToInitializeInConstructor() {
1191: List result = new ArrayList(0);
1192: for (Iterator iter = fAnonymousInnerClassNode
1193: .bodyDeclarations().iterator(); iter.hasNext();) {
1194: Object element = iter.next();
1195: if (element instanceof FieldDeclaration) {
1196: List fragments = ((FieldDeclaration) element)
1197: .fragments();
1198: for (Iterator fragmentIter = fragments.iterator(); fragmentIter
1199: .hasNext();) {
1200: VariableDeclarationFragment fragment = (VariableDeclarationFragment) fragmentIter
1201: .next();
1202: if (isToBeInitializerInConstructor(fragment, result))
1203: result.add(fragment);
1204: }
1205: }
1206: }
1207: return result;
1208: }
1209:
1210: private boolean isToBeInitializerInConstructor(
1211: VariableDeclarationFragment fragment,
1212: List fieldsToInitialize) {
1213: return fragment.getInitializer() != null
1214: && areLocalsUsedIn(fragment.getInitializer(),
1215: fieldsToInitialize);
1216: }
1217:
1218: private boolean areLocalsUsedIn(Expression fieldInitializer,
1219: List fieldsToInitialize) {
1220: Set localsUsed = new HashSet(0);
1221: collectRefrencedVariables(fieldInitializer, localsUsed);
1222:
1223: ITypeBinding anonType = fAnonymousInnerClassNode
1224: .resolveBinding();
1225:
1226: for (Iterator iterator = localsUsed.iterator(); iterator
1227: .hasNext();) {
1228: IVariableBinding curr = (IVariableBinding) iterator.next();
1229: if (isBindingToTemp(curr)) { // reference a local from outside
1230: return true;
1231: } else if (curr.isField()
1232: && (curr.getDeclaringClass() == anonType)
1233: && fieldsToInitialize.contains(fCompilationUnitNode
1234: .findDeclaringNode(curr))) {
1235: return true; // references a field that references a local from outside
1236: }
1237: }
1238: return false;
1239: }
1240:
1241: private IMethodBinding getSuperConstructorBinding() {
1242: //workaround for missing java core functionality - finding a
1243: // super constructor for an anonymous class creation
1244: IMethodBinding anonConstr = ((ClassInstanceCreation) fAnonymousInnerClassNode
1245: .getParent()).resolveConstructorBinding();
1246: if (anonConstr == null)
1247: return null;
1248: ITypeBinding super Class = anonConstr.getDeclaringClass()
1249: .getSuperclass();
1250: IMethodBinding[] super Methods = super Class.getDeclaredMethods();
1251: for (int i = 0; i < super Methods.length; i++) {
1252: IMethodBinding super Method = super Methods[i];
1253: if (super Method.isConstructor()
1254: && parameterTypesMatch(super Method, anonConstr))
1255: return super Method;
1256: }
1257: Assert.isTrue(false);//there's no way - it must be there
1258: return null;
1259: }
1260:
1261: private static boolean parameterTypesMatch(IMethodBinding m1,
1262: IMethodBinding m2) {
1263: ITypeBinding[] m1Params = m1.getParameterTypes();
1264: ITypeBinding[] m2Params = m2.getParameterTypes();
1265: if (m1Params.length != m2Params.length)
1266: return false;
1267: for (int i = 0; i < m2Params.length; i++) {
1268: if (!m1Params[i].equals(m2Params[i]))
1269: return false;
1270: }
1271: return true;
1272: }
1273:
1274: private void addExceptionsToNewConstructor(
1275: MethodDeclaration newConstructor) {
1276: IMethodBinding constructorBinding = getSuperConstructorBinding();
1277: if (constructorBinding == null)
1278: return;
1279: ITypeBinding[] exceptions = constructorBinding
1280: .getExceptionTypes();
1281: for (int i = 0; i < exceptions.length; i++) {
1282: Name exceptionTypeName = fAnonymousInnerClassNode.getAST()
1283: .newName(Bindings.getNameComponents(exceptions[i]));
1284: newConstructor.thrownExceptions().add(exceptionTypeName);
1285: }
1286: }
1287:
1288: private SingleVariableDeclaration newParameterDeclaration(AST ast,
1289: ImportRewrite importRewrite, String paramName,
1290: ITypeBinding paramType) {
1291: SingleVariableDeclaration param = ast
1292: .newSingleVariableDeclaration();
1293: param.setExtraDimensions(0);
1294: param.setInitializer(null);
1295: param.setType(importRewrite.addImport(paramType, ast));
1296: param.setName(ast.newSimpleName(paramName));
1297: return param;
1298: }
1299:
1300: private void setSuperType(TypeDeclaration declaration)
1301: throws JavaModelException {
1302: ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation) fAnonymousInnerClassNode
1303: .getParent();
1304: ITypeBinding binding = classInstanceCreation
1305: .resolveTypeBinding();
1306: if (binding == null)
1307: return;
1308: Type newType = (Type) ASTNode.copySubtree(
1309: fAnonymousInnerClassNode.getAST(),
1310: classInstanceCreation.getType());
1311: if (binding.getSuperclass().getQualifiedName().equals(
1312: "java.lang.Object")) { //$NON-NLS-1$
1313: Assert.isTrue(binding.getInterfaces().length <= 1);
1314: if (binding.getInterfaces().length == 0)
1315: return;
1316: declaration.super InterfaceTypes().add(0, newType);
1317: } else {
1318: declaration.setSuperclassType(newType);
1319: }
1320: }
1321:
1322: private ITypeBinding getSuperTypeBinding() {
1323: ITypeBinding types = fAnonymousInnerClassNode.resolveBinding();
1324: ITypeBinding[] interfaces = types.getInterfaces();
1325: if (interfaces.length > 0)
1326: return interfaces[0];
1327: else
1328: return types.getSuperclass();
1329: }
1330:
1331: private int createModifiersForNestedClass() {
1332: int flags = fVisibility;
1333: if (fDeclareFinal)
1334: flags |= Modifier.FINAL;
1335: if (mustInnerClassBeStatic() || fDeclareStatic)
1336: flags |= Modifier.STATIC;
1337: return flags;
1338: }
1339:
1340: public boolean mustInnerClassBeStatic() {
1341: ITypeBinding typeBinding = ((AbstractTypeDeclaration) ASTNodes
1342: .getParent(fAnonymousInnerClassNode,
1343: AbstractTypeDeclaration.class))
1344: .resolveBinding();
1345: ASTNode current = fAnonymousInnerClassNode.getParent();
1346: boolean ans = false;
1347: while (current != null) {
1348: switch (current.getNodeType()) {
1349: case ASTNode.ANONYMOUS_CLASS_DECLARATION: {
1350: AnonymousClassDeclaration enclosingAnonymousClassDeclaration = (AnonymousClassDeclaration) current;
1351: ITypeBinding binding = enclosingAnonymousClassDeclaration
1352: .resolveBinding();
1353: if (binding != null
1354: && Bindings.isSuperType(typeBinding, binding
1355: .getSuperclass())) {
1356: return false;
1357: }
1358: break;
1359: }
1360: case ASTNode.FIELD_DECLARATION: {
1361: FieldDeclaration enclosingFieldDeclaration = (FieldDeclaration) current;
1362: if (Modifier.isStatic(enclosingFieldDeclaration
1363: .getModifiers())) {
1364: ans = true;
1365: }
1366: break;
1367: }
1368: case ASTNode.METHOD_DECLARATION: {
1369: MethodDeclaration enclosingMethodDeclaration = (MethodDeclaration) current;
1370: if (Modifier.isStatic(enclosingMethodDeclaration
1371: .getModifiers())) {
1372: ans = true;
1373: }
1374: break;
1375: }
1376: case ASTNode.TYPE_DECLARATION: {
1377: return ans;
1378: }
1379: }
1380: current = current.getParent();
1381: }
1382: return ans;
1383: }
1384:
1385: public RefactoringStatus initialize(
1386: final RefactoringArguments arguments) {
1387: fSelfInitializing = true;
1388: if (arguments instanceof JavaRefactoringArguments) {
1389: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
1390: final String handle = extended
1391: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1392: if (handle != null) {
1393: final IJavaElement element = JavaRefactoringDescriptorUtil
1394: .handleToElement(extended.getProject(), handle,
1395: false);
1396: if (element == null
1397: || !element.exists()
1398: || element.getElementType() != IJavaElement.COMPILATION_UNIT)
1399: return createInputFatalStatus(element,
1400: IJavaRefactorings.CONVERT_ANONYMOUS);
1401: else {
1402: fCu = (ICompilationUnit) element;
1403: }
1404: } else
1405: return RefactoringStatus
1406: .createFatalErrorStatus(Messages
1407: .format(
1408: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1409: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1410: final String name = extended
1411: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
1412: if (name != null && !"".equals(name)) //$NON-NLS-1$
1413: fClassName = name;
1414: else
1415: return RefactoringStatus
1416: .createFatalErrorStatus(Messages
1417: .format(
1418: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1419: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
1420: final String visibility = extended
1421: .getAttribute(ATTRIBUTE_VISIBILITY);
1422: if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$
1423: int flag = 0;
1424: try {
1425: flag = Integer.parseInt(visibility);
1426: } catch (NumberFormatException exception) {
1427: return RefactoringStatus
1428: .createFatalErrorStatus(Messages
1429: .format(
1430: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1431: ATTRIBUTE_VISIBILITY));
1432: }
1433: fVisibility = flag;
1434: }
1435: final String selection = extended
1436: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
1437: if (selection != null) {
1438: int offset = -1;
1439: int length = -1;
1440: final StringTokenizer tokenizer = new StringTokenizer(
1441: selection);
1442: if (tokenizer.hasMoreTokens())
1443: offset = Integer.valueOf(tokenizer.nextToken())
1444: .intValue();
1445: if (tokenizer.hasMoreTokens())
1446: length = Integer.valueOf(tokenizer.nextToken())
1447: .intValue();
1448: if (offset >= 0 && length >= 0) {
1449: fSelectionStart = offset;
1450: fSelectionLength = length;
1451: } else
1452: return RefactoringStatus
1453: .createFatalErrorStatus(Messages
1454: .format(
1455: RefactoringCoreMessages.InitializableRefactoring_illegal_argument,
1456: new Object[] {
1457: selection,
1458: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION }));
1459: } else
1460: return RefactoringStatus
1461: .createFatalErrorStatus(Messages
1462: .format(
1463: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1464: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
1465: final String declareStatic = extended
1466: .getAttribute(ATTRIBUTE_STATIC);
1467: if (declareStatic != null) {
1468: fDeclareStatic = Boolean.valueOf(declareStatic)
1469: .booleanValue();
1470: } else
1471: return RefactoringStatus
1472: .createFatalErrorStatus(Messages
1473: .format(
1474: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1475: ATTRIBUTE_STATIC));
1476: final String declareFinal = extended
1477: .getAttribute(ATTRIBUTE_FINAL);
1478: if (declareFinal != null) {
1479: fDeclareFinal = Boolean.valueOf(declareStatic)
1480: .booleanValue();
1481: } else
1482: return RefactoringStatus
1483: .createFatalErrorStatus(Messages
1484: .format(
1485: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1486: ATTRIBUTE_FINAL));
1487: } else
1488: return RefactoringStatus
1489: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1490: return new RefactoringStatus();
1491: }
1492: }
|