| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-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.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.TextEdit; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.ExpressionStatement; |
| 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.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.Name; |
| 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.SuperConstructorInvocation; |
| import org.eclipse.jdt.core.dom.SuperFieldAccess; |
| import org.eclipse.jdt.core.dom.TypeDeclaration; |
| import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.jdt.core.formatter.CodeFormatter; |
| |
| import org.eclipse.jdt.internal.corext.Assert; |
| 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.ASTRewrite; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.jdt.internal.corext.dom.NodeFinder; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.base.IChange; |
| import org.eclipse.jdt.internal.corext.refactoring.base.Refactoring; |
| import org.eclipse.jdt.internal.corext.refactoring.base.RefactoringStatus; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.TextChange; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.jdt.internal.corext.textmanipulation.TextBuffer; |
| import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; |
| import org.eclipse.jdt.internal.corext.util.Strings; |
| |
| public class ConvertAnonymousToNestedRefactoring extends Refactoring { |
| |
| private final int fSelectionStart; |
| private final int fSelectionLength; |
| private final ICompilationUnit fCu; |
| |
| private int fVisibility; /* see Modifier */ |
| private boolean fDeclareFinal; |
| private boolean fDeclareStatic; |
| private String fClassName; |
| |
| private CompilationUnit fCompilationUnitNode; |
| private AnonymousClassDeclaration fAnonymousInnerClassNode; |
| private Set fClassNamesUsed; |
| |
| private ConvertAnonymousToNestedRefactoring(ICompilationUnit cu, int selectionStart, int selectionLength) { |
| Assert.isTrue(selectionStart >= 0); |
| Assert.isTrue(selectionLength >= 0); |
| Assert.isTrue(cu.exists()); |
| fSelectionStart= selectionStart; |
| fSelectionLength= selectionLength; |
| fCu= cu; |
| } |
| |
| public static boolean isAvailable(IType type) throws JavaModelException { |
| return type.isAnonymous(); |
| } |
| |
| public static ConvertAnonymousToNestedRefactoring create(ICompilationUnit cu, int selectionStart, int selectionLength) { |
| return new ConvertAnonymousToNestedRefactoring(cu, selectionStart, selectionLength); |
| } |
| |
| public int[] getAvailableVisibilities() { |
| if (isLocalInnerType()) { |
| return new int[] { Modifier.NONE }; |
| } else { |
| return new int[] { Modifier.PUBLIC, Modifier.PROTECTED, Modifier.NONE, Modifier.PRIVATE }; |
| } |
| } |
| |
| private boolean isLocalInnerType() { |
| return ASTNodes.getParent(getTypeDeclaration(), 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; |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#getName() |
| */ |
| public String getName() { |
| return RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.name"); //$NON-NLS-1$ |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.Refactoring#checkActivation(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public RefactoringStatus checkActivation(IProgressMonitor pm) throws JavaModelException { |
| try { |
| RefactoringStatus result= Checks.validateModifiesFiles(ResourceUtil.getFiles(new ICompilationUnit[]{fCu})); |
| if (result.hasFatalError()) |
| return result; |
| |
| initAST(); |
| |
| if (fAnonymousInnerClassNode == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.place_caret")); //$NON-NLS-1$ |
| initializeDefaults(); |
| if (getSuperConstructorBinding() == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.compile_errors")); //$NON-NLS-1$ |
| return new RefactoringStatus(); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private void initializeDefaults() { |
| fVisibility= isLocalInnerType() ? Modifier.NONE : Modifier.PRIVATE; |
| fClassName= ""; //$NON-NLS-1$ |
| fDeclareFinal= true; |
| fDeclareStatic = mustInnerClassBeStatic(); |
| } |
| |
| private void initAST() { |
| fCompilationUnitNode= AST.parseCompilationUnit(fCu, true); |
| fAnonymousInnerClassNode= getAnonymousInnerClass(NodeFinder.perform(fCompilationUnitNode, fSelectionStart, fSelectionLength)); |
| if (fAnonymousInnerClassNode != null) { |
| TypeDeclaration[] nestedtypes= getTypeDeclaration().getTypes(); |
| fClassNamesUsed= new HashSet(nestedtypes.length); |
| for (int i= 0; i < nestedtypes.length; i++) { |
| fClassNamesUsed.add(nestedtypes[i].getName().getIdentifier()); |
| } |
| } |
| } |
| |
| 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; |
| } |
| if (node.getParent() instanceof ClassInstanceCreation) { |
| 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); |
| if (result.hasFatalError()) |
| return result; |
| |
| if (fClassNamesUsed.contains(fClassName)) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.type_exists")); //$NON-NLS-1$ |
| IMethodBinding superConstructorBinding = getSuperConstructorBinding(); |
| if (superConstructorBinding == null) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.compile_errors")); //$NON-NLS-1$ |
| if (fClassName.equals(superConstructorBinding.getDeclaringClass().getName())) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.another_name")); //$NON-NLS-1$ |
| if (classNameHidesEnclosingType()) |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.name_hides")); //$NON-NLS-1$ |
| return result; |
| } |
| |
| private boolean accessesAnonymousFields() { |
| List anonymousInnerFieldTypes = getAllEnclosingAnonymousTypesField(); |
| List accessedField = getAllAccessedFields(); |
| final Iterator it = anonymousInnerFieldTypes.iterator(); |
| while(it.hasNext()) { |
| final IVariableBinding variableBinding = (IVariableBinding) it.next(); |
| final Iterator it2 = accessedField.iterator(); |
| while (it2.hasNext()) { |
| IVariableBinding variableBinding2 = (IVariableBinding) it2.next(); |
| if(Bindings.equals(variableBinding, variableBinding2)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private List getAllAccessedFields() { |
| final List accessedFields= new ArrayList(); |
| |
| ASTVisitor visitor= new ASTVisitor() { |
| public boolean visit(SimpleName node) { |
| final IBinding binding = node.resolveBinding(); |
| if(binding != null && binding instanceof IVariableBinding) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| |
| public boolean visit(FieldAccess node) { |
| final IVariableBinding binding = node.resolveFieldBinding(); |
| if(binding != null) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| |
| public boolean visit(QualifiedName node) { |
| final IBinding binding = node.resolveBinding(); |
| if(binding != null && binding instanceof IVariableBinding) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| |
| public boolean visit(SuperFieldAccess node) { |
| final IVariableBinding binding = node.resolveFieldBinding(); |
| if(binding != null) |
| accessedFields.add(binding); |
| return super.visit(node); |
| } |
| }; |
| fAnonymousInnerClassNode.accept(visitor); |
| |
| return accessedFields; |
| } |
| |
| private List getAllEnclosingAnonymousTypesField() { |
| final List ans = new ArrayList(); |
| final TypeDeclaration typeDeclaration = getTypeDeclaration(); |
| AnonymousClassDeclaration current = (AnonymousClassDeclaration) ASTNodes.getParent(fAnonymousInnerClassNode, ASTNode.ANONYMOUS_CLASS_DECLARATION); |
| while(current != null) |
| { |
| if(ASTNodes.isParent(current, typeDeclaration)) |
| { |
| ITypeBinding binding = current.resolveBinding(); |
| if(binding != null) |
| { |
| ans.addAll(Arrays.asList(binding.getDeclaredFields())); |
| } |
| } |
| else |
| { |
| break; |
| } |
| current = (AnonymousClassDeclaration) ASTNodes.getParent(current, ASTNode.ANONYMOUS_CLASS_DECLARATION); |
| } |
| return ans; |
| } |
| |
| private boolean classNameHidesEnclosingType() { |
| ITypeBinding type= getTypeDeclaration().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) |
| */ |
| public RefactoringStatus checkInput(IProgressMonitor pm) throws JavaModelException { |
| try { |
| RefactoringStatus status= validateInput(); |
| if (accessesAnonymousFields()) |
| status.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.annonymous_field_access"))); //$NON-NLS-1$ |
| return status; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.corext.refactoring.base.IRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public IChange createChange(IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| try { |
| ASTRewrite rewrite= new ASTRewrite(fCompilationUnitNode); |
| addNestedClass(rewrite); |
| modifyConstructorCall(rewrite); |
| return createChange(rewrite); |
| } catch (JavaModelException e) { |
| throw e; |
| } catch (CoreException e) { |
| throw new JavaModelException(e); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private IChange createChange(ASTRewrite rewrite) throws CoreException { |
| TextChange change= new CompilationUnitChange("", fCu); //$NON-NLS-1$ |
| TextBuffer textBuffer= TextBuffer.create(fCu.getBuffer().getContents()); |
| TextEdit resultingEdits= new MultiTextEdit(); |
| rewrite.rewriteNode(textBuffer, resultingEdits); |
| change.addTextEdit(RefactoringCoreMessages.getString("ConvertAnonymousToNestedRefactoring.edit_name"), resultingEdits); //$NON-NLS-1$ |
| rewrite.removeModifications(); |
| return change; |
| } |
| |
| private void modifyConstructorCall(ASTRewrite rewrite) { |
| rewrite.markAsReplaced(getClassInstanceCreation(), createNewClassInstanceCreation(rewrite)); |
| } |
| |
| private ASTNode createNewClassInstanceCreation(ASTRewrite rewrite) { |
| ClassInstanceCreation newClassCreation= getAST().newClassInstanceCreation(); |
| newClassCreation.setAnonymousClassDeclaration(null); |
| newClassCreation.setName(getAST().newSimpleName(fClassName)); |
| copyArguments(rewrite, newClassCreation); |
| addArgumentsForLocalsUsedInInnerClass(rewrite, newClassCreation); |
| return newClassCreation; |
| } |
| |
| private void addArgumentsForLocalsUsedInInnerClass(ASTRewrite rewrite, ClassInstanceCreation newClassCreation) { |
| IVariableBinding[] usedLocals= getUsedLocalVariables(); |
| for (int i= 0; i < usedLocals.length; i++) { |
| IVariableBinding local= usedLocals[i]; |
| Expression expression= getAST().newSimpleName(local.getName()); |
| rewrite.markAsInserted(expression); |
| newClassCreation.arguments().add(expression); |
| } |
| } |
| |
| private void copyArguments(ASTRewrite rewrite, ClassInstanceCreation newClassCreation) { |
| for (Iterator iter= getClassInstanceCreation().arguments().iterator(); iter.hasNext(); ) { |
| Expression arg= (Expression)iter.next(); |
| Expression copy= (Expression)rewrite.createCopy(arg); |
| rewrite.markAsInserted(copy); |
| newClassCreation.arguments().add(copy); |
| } |
| } |
| |
| private void addNestedClass(ASTRewrite rewrite) throws JavaModelException { |
| TypeDeclaration type= getTypeDeclaration(); |
| List bodyDeclarations= type.bodyDeclarations(); |
| int index= findIndexOfFistNestedClass(bodyDeclarations); |
| if (index == -1) |
| index= 0; |
| TypeDeclaration newNestedClass= createNewNestedClass(rewrite); |
| rewrite.markAsInserted(newNestedClass); |
| bodyDeclarations.add(index, newNestedClass); |
| } |
| |
| private static int findIndexOfFistNestedClass(List bodyDeclarations) { |
| for (int i= 0, n= bodyDeclarations.size(); i < n; i++) { |
| BodyDeclaration each= (BodyDeclaration)bodyDeclarations.get(i); |
| if (isNestedType(each)) |
| return i; |
| } |
| return -1; |
| } |
| |
| private static boolean isNestedType(BodyDeclaration each) { |
| if (!(each instanceof TypeDeclaration)) |
| return false; |
| return (each.getParent() instanceof TypeDeclaration); |
| } |
| |
| private TypeDeclaration createNewNestedClass(ASTRewrite rewrite) throws JavaModelException { |
| TypeDeclaration newType= getAST().newTypeDeclaration(); |
| newType.setInterface(false); |
| newType.setJavadoc(null); |
| newType.setModifiers(createModifiersForNestedClass()); |
| newType.setName(getAST().newSimpleName(fClassName)); |
| setSuperType(newType); |
| removeInitializationFromDeclaredFields(rewrite); |
| copyBodyDeclarationsToNestedClass(rewrite, newType); |
| createFieldsForAccessedLocals(rewrite, newType); |
| createNewConstructorIfNeeded(rewrite, newType); |
| return newType; |
| } |
| |
| private void removeInitializationFromDeclaredFields(ASTRewrite rewrite) { |
| for (Iterator iter= getFieldsToInitializeInConstructor().iterator(); iter.hasNext(); ) { |
| VariableDeclarationFragment fragment= (VariableDeclarationFragment)iter.next(); |
| Assert.isNotNull(fragment.getInitializer()); |
| rewrite.markAsRemoved(fragment.getInitializer()); |
| } |
| } |
| |
| private void createFieldsForAccessedLocals(ASTRewrite rewrite, TypeDeclaration newType) { |
| IVariableBinding[] usedLocals= getUsedLocalVariables(); |
| for (int i= 0; i < usedLocals.length; i++) { |
| IVariableBinding local= usedLocals[i]; |
| VariableDeclarationFragment fragment= getAST().newVariableDeclarationFragment(); |
| fragment.setExtraDimensions(0); |
| fragment.setInitializer(null); |
| fragment.setName(getAST().newSimpleName(local.getName())); |
| FieldDeclaration field= getAST().newFieldDeclaration(fragment); |
| field.setType(ASTNodeFactory.newType(getAST(), local.getType(), false)); |
| field.setModifiers(Modifier.PRIVATE | Modifier.FINAL); |
| newType.bodyDeclarations().add(findIndexOfLastField(newType.bodyDeclarations()) + 1, field); |
| rewrite.markAsInserted(field); |
| } |
| } |
| |
| private IVariableBinding[] getUsedLocalVariables() { |
| final Set result= new HashSet(0); |
| fAnonymousInnerClassNode.accept(createTempUsageFinder(result)); |
| return (IVariableBinding[])result.toArray(new IVariableBinding[result.size()]); |
| } |
| |
| private ASTVisitor createTempUsageFinder(final Set result) { |
| return new ASTVisitor() { |
| public boolean visit(SimpleName node) { |
| IBinding binding= node.resolveBinding(); |
| if (ConvertAnonymousToNestedRefactoring.this.isBindingToTemp(binding)) |
| result.add(binding); |
| return true; |
| } |
| }; |
| } |
| |
| private boolean isBindingToTemp(IBinding binding) { |
| if (!(binding instanceof IVariableBinding)) |
| return false; |
| if (!Modifier.isFinal(binding.getModifiers())) |
| return false; |
| ASTNode declaringNode= fCompilationUnitNode.findDeclaringNode(binding); |
| if (declaringNode == null) |
| return false; |
| if (ASTNodes.isParent(declaringNode, fAnonymousInnerClassNode)) |
| return false; |
| return true; |
| } |
| |
| private void createNewConstructorIfNeeded(ASTRewrite rewrite, TypeDeclaration newType) throws JavaModelException { |
| IVariableBinding[] usedLocals= getUsedLocalVariables(); |
| |
| if (getClassInstanceCreation().arguments().isEmpty() && usedLocals.length == 0) |
| return; |
| |
| MethodDeclaration newConstructor= getAST().newMethodDeclaration(); |
| newConstructor.setConstructor(true); |
| newConstructor.setExtraDimensions(0); |
| newConstructor.setJavadoc(null); |
| newConstructor.setModifiers(fVisibility); |
| newConstructor.setName(getAST().newSimpleName(fClassName)); |
| addParametersToNewConstructor(newConstructor, rewrite); |
| int paramCount= newConstructor.parameters().size(); |
| |
| addParametersForLocalsUsedInInnerClass(rewrite, usedLocals, newConstructor); |
| |
| Block constructorBody= getAST().newBlock(); |
| SuperConstructorInvocation superConstructorInvocation= getAST().newSuperConstructorInvocation(); |
| for (int i= 0; i < paramCount; i++) { |
| SingleVariableDeclaration param= (SingleVariableDeclaration)newConstructor.parameters().get(i); |
| superConstructorInvocation.arguments().add(getAST().newSimpleName(param.getName().getIdentifier())); |
| } |
| constructorBody.statements().add(superConstructorInvocation); |
| |
| for (int i= 0; i < usedLocals.length; i++) { |
| IVariableBinding local= usedLocals[i]; |
| String unformattedAssigmentCode= "this." + local.getName() + "=" + local.getName(); //$NON-NLS-1$ //$NON-NLS-2$ |
| String assignmentCode= CodeFormatterUtil.format(CodeFormatter.K_EXPRESSION, unformattedAssigmentCode, 0, null, getLineSeparator(), null); |
| Expression assignmentExpression= (Expression)rewrite.createPlaceholder(assignmentCode, ASTRewrite.EXPRESSION); |
| ExpressionStatement assignmentStatement= getAST().newExpressionStatement(assignmentExpression); |
| constructorBody.statements().add(assignmentStatement); |
| } |
| |
| addFieldInitialization(rewrite, constructorBody); |
| |
| newConstructor.setBody(constructorBody); |
| |
| addExceptionsToNewConstructor(newConstructor); |
| rewrite.markAsInserted(newConstructor); |
| int index= 1 + usedLocals.length + findIndexOfLastField(fAnonymousInnerClassNode.bodyDeclarations()); |
| newType.bodyDeclarations().add(index, newConstructor); |
| } |
| |
| private void addFieldInitialization(ASTRewrite rewrite, Block constructorBody) { |
| for (Iterator iter= getFieldsToInitializeInConstructor().iterator(); iter.hasNext(); ) { |
| VariableDeclarationFragment fragment= (VariableDeclarationFragment)iter.next(); |
| Assignment assignmentExpression= getAST().newAssignment(); |
| assignmentExpression.setOperator(Assignment.Operator.ASSIGN); |
| assignmentExpression.setLeftHandSide(getAST().newSimpleName(fragment.getName().getIdentifier())); |
| Expression rhs= (Expression)rewrite.createCopy(fragment.getInitializer()); |
| assignmentExpression.setRightHandSide(rhs); |
| ExpressionStatement assignmentStatement= getAST().newExpressionStatement(assignmentExpression); |
| constructorBody.statements().add(assignmentStatement); |
| } |
| } |
| |
| //live List of VariableDeclarationFragments |
| private List getFieldsToInitializeInConstructor() { |
| List result= new ArrayList(0); |
| for (Iterator iter= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iter.hasNext(); ) { |
| BodyDeclaration element= (BodyDeclaration)iter.next(); |
| if (!(element instanceof FieldDeclaration)) |
| continue; |
| FieldDeclaration field= (FieldDeclaration)element; |
| for (Iterator fragmentIter= field.fragments().iterator(); fragmentIter.hasNext(); ) { |
| VariableDeclarationFragment fragment= (VariableDeclarationFragment)fragmentIter.next(); |
| if (isToBeInitializerInConstructor(fragment)) |
| result.add(fragment); |
| } |
| } |
| return result; |
| } |
| |
| private boolean isToBeInitializerInConstructor(VariableDeclarationFragment fragment) { |
| if (fragment.getInitializer() == null) |
| return false; |
| return areLocalsUsedIn(fragment.getInitializer()); |
| } |
| |
| private boolean areLocalsUsedIn(Expression fieldInitializer) { |
| Set localsUsed= new HashSet(0); |
| fieldInitializer.accept(createTempUsageFinder(localsUsed)); |
| return !localsUsed.isEmpty(); |
| } |
| |
| private void addParametersForLocalsUsedInInnerClass(ASTRewrite rewrite, IVariableBinding[] usedLocals, MethodDeclaration newConstructor) { |
| for (int i= 0; i < usedLocals.length; i++) { |
| IVariableBinding local= usedLocals[i]; |
| SingleVariableDeclaration param= createNewParamDeclarationNode(local.getName(), local.getType()); |
| rewrite.markAsInserted(param); |
| newConstructor.parameters().add(param); |
| } |
| } |
| |
| private IMethodBinding getSuperConstructorBinding() { |
| //workaround for missing jcore functionality - finding a |
| // superconstructor for an anonymous class creation |
| IMethodBinding anonConstr= getClassInstanceCreation().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= getAST().newName(Bindings.getNameComponents(exceptions[i])); |
| newConstructor.thrownExceptions().add(exceptionTypeName); |
| } |
| } |
| |
| private void addParametersToNewConstructor(MethodDeclaration newConstructor, ASTRewrite rewrite) throws JavaModelException { |
| IMethodBinding constructorBinding= getSuperConstructorBinding(); |
| if (constructorBinding == null) |
| return; |
| ITypeBinding[] paramTypes= constructorBinding.getParameterTypes(); |
| IMethod method= Bindings.findMethod(constructorBinding, fCu.getJavaProject()); |
| if (method == null) |
| return; |
| String[] parameterNames= method.getParameterNames(); |
| for (int i= 0; i < parameterNames.length; i++) { |
| SingleVariableDeclaration param= createNewParamDeclarationNode(parameterNames[i], paramTypes[i]); |
| rewrite.markAsInserted(param); |
| newConstructor.parameters().add(param); |
| } |
| } |
| |
| private SingleVariableDeclaration createNewParamDeclarationNode(String paramName, ITypeBinding paramType) { |
| SingleVariableDeclaration param= getAST().newSingleVariableDeclaration(); |
| param.setExtraDimensions(0); |
| param.setInitializer(null); |
| param.setModifiers(Modifier.NONE); |
| param.setName(getAST().newSimpleName(paramName)); |
| param.setType(ASTNodeFactory.newType(getAST(), paramType, false)); |
| return param; |
| } |
| |
| private void copyBodyDeclarationsToNestedClass(ASTRewrite rewrite, TypeDeclaration newType) { |
| for (Iterator iter= fAnonymousInnerClassNode.bodyDeclarations().iterator(); iter.hasNext(); ) { |
| BodyDeclaration element= (BodyDeclaration)iter.next(); |
| BodyDeclaration copy= (BodyDeclaration)rewrite.createCopy(element); |
| rewrite.markAsInserted(copy); |
| newType.bodyDeclarations().add(copy); |
| } |
| } |
| |
| private void setSuperType(TypeDeclaration newType) throws JavaModelException { |
| ITypeBinding binding= getClassInstanceCreation().resolveTypeBinding(); |
| if (binding == null) |
| return; |
| if (binding.getSuperclass().getQualifiedName().equals("java.lang.Object")) { //$NON-NLS-1$ |
| Assert.isTrue(binding.getInterfaces().length <= 1); |
| if (binding.getInterfaces().length == 0) |
| return; |
| newType.superInterfaces().add(0, getSuperTypeName()); |
| } else { |
| newType.setSuperclass(getSuperTypeName()); |
| } |
| } |
| |
| private Name getSuperTypeName() throws JavaModelException { |
| return getAST().newName(Strings.splitByToken(getNodeSourceCode(getClassInstanceCreation().getName()), ".")); //$NON-NLS-1$ |
| } |
| |
| private String getNodeSourceCode(ASTNode node) throws JavaModelException { |
| return fCu.getBuffer().getText(node.getStartPosition(), node.getLength()); |
| } |
| |
| private int createModifiersForNestedClass() { |
| int flags= fVisibility; |
| if (fDeclareFinal) |
| flags|= Modifier.FINAL; |
| if (mustInnerClassBeStatic() || fDeclareStatic) |
| flags|= Modifier.STATIC; |
| return flags; |
| } |
| |
| private AST getAST() { |
| return fAnonymousInnerClassNode.getAST(); |
| } |
| |
| private ClassInstanceCreation getClassInstanceCreation() { |
| return (ClassInstanceCreation)fAnonymousInnerClassNode.getParent(); |
| } |
| |
| private TypeDeclaration getTypeDeclaration() { |
| return (TypeDeclaration)ASTNodes.getParent(fAnonymousInnerClassNode, TypeDeclaration.class); |
| } |
| |
| public boolean isStaticModifierOptional() { |
| return !mustInnerClassBeStatic() && !isLocalInnerType(); |
| } |
| |
| private boolean mustInnerClassBeStatic() { |
| ITypeBinding typeBinding = getTypeDeclaration().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 String getLineSeparator() { |
| try { |
| return StubUtility.getLineDelimiterUsed(fCu); |
| } catch (JavaModelException e) { |
| return System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| private static int findIndexOfLastField(List bodyDeclarations) { |
| for (int i= bodyDeclarations.size() - 1; i >= 0; i--) { |
| BodyDeclaration each= (BodyDeclaration)bodyDeclarations.get(i); |
| if (each instanceof FieldDeclaration) |
| return i; |
| } |
| return -1; |
| } |
| } |