| /******************************************************************************* |
| * Copyright (c) 2000, 2013 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * N.Metchev@teamphone.com - contributed fixes for |
| * - convert anonymous to nested should sometimes declare class as static [refactoring] |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=43360) |
| * - Convert anonymous to nested: should show error if field form outer anonymous type is references [refactoring] |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=48282) |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.refactoring.code; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.Refactoring; |
| import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.NamingConventions; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.jdt.core.dom.ArrayCreation; |
| import org.eclipse.jdt.core.dom.ArrayInitializer; |
| import org.eclipse.jdt.core.dom.ArrayType; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.FieldAccess; |
| import org.eclipse.jdt.core.dom.FieldDeclaration; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| import org.eclipse.jdt.core.dom.Initializer; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.NodeFinder; |
| import org.eclipse.jdt.core.dom.ParameterizedType; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.Statement; |
| import org.eclipse.jdt.core.dom.SuperConstructorInvocation; |
| import org.eclipse.jdt.core.dom.SuperFieldAccess; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.TypeParameter; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.refactoring.CompilationUnitChange; |
| import org.eclipse.jdt.core.refactoring.IJavaRefactorings; |
| import org.eclipse.jdt.core.refactoring.descriptors.ConvertAnonymousDescriptor; |
| import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; |
| |
| import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; |
| import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; |
| import org.eclipse.jdt.internal.corext.fix.LinkedProposalModel; |
| import org.eclipse.jdt.internal.corext.fix.LinkedProposalPositionGroup; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.CodeGeneration; |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| |
| import org.eclipse.jdt.internal.ui.text.correction.ModifierCorrectionSubProcessor; |
| import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; |
| import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider; |
| |
| public class ConvertAnonymousToNestedRefactoring extends Refactoring { |
| |
| private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_FINAL= "final"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_STATIC= "static"; //$NON-NLS-1$ |
| |
| private static final String KEY_TYPE_NAME= "type_name"; //$NON-NLS-1$ |
| private static final String KEY_PARAM_NAME_EXT= "param_name_ext"; //$NON-NLS-1$ |
| private static final String KEY_PARAM_NAME_CONST= "param_name_const"; //$NON-NLS-1$ |
| private static final String KEY_FIELD_NAME_EXT= "field_name_ext"; //$NON-NLS-1$ |
| |
| public static class TypeVariableFinder extends ASTVisitor { |
| |
| private final Map<String, ITypeBinding> fBindings= new HashMap<String, ITypeBinding>(); |
| private final List<ITypeBinding> fFound= new ArrayList<ITypeBinding>(); |
| |
| @Override |
| public final boolean visit(final SimpleName node) { |
| Assert.isNotNull(node); |
| final ITypeBinding binding= node.resolveTypeBinding(); |
| if (binding != null && binding.isTypeVariable() && !fBindings.containsKey(binding.getKey())) { |
| fBindings.put(binding.getKey(), binding); |
| fFound.add(binding); |
| } |
| return true; |
| } |
| |
| public final ITypeBinding[] getResult() { |
| final ITypeBinding[] result= new ITypeBinding[fFound.size()]; |
| fFound.toArray(result); |
| return result; |
| } |
| } |
| |
| private int fSelectionStart; |
| private int fSelectionLength; |
| private ICompilationUnit fCu; |
| |
| private int fVisibility; /* see Modifier */ |
| private boolean fDeclareFinal= true; |
| private boolean fDeclareStatic; |
| private String fClassName= ""; //$NON-NLS-1$ |
| |
| private CompilationUnit fCompilationUnitNode; |
| private AnonymousClassDeclaration fAnonymousInnerClassNode; |
| private Set<String> fClassNamesUsed; |
| private boolean fSelfInitializing= false; |
| |
| private LinkedProposalModel fLinkedProposalModel; |
| |
| /** |
| * Creates a new convert anonymous to nested refactoring. |
| * |
| * @param unit the compilation unit, or <code>null</code> if invoked by scripting |
| * @param selectionStart start |
| * @param selectionLength length |
| */ |
| public ConvertAnonymousToNestedRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength) { |
| Assert.isTrue(selectionStart >= 0); |
| Assert.isTrue(selectionLength >= 0); |
| Assert.isTrue(unit == null || unit.exists()); |
| fSelectionStart= selectionStart; |
| fSelectionLength= selectionLength; |
| fCu= unit; |
| fAnonymousInnerClassNode= null; |
| fCompilationUnitNode= null; |
| } |
| |
| public ConvertAnonymousToNestedRefactoring(AnonymousClassDeclaration declaration) { |
| Assert.isTrue(declaration != null); |
| |
| ASTNode astRoot= declaration.getRoot(); |
| Assert.isTrue(astRoot instanceof CompilationUnit); |
| fCompilationUnitNode= (CompilationUnit) astRoot; |
| |
| IJavaElement javaElement= fCompilationUnitNode.getJavaElement(); |
| Assert.isTrue(javaElement instanceof ICompilationUnit); |
| |
| fCu= (ICompilationUnit) javaElement; |
| fSelectionStart= declaration.getStartPosition(); |
| fSelectionLength= declaration.getLength(); |
| } |
| |
| public ConvertAnonymousToNestedRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) { |
| this(null, 0, 0); |
| RefactoringStatus initializeStatus= initialize(arguments); |
| status.merge(initializeStatus); |
| } |
| |
| public void setLinkedProposalModel(LinkedProposalModel linkedProposalModel) { |
| fLinkedProposalModel= linkedProposalModel; |
| } |
| |
| public int[] getAvailableVisibilities() { |
| if (isLocalInnerType()) { |
| return new int[] { Modifier.NONE }; |
| } else { |
| return new int[] { Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE }; |
| } |
| } |
| |
| public boolean isLocalInnerType() { |
| return ASTNodes.getParent(ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class), ASTNode.ANONYMOUS_CLASS_DECLARATION) != null; |
| } |
| |
| public int getVisibility() { |
| return fVisibility; |
| } |
| |
| public void setVisibility(int visibility) { |
| Assert.isTrue(visibility == Modifier.PRIVATE || visibility == Modifier.NONE || visibility == Modifier.PROTECTED || visibility == Modifier.PUBLIC); |
| fVisibility= visibility; |
| } |
| |
| public void setClassName(String className) { |
| Assert.isNotNull(className); |
| fClassName= className; |
| } |
| |
| public boolean canEnableSettingFinal() { |
| return true; |
| } |
| |
| public boolean getDeclareFinal() { |
| return fDeclareFinal; |
| } |
| |
| public boolean getDeclareStatic() { |
| return fDeclareStatic; |
| } |
| |
| public void setDeclareFinal(boolean declareFinal) { |
| fDeclareFinal= declareFinal; |
| } |
| |
| public void setDeclareStatic(boolean declareStatic) { |
| fDeclareStatic= declareStatic; |
| } |
| |
| @Override |
| public String getName() { |
| return RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name; |
| } |
| |
| private boolean useThisForFieldAccess() { |
| return StubUtility.useThisForFieldAccess(fCu.getJavaProject()); |
| } |
| |
| private boolean doAddComments() { |
| return StubUtility.doAddComments(fCu.getJavaProject()); |
| } |
| |
| @Override |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { |
| RefactoringStatus result= Checks.validateModifiesFiles( |
| ResourceUtil.getFiles(new ICompilationUnit[]{fCu}), |
| getValidationContext()); |
| if (result.hasFatalError()) |
| return result; |
| |
| initAST(pm); |
| |
| if (fAnonymousInnerClassNode == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_place_caret); |
| if (!fSelfInitializing) |
| initializeDefaults(); |
| if (getSuperConstructorBinding() == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors); |
| if (getSuperTypeBinding().isLocal()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_extends_local_class); |
| return new RefactoringStatus(); |
| } |
| |
| private void initializeDefaults() { |
| fVisibility= isLocalInnerType() ? Modifier.NONE : Modifier.PRIVATE; |
| fDeclareStatic = mustInnerClassBeStatic(); |
| } |
| |
| private void initAST(IProgressMonitor pm) { |
| if (fCompilationUnitNode == null) { |
| fCompilationUnitNode= RefactoringASTParser.parseWithASTProvider(fCu, true, pm); |
| } |
| if (fAnonymousInnerClassNode == null) { |
| fAnonymousInnerClassNode= getAnonymousInnerClass(NodeFinder.perform(fCompilationUnitNode, fSelectionStart, fSelectionLength)); |
| } |
| if (fAnonymousInnerClassNode != null) { |
| final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class); |
| if (declaration instanceof TypeDeclaration) { |
| final AbstractTypeDeclaration[] nested= ((TypeDeclaration) declaration).getTypes(); |
| fClassNamesUsed= new HashSet<String>(nested.length); |
| for (int index= 0; index < nested.length; index++) |
| fClassNamesUsed.add(nested[index].getName().getIdentifier()); |
| } else |
| fClassNamesUsed= Collections.emptySet(); |
| } |
| } |
| |
| private static AnonymousClassDeclaration getAnonymousInnerClass(ASTNode node) { |
| if (node == null) |
| return null; |
| if (node instanceof AnonymousClassDeclaration) |
| return (AnonymousClassDeclaration)node; |
| if (node instanceof ClassInstanceCreation) { |
| AnonymousClassDeclaration anon= ((ClassInstanceCreation)node).getAnonymousClassDeclaration(); |
| if (anon != null) |
| return anon; |
| } |
| node= ASTNodes.getNormalizedNode(node); |
| if (node.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) { |
| AnonymousClassDeclaration anon= ((ClassInstanceCreation)node.getParent()).getAnonymousClassDeclaration(); |
| if (anon != null) |
| return anon; |
| } |
| return (AnonymousClassDeclaration)ASTNodes.getParent(node, AnonymousClassDeclaration.class); |
| } |
| |
| public RefactoringStatus validateInput() { |
| RefactoringStatus result= Checks.checkTypeName(fClassName, fCu); |
| if (result.hasFatalError()) |
| return result; |
| |
| if (fClassNamesUsed.contains(fClassName)) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_type_exists); |
| IMethodBinding superConstructorBinding = getSuperConstructorBinding(); |
| if (superConstructorBinding == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_compile_errors); |
| if (fClassName.equals(superConstructorBinding.getDeclaringClass().getName())) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_another_name); |
| if (classNameHidesEnclosingType()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name_hides); |
| return result; |
| } |
| |
| private boolean accessesAnonymousFields() { |
| List<IVariableBinding> anonymousInnerFieldTypes = getAllEnclosingAnonymousTypesField(); |
| List<IBinding> accessedField = getAllAccessedFields(); |
| final Iterator<IVariableBinding> it = anonymousInnerFieldTypes.iterator(); |
| while(it.hasNext()) { |
| final IVariableBinding variableBinding = it.next(); |
| final Iterator<IBinding> it2 = accessedField.iterator(); |
| while (it2.hasNext()) { |
| IVariableBinding variableBinding2 = (IVariableBinding) it2.next(); |
| if(Bindings.equals(variableBinding, variableBinding2)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private List<IBinding> getAllAccessedFields() { |
| final List<IBinding> accessedFields= new ArrayList<IBinding>(); |
| |
| ASTVisitor visitor= new ASTVisitor() { |
| |
| @Override |
| public boolean visit(FieldAccess node) { |
| final IVariableBinding binding= node.resolveFieldBinding(); |
| if (binding != null && !binding.isEnumConstant()) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| |
| @Override |
| public boolean visit(QualifiedName node) { |
| final IBinding binding= node.resolveBinding(); |
| if (binding != null && binding instanceof IVariableBinding) { |
| IVariableBinding variable= (IVariableBinding) binding; |
| if (!variable.isEnumConstant() && variable.isField()) |
| accessedFields.add(binding); |
| } |
| return super.visit(node); |
| } |
| |
| @Override |
| public boolean visit(SimpleName node) { |
| final IBinding binding= node.resolveBinding(); |
| if (binding != null && binding instanceof IVariableBinding) { |
| IVariableBinding variable= (IVariableBinding) binding; |
| if (!variable.isEnumConstant() && variable.isField()) |
| accessedFields.add(binding); |
| } |
| return super.visit(node); |
| } |
| |
| @Override |
| public boolean visit(SuperFieldAccess node) { |
| final IVariableBinding binding= node.resolveFieldBinding(); |
| if (binding != null && !binding.isEnumConstant()) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| }; |
| fAnonymousInnerClassNode.accept(visitor); |
| |
| return accessedFields; |
| } |
| |
| private List<IVariableBinding> getAllEnclosingAnonymousTypesField() { |
| final List<IVariableBinding> ans= new ArrayList<IVariableBinding>(); |
| final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class); |
| AnonymousClassDeclaration anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, ASTNode.ANONYMOUS_CLASS_DECLARATION); |
| while (anonymous != null) { |
| if (ASTNodes.isParent(anonymous, declaration)) { |
| ITypeBinding binding= anonymous.resolveBinding(); |
| if (binding != null) { |
| ans.addAll(Arrays.asList(binding.getDeclaredFields())); |
| } |
| } else { |
| break; |
| } |
| anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(anonymous, ASTNode.ANONYMOUS_CLASS_DECLARATION); |
| } |
| return ans; |
| } |
| |
| private boolean classNameHidesEnclosingType() { |
| ITypeBinding type= ((AbstractTypeDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class)).resolveBinding(); |
| while (type != null) { |
| if (fClassName.equals(type.getName())) |
| return true; |
| type= type.getDeclaringClass(); |
| } |
| return false; |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkInput(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { |
| try { |
| RefactoringStatus status= validateInput(); |
| if (accessesAnonymousFields()) |
| status.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_anonymous_field_access)); |
| return status; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| public CompilationUnitChange createCompilationUnitChange(IProgressMonitor pm) throws CoreException { |
| final CompilationUnitRewrite rewrite= new CompilationUnitRewrite(fCu, fCompilationUnitNode); |
| final ITypeBinding[] typeParameters= getTypeParameters(); |
| addNestedClass(rewrite, typeParameters); |
| modifyConstructorCall(rewrite, typeParameters); |
| return rewrite.createChange(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_name, false, pm); |
| } |
| |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| public Change createChange(IProgressMonitor pm) throws CoreException { |
| final CompilationUnitChange result= createCompilationUnitChange(pm); |
| result.setDescriptor(createRefactoringDescriptor()); |
| return result; |
| } |
| |
| private ITypeBinding[] getTypeParameters() { |
| final List<ITypeBinding> parameters= new ArrayList<ITypeBinding>(4); |
| final ClassInstanceCreation creation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent(); |
| if (fDeclareStatic) { |
| final TypeVariableFinder finder= new TypeVariableFinder(); |
| creation.accept(finder); |
| return finder.getResult(); |
| } else { |
| final MethodDeclaration declaration= getEnclosingMethodDeclaration(creation); |
| if (declaration != null) { |
| ITypeBinding binding= null; |
| TypeParameter parameter= null; |
| for (final Iterator<TypeParameter> iterator= declaration.typeParameters().iterator(); iterator.hasNext();) { |
| parameter= iterator.next(); |
| binding= parameter.resolveBinding(); |
| if (binding != null) |
| parameters.add(binding); |
| } |
| } |
| } |
| final TypeVariableFinder finder= new TypeVariableFinder(); |
| creation.accept(finder); |
| final ITypeBinding[] variables= finder.getResult(); |
| final List<ITypeBinding> remove= new ArrayList<ITypeBinding>(4); |
| boolean match= false; |
| ITypeBinding binding= null; |
| ITypeBinding variable= null; |
| for (final Iterator<ITypeBinding> iterator= parameters.iterator(); iterator.hasNext();) { |
| match= false; |
| binding= iterator.next(); |
| for (int index= 0; index < variables.length; index++) { |
| variable= variables[index]; |
| if (variable.equals(binding)) |
| match= true; |
| } |
| if (!match) |
| remove.add(binding); |
| } |
| parameters.removeAll(remove); |
| final ITypeBinding[] result= new ITypeBinding[parameters.size()]; |
| parameters.toArray(result); |
| return result; |
| } |
| |
| private MethodDeclaration getEnclosingMethodDeclaration(ASTNode node) { |
| ASTNode parent= node.getParent(); |
| if (parent != null) { |
| if (parent instanceof AbstractTypeDeclaration) |
| return null; |
| else if (parent instanceof MethodDeclaration) |
| return (MethodDeclaration) parent; |
| return getEnclosingMethodDeclaration(parent); |
| } |
| return null; |
| } |
| |
| private RefactoringChangeDescriptor createRefactoringDescriptor() { |
| final ITypeBinding binding= fAnonymousInnerClassNode.resolveBinding(); |
| final String[] labels= new String[] { BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(binding.getDeclaringMethod(), JavaElementLabels.ALL_FULLY_QUALIFIED)}; |
| final Map<String, String> arguments= new HashMap<String, String>(); |
| final String projectName= fCu.getJavaProject().getElementName(); |
| final int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| final String description= RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description_short; |
| final String header= Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_descriptor_description, labels); |
| final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(projectName, this, header); |
| comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED))); |
| comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_class_name_pattern, BasicElementLabels.getJavaElementName(fClassName))); |
| String visibility= JdtFlags.getVisibilityString(fVisibility); |
| if (visibility.length() == 0) |
| visibility= RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_default_visibility; |
| comment.addSetting(Messages.format(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_visibility_pattern, visibility)); |
| if (fDeclareFinal && fDeclareStatic) |
| comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final_static); |
| else if (fDeclareFinal) |
| comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_final); |
| else if (fDeclareStatic) |
| comment.addSetting(RefactoringCoreMessages.ConvertAnonymousToNestedRefactoring_declare_static); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(projectName, fCu)); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fClassName); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + ' ' + new Integer(fSelectionLength).toString()); |
| arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal).toString()); |
| arguments.put(ATTRIBUTE_STATIC, Boolean.valueOf(fDeclareStatic).toString()); |
| arguments.put(ATTRIBUTE_VISIBILITY, new Integer(fVisibility).toString()); |
| |
| ConvertAnonymousDescriptor descriptor= RefactoringSignatureDescriptorFactory.createConvertAnonymousDescriptor(projectName, description, comment.asString(), arguments, flags); |
| return new RefactoringChangeDescriptor(descriptor); |
| } |
| |
| private void modifyConstructorCall(CompilationUnitRewrite rewrite, ITypeBinding[] parameters) { |
| rewrite.getASTRewrite().replace(fAnonymousInnerClassNode.getParent(), createNewClassInstanceCreation(rewrite, parameters), null); |
| } |
| |
| private ASTNode createNewClassInstanceCreation(CompilationUnitRewrite rewrite, ITypeBinding[] parameters) { |
| AST ast= fAnonymousInnerClassNode.getAST(); |
| ClassInstanceCreation newClassCreation= ast.newClassInstanceCreation(); |
| newClassCreation.setAnonymousClassDeclaration(null); |
| Type type= null; |
| SimpleName newNameNode= ast.newSimpleName(fClassName); |
| if (parameters.length > 0) { |
| final ParameterizedType parameterized= ast.newParameterizedType(ast.newSimpleType(newNameNode)); |
| for (int index= 0; index < parameters.length; index++) |
| parameterized.typeArguments().add(ast.newSimpleType(ast.newSimpleName(parameters[index].getName()))); |
| type= parameterized; |
| } else |
| type= ast.newSimpleType(newNameNode); |
| newClassCreation.setType(type); |
| copyArguments(rewrite, newClassCreation); |
| addArgumentsForLocalsUsedInInnerClass(newClassCreation); |
| |
| addLinkedPosition(KEY_TYPE_NAME, newNameNode, rewrite.getASTRewrite(), true); |
| |
| return newClassCreation; |
| } |
| |
| private void addArgumentsForLocalsUsedInInnerClass(ClassInstanceCreation newClassCreation) { |
| IVariableBinding[] usedLocals= getUsedLocalVariables(); |
| for (int i= 0; i < usedLocals.length; i++) { |
| final AST ast= fAnonymousInnerClassNode.getAST(); |
| final IVariableBinding binding= usedLocals[i]; |
| Name name= null; |
| if (binding.isEnumConstant()) |
| name= ast.newQualifiedName(ast.newSimpleName(binding.getDeclaringClass().getName()), ast.newSimpleName(binding.getName())); |
| else |
| name= ast.newSimpleName(binding.getName()); |
| newClassCreation.arguments().add(name); |
| } |
| } |
| |
| private void copyArguments(CompilationUnitRewrite rewrite, ClassInstanceCreation newClassCreation) { |
| Iterator<Expression> iter= ((ClassInstanceCreation) fAnonymousInnerClassNode.getParent()).arguments().iterator(); |
| if (!iter.hasNext()) |
| return; |
| |
| IMethodBinding superConstructorBinding= getSuperConstructorBinding(); |
| ITypeBinding[] parameterTypes= superConstructorBinding.getParameterTypes(); |
| |
| List<Expression> arguments= newClassCreation.arguments(); |
| ASTRewrite astRewrite= rewrite.getASTRewrite(); |
| int last= parameterTypes.length - 1; |
| |
| for (int i= 0; i < last; i++) { |
| arguments.add((Expression) astRewrite.createCopyTarget(iter.next())); |
| } |
| if (superConstructorBinding.isVarargs()) { |
| AST ast= astRewrite.getAST(); |
| ArrayCreation arrayCreation= ast.newArrayCreation(); |
| arrayCreation.setType((ArrayType) rewrite.getImportRewrite().addImport(parameterTypes[last], ast)); |
| ArrayInitializer initializer= ast.newArrayInitializer(); |
| arrayCreation.setInitializer(initializer); |
| arguments.add(arrayCreation); |
| arguments= initializer.expressions(); |
| } |
| while (iter.hasNext()) { |
| arguments.add((Expression) astRewrite.createCopyTarget(iter.next())); |
| } |
| } |
| |
| private void addNestedClass(CompilationUnitRewrite rewrite, ITypeBinding[] typeParameters) throws CoreException { |
| final AbstractTypeDeclaration declarations= (AbstractTypeDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class); |
| int index= findIndexOfFistNestedClass(declarations.bodyDeclarations()); |
| if (index == -1) |
| index= 0; |
| rewrite.getASTRewrite().getListRewrite(declarations, declarations.getBodyDeclarationsProperty()).insertAt(createNewNestedClass(rewrite, typeParameters), index, null); |
| } |
| |
| private static int findIndexOfFistNestedClass(List<BodyDeclaration> list) { |
| for (int i= 0, n= list.size(); i < n; i++) { |
| BodyDeclaration each= list.get(i); |
| if (isNestedType(each)) |
| return i; |
| } |
| return -1; |
| } |
| |
| private static boolean isNestedType(BodyDeclaration each) { |
| if (!(each instanceof AbstractTypeDeclaration)) |
| return false; |
| return (each.getParent() instanceof AbstractTypeDeclaration); |
| } |
| |
| private AbstractTypeDeclaration createNewNestedClass(CompilationUnitRewrite rewrite, ITypeBinding[] typeParameters) throws CoreException { |
| final AST ast= fAnonymousInnerClassNode.getAST(); |
| |
| final TypeDeclaration newDeclaration= ast.newTypeDeclaration(); |
| newDeclaration.setInterface(false); |
| newDeclaration.setJavadoc(null); |
| newDeclaration.modifiers().addAll(ASTNodeFactory.newModifiers(ast, createModifiersForNestedClass())); |
| newDeclaration.setName(ast.newSimpleName(fClassName)); |
| |
| TypeParameter parameter= null; |
| for (int index= 0; index < typeParameters.length; index++) { |
| parameter= ast.newTypeParameter(); |
| parameter.setName(ast.newSimpleName(typeParameters[index].getName())); |
| newDeclaration.typeParameters().add(parameter); |
| } |
| setSuperType(newDeclaration); |
| |
| IJavaProject project= fCu.getJavaProject(); |
| |
| IVariableBinding[] bindings= getUsedLocalVariables(); |
| ArrayList<String> fieldNames= new ArrayList<String>(); |
| for (int i= 0; i < bindings.length; i++) { |
| String name= StubUtility.getBaseName(bindings[i], project); |
| String[] fieldNameProposals= StubUtility.getVariableNameSuggestions(NamingConventions.VK_INSTANCE_FIELD, project, name, 0, fieldNames, true); |
| fieldNames.add(fieldNameProposals[0]); |
| |
| |
| if (fLinkedProposalModel != null) { |
| LinkedProposalPositionGroup positionGroup= fLinkedProposalModel.getPositionGroup(KEY_FIELD_NAME_EXT + i, true); |
| for (int k= 0; k < fieldNameProposals.length; k++) { |
| positionGroup.addProposal(fieldNameProposals[k], null, fieldNameProposals.length - k); |
| } |
| } |
| } |
| String[] allFieldNames= fieldNames.toArray(new String[fieldNames.size()]); |
| |
| List<BodyDeclaration> newBodyDeclarations= newDeclaration.bodyDeclarations(); |
| |
| createFieldsForAccessedLocals(rewrite, bindings, allFieldNames, newBodyDeclarations); |
| |
| MethodDeclaration newConstructorDecl= createNewConstructor(rewrite, bindings, allFieldNames); |
| if (newConstructorDecl != null) { |
| newBodyDeclarations.add(newConstructorDecl); |
| } |
| |
| updateAndMoveBodyDeclarations(rewrite, bindings, allFieldNames, newBodyDeclarations, newConstructorDecl); |
| |
| if (doAddComments()) { |
| String[] parameterNames= new String[typeParameters.length]; |
| for (int index= 0; index < parameterNames.length; index++) { |
| parameterNames[index]= typeParameters[index].getName(); |
| } |
| String string= CodeGeneration.getTypeComment(rewrite.getCu(), fClassName, parameterNames, StubUtility.getLineDelimiterUsed(fCu)); |
| if (string != null) { |
| Javadoc javadoc= (Javadoc) rewrite.getASTRewrite().createStringPlaceholder(string, ASTNode.JAVADOC); |
| newDeclaration.setJavadoc(javadoc); |
| } |
| } |
| if (fLinkedProposalModel != null) { |
| addLinkedPosition(KEY_TYPE_NAME, newDeclaration.getName(), rewrite.getASTRewrite(), false); |
| ModifierCorrectionSubProcessor.installLinkedVisibilityProposals(fLinkedProposalModel, rewrite.getASTRewrite(), newDeclaration.modifiers(), false); |
| } |
| |
| return newDeclaration; |
| } |
| |
| private void updateAndMoveBodyDeclarations(CompilationUnitRewrite rewriter, IVariableBinding[] bindings, String[] fieldNames, List<BodyDeclaration> newBodyDeclarations, MethodDeclaration newConstructorDecl) { |
| final ASTRewrite astRewrite= rewriter.getASTRewrite(); |
| final AST ast= astRewrite.getAST(); |
| |
| final boolean useThisAccess= useThisForFieldAccess(); |
| |
| int fieldInsertIndex= newConstructorDecl != null ? newBodyDeclarations.lastIndexOf(newConstructorDecl) : newBodyDeclarations.size(); |
| |
| for (Iterator<BodyDeclaration> iterator= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iterator.hasNext();) { |
| BodyDeclaration body= iterator.next(); |
| |
| for (int i= 0; i < bindings.length; i++) { |
| SimpleName[] names= LinkedNodeFinder.findByBinding(body, bindings[i]); |
| String fieldName= fieldNames[i]; |
| for (int k= 0; k < names.length; k++) { |
| SimpleName newNode= ast.newSimpleName(fieldName); |
| if (useThisAccess) { |
| FieldAccess access= ast.newFieldAccess(); |
| access.setExpression(ast.newThisExpression()); |
| access.setName(newNode); |
| astRewrite.replace(names[k], access, null); |
| } else { |
| astRewrite.replace(names[k], newNode, null); |
| } |
| addLinkedPosition(KEY_FIELD_NAME_EXT + i, newNode, astRewrite, false); |
| } |
| } |
| if (body instanceof Initializer || body instanceof FieldDeclaration) { |
| newBodyDeclarations.add(fieldInsertIndex++, (BodyDeclaration) astRewrite.createMoveTarget(body)); |
| } else { |
| newBodyDeclarations.add((BodyDeclaration) astRewrite.createMoveTarget(body)); |
| } |
| } |
| |
| if (newConstructorDecl != null) { |
| // move initialization of existing fields to constructor if an outer is referenced |
| List<Statement> bodyStatements= newConstructorDecl.getBody().statements(); |
| |
| List<VariableDeclarationFragment> fieldsToInitializeInConstructor= getFieldsToInitializeInConstructor(); |
| for (Iterator<VariableDeclarationFragment> iter= fieldsToInitializeInConstructor.iterator(); iter.hasNext();) { |
| VariableDeclarationFragment fragment= iter.next(); |
| Expression initializer= fragment.getInitializer(); |
| Expression replacement= (Expression) astRewrite.get(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY); |
| if (replacement == initializer) { |
| replacement= (Expression) astRewrite.createMoveTarget(initializer); |
| } |
| astRewrite.remove(initializer, null); |
| SimpleName fieldNameNode= ast.newSimpleName(fragment.getName().getIdentifier()); |
| bodyStatements.add(newFieldAssignment(ast, fieldNameNode, replacement, useThisAccess)); |
| } |
| } |
| } |
| |
| private void createFieldsForAccessedLocals(CompilationUnitRewrite rewrite, IVariableBinding[] varBindings, String[] fieldNames, List<BodyDeclaration> newBodyDeclarations) throws CoreException { |
| final ImportRewrite importRewrite= rewrite.getImportRewrite(); |
| final ASTRewrite astRewrite= rewrite.getASTRewrite(); |
| final AST ast= astRewrite.getAST(); |
| |
| for (int i= 0; i < varBindings.length; i++) { |
| VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment(); |
| fragment.setExtraDimensions(0); |
| fragment.setInitializer(null); |
| fragment.setName(ast.newSimpleName(fieldNames[i])); |
| FieldDeclaration field= ast.newFieldDeclaration(fragment); |
| ITypeBinding varType= varBindings[i].getType(); |
| field.setType(importRewrite.addImport(varType, ast)); |
| field.modifiers().addAll(ASTNodeFactory.newModifiers(ast, Modifier.PRIVATE | Modifier.FINAL)); |
| if (doAddComments()) { |
| String string= CodeGeneration.getFieldComment(rewrite.getCu(), varType.getName(), fieldNames[i], StubUtility.getLineDelimiterUsed(fCu)); |
| if (string != null) { |
| Javadoc javadoc= (Javadoc) astRewrite.createStringPlaceholder(string, ASTNode.JAVADOC); |
| field.setJavadoc(javadoc); |
| } |
| } |
| |
| newBodyDeclarations.add(field); |
| |
| addLinkedPosition(KEY_FIELD_NAME_EXT + i, fragment.getName(), astRewrite, false); |
| } |
| } |
| |
| private void addLinkedPosition(String key, ASTNode nodeToTrack, ASTRewrite rewrite, boolean isFirst) { |
| if (fLinkedProposalModel != null) { |
| fLinkedProposalModel.getPositionGroup(key, true).addPosition(rewrite.track(nodeToTrack), isFirst); |
| } |
| } |
| |
| |
| private IVariableBinding[] getUsedLocalVariables() { |
| final Set<IBinding> result= new HashSet<IBinding>(0); |
| collectRefrencedVariables(fAnonymousInnerClassNode, result); |
| ArrayList<IVariableBinding> usedLocals= new ArrayList<IVariableBinding>(); |
| for (Iterator<IBinding> iterator= result.iterator(); iterator.hasNext();) { |
| IVariableBinding next= (IVariableBinding) iterator.next(); |
| if (isBindingToTemp(next)) { |
| usedLocals.add(next); |
| } |
| } |
| return usedLocals.toArray(new IVariableBinding[usedLocals.size()]); |
| } |
| |
| private void collectRefrencedVariables(ASTNode root, final Set<IBinding> result) { |
| root.accept(new ASTVisitor() { |
| @Override |
| public boolean visit(SimpleName node) { |
| IBinding binding= node.resolveBinding(); |
| if (binding instanceof IVariableBinding) |
| result.add(binding); |
| return true; |
| } |
| }); |
| } |
| |
| private boolean isBindingToTemp(IVariableBinding variable) { |
| if (variable.isField()) |
| return false; |
| if (!Modifier.isFinal(variable.getModifiers())) |
| return false; |
| ASTNode declaringNode= fCompilationUnitNode.findDeclaringNode(variable); |
| if (declaringNode == null) |
| return false; |
| if (ASTNodes.isParent(declaringNode, fAnonymousInnerClassNode)) |
| return false; |
| return true; |
| } |
| |
| private MethodDeclaration createNewConstructor(CompilationUnitRewrite rewrite, IVariableBinding[] bindings, String[] fieldNames) throws JavaModelException { |
| ClassInstanceCreation instanceCreation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent(); |
| |
| if (instanceCreation.arguments().isEmpty() && bindings.length == 0) |
| return null; |
| |
| IJavaProject project= fCu.getJavaProject(); |
| AST ast= rewrite.getAST(); |
| ImportRewrite importRewrite= rewrite.getImportRewrite(); |
| ASTRewrite astRewrite= rewrite.getASTRewrite(); |
| |
| MethodDeclaration newConstructor= ast.newMethodDeclaration(); |
| newConstructor.setConstructor(true); |
| newConstructor.setExtraDimensions(0); |
| newConstructor.setJavadoc(null); |
| newConstructor.modifiers().addAll(ASTNodeFactory.newModifiers(ast, fVisibility)); |
| newConstructor.setName(ast.newSimpleName(fClassName)); |
| addLinkedPosition(KEY_TYPE_NAME, newConstructor.getName(), astRewrite, false); |
| |
| newConstructor.setBody(ast.newBlock()); |
| |
| List<Statement> newStatements= newConstructor.getBody().statements(); |
| |
| List<SingleVariableDeclaration> newParameters= newConstructor.parameters(); |
| List<String> newParameterNames= new ArrayList<String>(); |
| |
| // add parameters for elements passed with the instance creation |
| if (!instanceCreation.arguments().isEmpty()) { |
| IMethodBinding constructorBinding= getSuperConstructorBinding(); |
| if (constructorBinding != null) { |
| SuperConstructorInvocation superConstructorInvocation= ast.newSuperConstructorInvocation(); |
| ITypeBinding[] parameterTypes= constructorBinding.getParameterTypes(); |
| String[][] parameterNames= StubUtility.suggestArgumentNamesWithProposals(project, constructorBinding); |
| for (int i= 0; i < parameterNames.length; i++) { |
| String[] nameProposals= parameterNames[i]; |
| String paramName= nameProposals[0]; |
| |
| SingleVariableDeclaration param= newParameterDeclaration(ast, importRewrite, paramName, parameterTypes[i]); |
| newParameters.add(param); |
| newParameterNames.add(paramName); |
| |
| SimpleName newSIArgument= ast.newSimpleName(paramName); |
| superConstructorInvocation.arguments().add(newSIArgument); |
| |
| if (fLinkedProposalModel != null) { |
| LinkedProposalPositionGroup positionGroup= fLinkedProposalModel.getPositionGroup(KEY_PARAM_NAME_CONST+ String.valueOf(i), true); |
| positionGroup.addPosition(astRewrite.track(param.getName()), false); |
| positionGroup.addPosition(astRewrite.track(newSIArgument), false); |
| for (int k= 0; k < nameProposals.length; k++) { |
| positionGroup.addProposal(nameProposals[k], null, nameProposals.length - k); |
| } |
| } |
| } |
| newStatements.add(superConstructorInvocation); |
| } |
| } |
| // add parameters for all outer variables used |
| boolean useThisAccess= useThisForFieldAccess(); |
| for (int i= 0; i < bindings.length; i++) { |
| String baseName= StubUtility.getBaseName(bindings[i], project); |
| String[] paramNameProposals= StubUtility.getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, baseName, 0, newParameterNames, true); |
| String paramName= paramNameProposals[0]; |
| |
| SingleVariableDeclaration param= newParameterDeclaration(ast, importRewrite, paramName, bindings[i].getType()); |
| newParameters.add(param); |
| newParameterNames.add(paramName); |
| |
| String fieldName= fieldNames[i]; |
| SimpleName fieldNameNode= ast.newSimpleName(fieldName); |
| SimpleName paramNameNode= ast.newSimpleName(paramName); |
| newStatements.add(newFieldAssignment(ast, fieldNameNode, paramNameNode, useThisAccess || newParameterNames.contains(fieldName))); |
| |
| if (fLinkedProposalModel != null) { |
| LinkedProposalPositionGroup positionGroup= fLinkedProposalModel.getPositionGroup(KEY_PARAM_NAME_EXT+ String.valueOf(i), true); |
| positionGroup.addPosition(astRewrite.track(param.getName()), false); |
| positionGroup.addPosition(astRewrite.track(paramNameNode), false); |
| for (int k= 0; k < paramNameProposals.length; k++) { |
| positionGroup.addProposal(paramNameProposals[k], null, paramNameProposals.length - k); |
| } |
| |
| fLinkedProposalModel.getPositionGroup(KEY_FIELD_NAME_EXT + i, true).addPosition(astRewrite.track(fieldNameNode), false); |
| } |
| } |
| |
| addExceptionsToNewConstructor(newConstructor); |
| |
| if (doAddComments()) { |
| try { |
| String[] allParamNames= newParameterNames.toArray(new String[newParameterNames.size()]); |
| String string= CodeGeneration.getMethodComment(fCu, fClassName, fClassName, allParamNames, new String[0], null, new String[0], null, StubUtility.getLineDelimiterUsed(fCu)); |
| if (string != null) { |
| Javadoc javadoc= (Javadoc) astRewrite.createStringPlaceholder(string, ASTNode.JAVADOC); |
| newConstructor.setJavadoc(javadoc); |
| } |
| } catch (CoreException exception) { |
| throw new JavaModelException(exception); |
| } |
| } |
| return newConstructor; |
| } |
| |
| private Statement newFieldAssignment(AST ast, SimpleName fieldNameNode, Expression initializer, boolean useThisAccess) { |
| Assignment assignment= ast.newAssignment(); |
| if (useThisAccess) { |
| FieldAccess access= ast.newFieldAccess(); |
| access.setExpression(ast.newThisExpression()); |
| access.setName(fieldNameNode); |
| assignment.setLeftHandSide(access); |
| } else { |
| assignment.setLeftHandSide(fieldNameNode); |
| } |
| assignment.setOperator(Assignment.Operator.ASSIGN); |
| assignment.setRightHandSide(initializer); |
| |
| return ast.newExpressionStatement(assignment); |
| } |
| |
| |
| // live List of VariableDeclarationFragments |
| private List<VariableDeclarationFragment> getFieldsToInitializeInConstructor() { |
| List<VariableDeclarationFragment> result= new ArrayList<VariableDeclarationFragment>(0); |
| for (Iterator<BodyDeclaration> iter= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iter.hasNext(); ) { |
| Object element= iter.next(); |
| if (element instanceof FieldDeclaration) { |
| List<VariableDeclarationFragment> fragments= ((FieldDeclaration) element).fragments(); |
| for (Iterator<VariableDeclarationFragment> fragmentIter= fragments.iterator(); fragmentIter.hasNext(); ) { |
| VariableDeclarationFragment fragment= fragmentIter.next(); |
| if (isToBeInitializerInConstructor(fragment, result)) |
| result.add(fragment); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private boolean isToBeInitializerInConstructor(VariableDeclarationFragment fragment, List<VariableDeclarationFragment> fieldsToInitialize) { |
| return fragment.getInitializer() != null && areLocalsUsedIn(fragment.getInitializer(), fieldsToInitialize); |
| } |
| |
| private boolean areLocalsUsedIn(Expression fieldInitializer, List<VariableDeclarationFragment> fieldsToInitialize) { |
| Set<IBinding> localsUsed= new HashSet<IBinding>(0); |
| collectRefrencedVariables(fieldInitializer, localsUsed); |
| |
| ITypeBinding anonType= fAnonymousInnerClassNode.resolveBinding(); |
| |
| for (Iterator<IBinding> iterator= localsUsed.iterator(); iterator.hasNext();) { |
| IVariableBinding curr= (IVariableBinding) iterator.next(); |
| if (isBindingToTemp(curr)) { // reference a local from outside |
| return true; |
| } else if (curr.isField() && (curr.getDeclaringClass() == anonType) && fieldsToInitialize.contains(fCompilationUnitNode.findDeclaringNode(curr))) { |
| return true; // references a field that references a local from outside |
| } |
| } |
| return false; |
| } |
| |
| private IMethodBinding getSuperConstructorBinding() { |
| //workaround for missing java core functionality - finding a |
| // super constructor for an anonymous class creation |
| IMethodBinding anonConstr= ((ClassInstanceCreation) fAnonymousInnerClassNode.getParent()).resolveConstructorBinding(); |
| if (anonConstr == null) |
| return null; |
| ITypeBinding superClass= anonConstr.getDeclaringClass().getSuperclass(); |
| IMethodBinding[] superMethods= superClass.getDeclaredMethods(); |
| for (int i= 0; i < superMethods.length; i++) { |
| IMethodBinding superMethod= superMethods[i]; |
| if (superMethod.isConstructor() && parameterTypesMatch(superMethod, anonConstr)) |
| return superMethod; |
| } |
| Assert.isTrue(false);//there's no way - it must be there |
| return null; |
| } |
| |
| private static boolean parameterTypesMatch(IMethodBinding m1, IMethodBinding m2) { |
| ITypeBinding[] m1Params= m1.getParameterTypes(); |
| ITypeBinding[] m2Params= m2.getParameterTypes(); |
| if (m1Params.length != m2Params.length) |
| return false; |
| for (int i= 0; i < m2Params.length; i++) { |
| if (!m1Params[i].equals(m2Params[i])) |
| return false; |
| } |
| return true; |
| } |
| |
| private void addExceptionsToNewConstructor(MethodDeclaration newConstructor) { |
| IMethodBinding constructorBinding= getSuperConstructorBinding(); |
| if (constructorBinding == null) |
| return; |
| ITypeBinding[] exceptions= constructorBinding.getExceptionTypes(); |
| for (int i= 0; i < exceptions.length; i++) { |
| Name exceptionTypeName= fAnonymousInnerClassNode.getAST().newName(Bindings.getNameComponents(exceptions[i])); |
| newConstructor.thrownExceptions().add(exceptionTypeName); |
| } |
| } |
| |
| private SingleVariableDeclaration newParameterDeclaration(AST ast, ImportRewrite importRewrite, String paramName, ITypeBinding paramType) { |
| SingleVariableDeclaration param= ast.newSingleVariableDeclaration(); |
| param.setType(importRewrite.addImport(paramType, ast)); |
| param.setName(ast.newSimpleName(paramName)); |
| return param; |
| } |
| |
| private void setSuperType(TypeDeclaration declaration) { |
| ClassInstanceCreation classInstanceCreation= (ClassInstanceCreation) fAnonymousInnerClassNode.getParent(); |
| ITypeBinding binding= classInstanceCreation.resolveTypeBinding(); |
| if (binding == null) |
| return; |
| Type newType= (Type) ASTNode.copySubtree(fAnonymousInnerClassNode.getAST(), classInstanceCreation.getType()); |
| if (binding.getSuperclass().getQualifiedName().equals("java.lang.Object")) { //$NON-NLS-1$ |
| Assert.isTrue(binding.getInterfaces().length <= 1); |
| if (binding.getInterfaces().length == 0) |
| return; |
| declaration.superInterfaceTypes().add(0, newType); |
| } else { |
| declaration.setSuperclassType(newType); |
| } |
| } |
| |
| private ITypeBinding getSuperTypeBinding() { |
| ITypeBinding types= fAnonymousInnerClassNode.resolveBinding(); |
| ITypeBinding[] interfaces= types.getInterfaces(); |
| if (interfaces.length > 0) |
| return interfaces[0]; |
| else |
| return types.getSuperclass(); |
| } |
| |
| private int createModifiersForNestedClass() { |
| int flags= fVisibility; |
| if (fDeclareFinal) |
| flags|= Modifier.FINAL; |
| if (mustInnerClassBeStatic() || fDeclareStatic) |
| flags|= Modifier.STATIC; |
| return flags; |
| } |
| |
| public boolean mustInnerClassBeStatic() { |
| ITypeBinding typeBinding = ((AbstractTypeDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, AbstractTypeDeclaration.class)).resolveBinding(); |
| ASTNode current = fAnonymousInnerClassNode.getParent(); |
| boolean ans = false; |
| while(current != null) { |
| switch(current.getNodeType()) { |
| case ASTNode.ANONYMOUS_CLASS_DECLARATION: |
| { |
| AnonymousClassDeclaration enclosingAnonymousClassDeclaration= (AnonymousClassDeclaration)current; |
| ITypeBinding binding= enclosingAnonymousClassDeclaration.resolveBinding(); |
| if (binding != null && Bindings.isSuperType(typeBinding, binding.getSuperclass())) { |
| return false; |
| } |
| break; |
| } |
| case ASTNode.FIELD_DECLARATION: |
| { |
| FieldDeclaration enclosingFieldDeclaration= (FieldDeclaration)current; |
| if (Modifier.isStatic(enclosingFieldDeclaration.getModifiers())) { |
| ans = true; |
| } |
| break; |
| } |
| case ASTNode.METHOD_DECLARATION: |
| { |
| MethodDeclaration enclosingMethodDeclaration = (MethodDeclaration)current; |
| if (Modifier.isStatic(enclosingMethodDeclaration.getModifiers())) { |
| ans = true; |
| } |
| break; |
| } |
| case ASTNode.TYPE_DECLARATION: |
| { |
| return ans; |
| } |
| } |
| current = current.getParent(); |
| } |
| return ans; |
| } |
| |
| private RefactoringStatus initialize(JavaRefactoringArguments arguments) { |
| fSelfInitializing= true; |
| final String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); |
| if (handle != null) { |
| final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false); |
| if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT) |
| return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.CONVERT_ANONYMOUS); |
| else { |
| fCu= (ICompilationUnit) element; |
| } |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); |
| final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME); |
| if (name != null && !"".equals(name)) //$NON-NLS-1$ |
| fClassName= name; |
| else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME)); |
| final String visibility= arguments.getAttribute(ATTRIBUTE_VISIBILITY); |
| if (visibility != null && !"".equals(visibility)) {//$NON-NLS-1$ |
| int flag= 0; |
| try { |
| flag= Integer.parseInt(visibility); |
| } catch (NumberFormatException exception) { |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY)); |
| } |
| fVisibility= flag; |
| } |
| final String selection= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION); |
| if (selection != null) { |
| int offset= -1; |
| int length= -1; |
| final StringTokenizer tokenizer= new StringTokenizer(selection); |
| if (tokenizer.hasMoreTokens()) |
| offset= Integer.valueOf(tokenizer.nextToken()).intValue(); |
| if (tokenizer.hasMoreTokens()) |
| length= Integer.valueOf(tokenizer.nextToken()).intValue(); |
| if (offset >= 0 && length >= 0) { |
| fSelectionStart= offset; |
| fSelectionLength= length; |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION})); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION)); |
| final String declareStatic= arguments.getAttribute(ATTRIBUTE_STATIC); |
| if (declareStatic != null) { |
| fDeclareStatic= Boolean.valueOf(declareStatic).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_STATIC)); |
| final String declareFinal= arguments.getAttribute(ATTRIBUTE_FINAL); |
| if (declareFinal != null) { |
| fDeclareFinal= Boolean.valueOf(declareStatic).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL)); |
| return new RefactoringStatus(); |
| } |
| } |