/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.corext.refactoring.structure; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.IRefactoringStatusEntryComparator; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.wst.jsdt.core.IField; import org.eclipse.wst.jsdt.core.IFunction; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.ITypeHierarchy; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTParser; import org.eclipse.wst.jsdt.core.dom.ASTVisitor; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration; import org.eclipse.wst.jsdt.core.dom.Assignment; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation; import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.FieldAccess; import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.core.dom.FunctionInvocation; import org.eclipse.wst.jsdt.core.dom.IBinding; import org.eclipse.wst.jsdt.core.dom.IFunctionBinding; import org.eclipse.wst.jsdt.core.dom.ITypeBinding; import org.eclipse.wst.jsdt.core.dom.IVariableBinding; import org.eclipse.wst.jsdt.core.dom.ImportDeclaration; import org.eclipse.wst.jsdt.core.dom.JSdoc; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.core.dom.Modifier; import org.eclipse.wst.jsdt.core.dom.Name; import org.eclipse.wst.jsdt.core.dom.QualifiedName; import org.eclipse.wst.jsdt.core.dom.QualifiedType; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.SimpleType; import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration; import org.eclipse.wst.jsdt.core.dom.Statement; import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation; import org.eclipse.wst.jsdt.core.dom.ThisExpression; import org.eclipse.wst.jsdt.core.dom.Type; import org.eclipse.wst.jsdt.core.dom.TypeDeclaration; import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement; import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment; import org.eclipse.wst.jsdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite; import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor; import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants; import org.eclipse.wst.jsdt.core.search.SearchMatch; import org.eclipse.wst.jsdt.core.search.SearchPattern; import org.eclipse.wst.jsdt.internal.corext.codemanipulation.CodeGenerationSettings; import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.dom.Bindings; import org.eclipse.wst.jsdt.internal.corext.dom.ModifierRewrite; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor; import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringScopeFactory; import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringSearchEngine2; import org.eclipse.wst.jsdt.internal.corext.refactoring.SearchResultGroup; import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CreateCompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ScriptableRefactoring; import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavaElementUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavadocUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextChangeManager; import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil; import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.corext.util.SearchUtils; import org.eclipse.wst.jsdt.internal.corext.util.Strings; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.internal.ui.preferences.JavaPreferencesSettings; import org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider; import org.eclipse.wst.jsdt.ui.CodeGeneration; import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; public final class MoveInnerToTopRefactoring extends ScriptableRefactoring { private static final String ATTRIBUTE_FIELD= "field"; //$NON-NLS-1$ private static final String ATTRIBUTE_MANDATORY= "mandatory"; //$NON-NLS-1$ private static final String ATTRIBUTE_POSSIBLE= "possible"; //$NON-NLS-1$ private static final String ATTRIBUTE_FINAL= "final"; //$NON-NLS-1$ private static final String ATTRIBUTE_FIELD_NAME= "fieldName"; //$NON-NLS-1$ private static final String ATTRIBUTE_PARAMETER_NAME= "parameterName"; //$NON-NLS-1$ private static class MemberAccessNodeCollector extends ASTVisitor { private final List fFieldAccesses= new ArrayList(0); private final ITypeHierarchy fHierarchy; private final List fMethodAccesses= new ArrayList(0); private final List fSimpleNames= new ArrayList(0); MemberAccessNodeCollector(ITypeHierarchy hierarchy) { Assert.isNotNull(hierarchy); fHierarchy= hierarchy; } FieldAccess[] getFieldAccesses() { return (FieldAccess[]) fFieldAccesses.toArray(new FieldAccess[fFieldAccesses.size()]); } FunctionInvocation[] getMethodInvocations() { return (FunctionInvocation[]) fMethodAccesses.toArray(new FunctionInvocation[fMethodAccesses.size()]); } SimpleName[] getSimpleFieldNames() { return (SimpleName[]) fSimpleNames.toArray(new SimpleName[fSimpleNames.size()]); } public boolean visit(FieldAccess node) { final ITypeBinding declaring= MoveInnerToTopRefactoring.getDeclaringTypeBinding(node); if (declaring != null) { final IType type= (IType) declaring.getJavaElement(); if (type != null && fHierarchy.contains(type)) fFieldAccesses.add(node); } return super.visit(node); } public boolean visit(FunctionInvocation node) { final ITypeBinding declaring= MoveInnerToTopRefactoring.getDeclaringTypeBinding(node); if (declaring != null) { final IType type= (IType) declaring.getJavaElement(); if (type != null && fHierarchy.contains(type)) fMethodAccesses.add(node); } return super.visit(node); } public boolean visit(SimpleName node) { if (node.getParent() instanceof QualifiedName) return super.visit(node); IBinding binding= node.resolveBinding(); if (binding instanceof IVariableBinding) { IVariableBinding variable= (IVariableBinding) binding; ITypeBinding declaring= variable.getDeclaringClass(); if (variable.isField() && declaring != null) { final IType type= (IType) declaring.getJavaElement(); if (type != null && fHierarchy.contains(type)) { fSimpleNames.add(node); return false; } } } return super.visit(node); } public boolean visit(ThisExpression node) { final Name qualifier= node.getQualifier(); if (qualifier != null) { final ITypeBinding binding= qualifier.resolveTypeBinding(); if (binding != null) { final IType type= (IType) binding.getJavaElement(); if (type != null && fHierarchy.contains(type)) { fSimpleNames.add(qualifier); return false; } } } return super.visit(node); } } private class TypeReferenceQualifier extends ASTVisitor { private final TextEditGroup fGroup; private final ITypeBinding fTypeBinding; public TypeReferenceQualifier(final ITypeBinding type, final TextEditGroup group) { Assert.isNotNull(type); Assert.isNotNull(type.getDeclaringClass()); fTypeBinding= type; fGroup= group; } public boolean visit(final ClassInstanceCreation node) { Assert.isNotNull(node); if (fCreateInstanceField) { final AST ast= node.getAST(); final Type type= node.getType(); final ITypeBinding binding= type.resolveBinding(); if (binding != null && binding.getDeclaringClass() != null && !Bindings.equals(binding, fTypeBinding) && fSourceRewrite.getRoot().findDeclaringNode(binding) != null) { if (!Modifier.isStatic(binding.getModifiers())) { Expression expression= null; if (fCodeGenerationSettings.useKeywordThis || fEnclosingInstanceFieldName.equals(fNameForEnclosingInstanceConstructorParameter)) { final FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); expression= access; } else expression= ast.newSimpleName(fEnclosingInstanceFieldName); if (node.getExpression() != null) fSourceRewrite.getImportRemover().registerRemovedNode(node.getExpression()); fSourceRewrite.getASTRewrite().set(node, ClassInstanceCreation.EXPRESSION_PROPERTY, expression, fGroup); } else addTypeQualification(type, fSourceRewrite, fGroup); } } return true; } public boolean visit(final QualifiedType node) { Assert.isNotNull(node); return false; } public boolean visit(final SimpleType node) { Assert.isNotNull(node); if (!(node.getParent() instanceof ClassInstanceCreation)) { final ITypeBinding binding= node.resolveBinding(); if (binding != null) { final ITypeBinding declaring= binding.getDeclaringClass(); if (declaring != null && !Bindings.equals(declaring, fTypeBinding.getDeclaringClass()) && !Bindings.equals(binding, fTypeBinding) && fSourceRewrite.getRoot().findDeclaringNode(binding) != null && Modifier.isStatic(binding.getModifiers())) addTypeQualification(node, fSourceRewrite, fGroup); } } return super.visit(node); } public boolean visit(final ThisExpression node) { Assert.isNotNull(node); final Name name= node.getQualifier(); if (name != null && name.isSimpleName()) { final AST ast= node.getAST(); Expression expression= null; if (fCodeGenerationSettings.useKeywordThis || fEnclosingInstanceFieldName.equals(fNameForEnclosingInstanceConstructorParameter)) { final FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); expression= access; } else expression= ast.newSimpleName(fEnclosingInstanceFieldName); fSourceRewrite.getASTRewrite().replace(node, expression, null); } return super.visit(node); } } private static boolean containsNonStatic(FieldAccess[] accesses) { for (int i= 0; i < accesses.length; i++) { if (!isStatic(accesses[i])) return true; } return false; } private static boolean containsNonStatic(FunctionInvocation[] invocations) { for (int i= 0; i < invocations.length; i++) { if (!isStatic(invocations[i])) return true; } return false; } private static boolean containsNonStatic(SimpleName[] fieldNames) { for (int i= 0; i < fieldNames.length; i++) { if (!isStaticFieldName(fieldNames[i])) return true; } return false; } private static boolean containsStatusEntry(final RefactoringStatus status, final RefactoringStatusEntry other) { return status.getEntries(new IRefactoringStatusEntryComparator() { public final int compare(final RefactoringStatusEntry entry1, final RefactoringStatusEntry entry2) { return entry1.getMessage().compareTo(entry2.getMessage()); } }, other).length > 0; } private static AbstractTypeDeclaration findTypeDeclaration(IType enclosing, AbstractTypeDeclaration[] declarations) { String typeName= enclosing.getElementName(); for (int i= 0; i < declarations.length; i++) { AbstractTypeDeclaration declaration= declarations[i]; if (declaration.getName().getIdentifier().equals(typeName)) return declaration; } return null; } private static AbstractTypeDeclaration findTypeDeclaration(IType type, JavaScriptUnit unit) { final List types= getDeclaringTypes(type); types.add(type); AbstractTypeDeclaration[] declarations= (AbstractTypeDeclaration[]) unit.types().toArray(new AbstractTypeDeclaration[unit.types().size()]); AbstractTypeDeclaration declaration= null; for (final Iterator iterator= types.iterator(); iterator.hasNext();) { IType enclosing= (IType) iterator.next(); declaration= findTypeDeclaration(enclosing, declarations); Assert.isNotNull(declaration); declarations= getAbstractTypeDeclarations(declaration); } Assert.isNotNull(declaration); return declaration; } public static AbstractTypeDeclaration[] getAbstractTypeDeclarations(final AbstractTypeDeclaration declaration) { int typeCount= 0; for (Iterator iterator= declaration.bodyDeclarations().listIterator(); iterator.hasNext();) { if (iterator.next() instanceof AbstractTypeDeclaration) { typeCount++; } } AbstractTypeDeclaration[] declarations= new AbstractTypeDeclaration[typeCount]; int next= 0; for (final Iterator iterator= declaration.bodyDeclarations().listIterator(); iterator.hasNext();) { Object object= iterator.next(); if (object instanceof AbstractTypeDeclaration) { declarations[next++]= (AbstractTypeDeclaration) object; } } return declarations; } private static ITypeBinding getDeclaringTypeBinding(FieldAccess fieldAccess) { IVariableBinding varBinding= fieldAccess.resolveFieldBinding(); if (varBinding == null) return null; return varBinding.getDeclaringClass(); } private static ITypeBinding getDeclaringTypeBinding(FunctionInvocation methodInvocation) { IFunctionBinding binding= methodInvocation.resolveMethodBinding(); if (binding == null) return null; return binding.getDeclaringClass(); } // List of ITypes private static List getDeclaringTypes(IType type) { IType declaringType= type.getDeclaringType(); if (declaringType == null) return new ArrayList(0); List result= getDeclaringTypes(declaringType); result.add(declaringType); return result; } private static String[] getFieldNames(IType type) { try { IField[] fields= type.getFields(); List result= new ArrayList(fields.length); for (int i= 0; i < fields.length; i++) { result.add(fields[i].getElementName()); } return (String[]) result.toArray(new String[result.size()]); } catch (JavaScriptModelException e) { return null; } } private static Set getMergedSet(Set s1, Set s2) { Set result= new HashSet(); result.addAll(s1); result.addAll(s2); return result; } private static String[] getParameterNamesOfAllConstructors(IType type) throws JavaScriptModelException { IFunction[] constructors= JavaElementUtil.getAllConstructors(type); Set result= new HashSet(); for (int i= 0; i < constructors.length; i++) { result.addAll(Arrays.asList(constructors[i].getParameterNames())); } return (String[]) result.toArray(new String[result.size()]); } private static ASTNode[] getReferenceNodesIn(JavaScriptUnit cuNode, Map references, IJavaScriptUnit cu) { SearchMatch[] results= (SearchMatch[]) references.get(cu); if (results == null) return new ASTNode[0]; return ASTNodeSearchUtil.getAstNodes(results, cuNode); } private static boolean isCorrespondingTypeBinding(ITypeBinding binding, IType type) { if (binding == null) return false; return Bindings.getFullyQualifiedName(binding).equals(JavaElementUtil.createSignature(type)); } private static boolean isStatic(FieldAccess access) { IVariableBinding fieldBinding= access.resolveFieldBinding(); if (fieldBinding == null) return false; return JdtFlags.isStatic(fieldBinding); } private static boolean isStatic(FunctionInvocation invocation) { IFunctionBinding methodBinding= invocation.resolveMethodBinding(); if (methodBinding == null) return false; return JdtFlags.isStatic(methodBinding); } private static boolean isStaticFieldName(SimpleName name) { IBinding binding= name.resolveBinding(); if (!(binding instanceof IVariableBinding)) return false; IVariableBinding variableBinding= (IVariableBinding) binding; if (!variableBinding.isField()) return false; return JdtFlags.isStatic(variableBinding); } private TextChangeManager fChangeManager; private CodeGenerationSettings fCodeGenerationSettings; private boolean fCreateInstanceField; private String fEnclosingInstanceFieldName; private boolean fIsInstanceFieldCreationMandatory; private boolean fIsInstanceFieldCreationPossible; private boolean fMarkInstanceFieldAsFinal; private String fNameForEnclosingInstanceConstructorParameter; private String fNewSourceOfInputType; private CompilationUnitRewrite fSourceRewrite; private Collection fStaticImports; private IType fType; private String fQualifiedTypeName; private Collection fTypeImports; /** * Creates a new move inner to top refactoring. * @param type the type, or <code>null</code> if invoked by scripting * @param settings the code generation settings, or <code>null</code> if invoked by scripting * @throws JavaScriptModelException */ public MoveInnerToTopRefactoring(IType type, CodeGenerationSettings settings) throws JavaScriptModelException { fType= type; fCodeGenerationSettings= settings; fMarkInstanceFieldAsFinal= true; // default if (fType != null) initialize(); } private void initialize() throws JavaScriptModelException { fQualifiedTypeName= JavaModelUtil.concatenateName(fType.getPackageFragment().getElementName(), fType.getElementName()); fEnclosingInstanceFieldName= getInitialNameForEnclosingInstanceField(); fSourceRewrite= new CompilationUnitRewrite(fType.getJavaScriptUnit()); fIsInstanceFieldCreationPossible= !(JdtFlags.isStatic(fType)); fIsInstanceFieldCreationMandatory= fIsInstanceFieldCreationPossible && isInstanceFieldCreationMandatory(); fCreateInstanceField= fIsInstanceFieldCreationMandatory; } private void addEnclosingInstanceDeclaration(final AbstractTypeDeclaration declaration, final ASTRewrite rewrite) throws CoreException { Assert.isNotNull(declaration); Assert.isNotNull(rewrite); final AST ast= declaration.getAST(); final VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment(); fragment.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); final FieldDeclaration newField= ast.newFieldDeclaration(fragment); newField.modifiers().addAll(ASTNodeFactory.newModifiers(ast, getEnclosingInstanceAccessModifiers())); newField.setType(createEnclosingType(ast)); final String comment= CodeGeneration.getFieldComment(fType.getJavaScriptUnit(), declaration.getName().getIdentifier(), fEnclosingInstanceFieldName, StubUtility.getLineDelimiterUsed(fType.getJavaScriptProject())); if (comment != null && comment.length() > 0) { final JSdoc doc= (JSdoc) rewrite.createStringPlaceholder(comment, ASTNode.JSDOC); newField.setJavadoc(doc); } rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertFirst(newField, null); } private void addImportsToTargetUnit(final IJavaScriptUnit targetUnit, final IProgressMonitor monitor) throws CoreException, JavaScriptModelException { monitor.beginTask("", 2); //$NON-NLS-1$ try { ImportRewrite rewrite= StubUtility.createImportRewrite(targetUnit, true); if (fTypeImports != null) { ITypeBinding type= null; for (final Iterator iterator= fTypeImports.iterator(); iterator.hasNext();) { type= (ITypeBinding) iterator.next(); rewrite.addImport(type); } } if (fStaticImports != null) { IBinding binding= null; for (final Iterator iterator= fStaticImports.iterator(); iterator.hasNext();) { binding= (IBinding) iterator.next(); rewrite.addStaticImport(binding); } } fTypeImports= null; fStaticImports= null; TextEdit edits= rewrite.rewriteImports(new SubProgressMonitor(monitor, 1)); JavaModelUtil.applyEdit(targetUnit, edits, false, new SubProgressMonitor(monitor, 1)); } finally { monitor.done(); } } private void addInheritedTypeQualifications(final AbstractTypeDeclaration declaration, final CompilationUnitRewrite targetRewrite, final TextEditGroup group) { Assert.isNotNull(declaration); Assert.isNotNull(targetRewrite); final JavaScriptUnit unit= (JavaScriptUnit) declaration.getRoot(); final ITypeBinding binding= declaration.resolveBinding(); if (binding != null) { Type type= null; if (declaration instanceof TypeDeclaration) { type= ((TypeDeclaration) declaration).getSuperclassType(); if (type != null && unit.findDeclaringNode(binding) != null) addTypeQualification(type, targetRewrite, group); } } } private void addParameterToConstructor(final ASTRewrite rewrite, final FunctionDeclaration declaration) throws JavaScriptModelException { Assert.isNotNull(rewrite); Assert.isNotNull(declaration); final AST ast= declaration.getAST(); final String name= getNameForEnclosingInstanceConstructorParameter(); final SingleVariableDeclaration variable= ast.newSingleVariableDeclaration(); variable.setType(createEnclosingType(ast)); variable.setName(ast.newSimpleName(name)); rewrite.getListRewrite(declaration, FunctionDeclaration.PARAMETERS_PROPERTY).insertFirst(variable, null); JavadocUtil.addParamJavadoc(name, declaration, rewrite, fType.getJavaScriptProject(), null); } private void addSimpleTypeQualification(final CompilationUnitRewrite targetRewrite, final ITypeBinding declaring, final SimpleType simpleType, final TextEditGroup group) { Assert.isNotNull(targetRewrite); Assert.isNotNull(declaring); Assert.isNotNull(simpleType); final AST ast= targetRewrite.getRoot().getAST(); if (!(simpleType.getName() instanceof QualifiedName)) { targetRewrite.getASTRewrite().replace(simpleType, ast.newQualifiedType(targetRewrite.getImportRewrite().addImport(declaring, ast), ast.newSimpleName(simpleType.getName().getFullyQualifiedName())), group); targetRewrite.getImportRemover().registerRemovedNode(simpleType); } } private void addTypeQualification(final Type type, final CompilationUnitRewrite targetRewrite, final TextEditGroup group) { Assert.isNotNull(type); Assert.isNotNull(targetRewrite); final ITypeBinding binding= type.resolveBinding(); if (binding != null) { final ITypeBinding declaring= binding.getDeclaringClass(); if (declaring != null) { if (type instanceof SimpleType) { final SimpleType simpleType= (SimpleType) type; addSimpleTypeQualification(targetRewrite, declaring, simpleType, group); } } } } private RefactoringStatus checkConstructorParameterNames() { RefactoringStatus result= new RefactoringStatus(); JavaScriptUnit cuNode= new RefactoringASTParser(AST.JLS3).parse(fType.getJavaScriptUnit(), false); FunctionDeclaration[] nodes= getConstructorDeclarationNodes(findTypeDeclaration(fType, cuNode)); for (int i= 0; i < nodes.length; i++) { FunctionDeclaration constructor= nodes[i]; for (Iterator iter= constructor.parameters().iterator(); iter.hasNext();) { SingleVariableDeclaration param= (SingleVariableDeclaration) iter.next(); if (fEnclosingInstanceFieldName.equals(param.getName().getIdentifier())) { String msg= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_name_used, new String[] { param.getName().getIdentifier(), fType.getElementName()}); result.addError(msg, JavaStatusContext.create(fType.getJavaScriptUnit(), param)); } } } return result; } public RefactoringStatus checkEnclosingInstanceName(String name) { if (!fCreateInstanceField) return new RefactoringStatus(); RefactoringStatus result= Checks.checkFieldName(name); if (!Checks.startsWithLowerCase(name)) result.addWarning(RefactoringCoreMessages.MoveInnerToTopRefactoring_names_start_lowercase); if (fType.getField(name).exists()) { Object[] keys= new String[] { name, fType.getElementName()}; String msg= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_already_declared, keys); result.addError(msg, JavaStatusContext.create(fType.getField(name))); } return result; } public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { pm.beginTask("", 2);//$NON-NLS-1$ try { RefactoringStatus result= new RefactoringStatus(); if (JdtFlags.isStatic(fType)) result.merge(checkEnclosingInstanceName(fEnclosingInstanceFieldName)); if (fType.getPackageFragment().getJavaScriptUnit((JavaModelUtil.getRenamedCUName(fType.getJavaScriptUnit(), fType.getElementName()))).exists()) { String message= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_compilation_Unit_exists, new String[] { (JavaModelUtil.getRenamedCUName(fType.getJavaScriptUnit(), fType.getElementName())), fType.getPackageFragment().getElementName()}); result.addFatalError(message); } result.merge(checkEnclosingInstanceName(fEnclosingInstanceFieldName)); result.merge(Checks.checkCompilationUnitName((JavaModelUtil.getRenamedCUName(fType.getJavaScriptUnit(), fType.getElementName())))); result.merge(checkConstructorParameterNames()); result.merge(checkTypeNameInPackage()); fChangeManager= createChangeManager(new SubProgressMonitor(pm, 1), result); result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), getValidationContext())); return result; } finally { pm.done(); } } public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException { return Checks.checkIfCuBroken(fType); } private RefactoringStatus checkTypeNameInPackage() throws JavaScriptModelException { IType type= Checks.findTypeInPackage(fType.getPackageFragment(), fType.getElementName()); if (type == null || !type.exists()) return null; String message= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_type_exists, new String[] { fType.getElementName(), fType.getPackageFragment().getElementName()}); return RefactoringStatus.createErrorStatus(message); } private Expression createAccessExpressionToEnclosingInstanceFieldText(ASTNode node, IBinding binding, AbstractTypeDeclaration declaration) { if (Modifier.isStatic(binding.getModifiers())) return node.getAST().newName(JavaModelUtil.getTypeQualifiedName(fType.getDeclaringType())); else if ((isInAnonymousTypeInsideInputType(node, declaration) || isInLocalTypeInsideInputType(node, declaration) || isInNonStaticMemberTypeInsideInputType(node, declaration))) return createQualifiedReadAccessExpressionForEnclosingInstance(node.getAST()); else return createReadAccessExpressionForEnclosingInstance(node.getAST()); } public Change createChange(final IProgressMonitor monitor) throws CoreException { monitor.beginTask(RefactoringCoreMessages.MoveInnerToTopRefactoring_creating_change, 1); final Map arguments= new HashMap(); String project= null; IJavaScriptProject javaProject= fType.getJavaScriptProject(); if (javaProject != null) project= javaProject.getElementName(); final String description= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_descriptor_description_short, fType.getElementName()); final String header= Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_descriptor_description, new String[] { JavaScriptElementLabels.getElementLabel(fType, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getElementLabel(fType.getParent(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}); final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_original_pattern, JavaScriptElementLabels.getElementLabel(fType, JavaScriptElementLabels.ALL_FULLY_QUALIFIED))); final boolean enclosing= fEnclosingInstanceFieldName != null && !"".equals(fEnclosingInstanceFieldName); //$NON-NLS-1$ if (enclosing) comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_field_pattern, fEnclosingInstanceFieldName)); if (fNameForEnclosingInstanceConstructorParameter != null && !"".equals(fNameForEnclosingInstanceConstructorParameter)) //$NON-NLS-1$ comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_parameter_pattern, fNameForEnclosingInstanceConstructorParameter)); if (enclosing && fMarkInstanceFieldAsFinal) comment.addSetting(RefactoringCoreMessages.MoveInnerToTopRefactoring_declare_final); final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.CONVERT_MEMBER_TYPE, project, description, comment.asString(), arguments, RefactoringDescriptor.MULTI_CHANGE | RefactoringDescriptor.STRUCTURAL_CHANGE | JavaScriptRefactoringDescriptor.JAR_REFACTORING | JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT); arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fType)); if (enclosing) arguments.put(ATTRIBUTE_FIELD_NAME, fEnclosingInstanceFieldName); if (fNameForEnclosingInstanceConstructorParameter != null && !"".equals(fNameForEnclosingInstanceConstructorParameter)) //$NON-NLS-1$ arguments.put(ATTRIBUTE_PARAMETER_NAME, fNameForEnclosingInstanceConstructorParameter); arguments.put(ATTRIBUTE_FIELD, Boolean.valueOf(fCreateInstanceField).toString()); arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fMarkInstanceFieldAsFinal).toString()); arguments.put(ATTRIBUTE_POSSIBLE, Boolean.valueOf(fIsInstanceFieldCreationPossible).toString()); arguments.put(ATTRIBUTE_MANDATORY, Boolean.valueOf(fIsInstanceFieldCreationMandatory).toString()); final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.MoveInnerToTopRefactoring_move_to_Top); result.addAll(fChangeManager.getAllChanges()); result.add(createCompilationUnitForMovedType(new SubProgressMonitor(monitor, 1))); return result; } private TextChangeManager createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException { Assert.isNotNull(monitor); Assert.isNotNull(status); final TextChangeManager manager= new TextChangeManager(); try { monitor.beginTask(RefactoringCoreMessages.MoveInnerToTopRefactoring_creating_preview, 4); final Map rewrites= new HashMap(2); fSourceRewrite.clearASTAndImportRewrites(); rewrites.put(fSourceRewrite.getCu(), fSourceRewrite); final MemberVisibilityAdjustor adjustor= new MemberVisibilityAdjustor(fType.getPackageFragment(), fType); adjustor.setRewrites(rewrites); adjustor.setVisibilitySeverity(RefactoringStatus.WARNING); adjustor.setFailureSeverity(RefactoringStatus.WARNING); adjustor.setStatus(status); adjustor.adjustVisibility(new SubProgressMonitor(monitor, 1)); final Map parameters= new LinkedHashMap(); final ITypeBinding[] bindings= new ITypeBinding[parameters.values().size()]; parameters.values().toArray(bindings); final Map typeReferences= createTypeReferencesMapping(new SubProgressMonitor(monitor, 1), status); Map constructorReferences= null; if (JdtFlags.isStatic(fType)) constructorReferences= new HashMap(0); else constructorReferences= createConstructorReferencesMapping(new SubProgressMonitor(monitor, 1), status); if (fCreateInstanceField) { // must increase visibility of all member types up // to the top level type to allow this IType type= fType; ModifierKeyword keyword= null; while ( (type= type.getDeclaringType()) != null) { if ((!adjustor.getAdjustments().containsKey(type)) && (Modifier.isPrivate(type.getFlags()))) adjustor.getAdjustments().put(type, new OutgoingMemberVisibilityAdjustment(type, keyword, RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_type_warning, new String[] { MemberVisibilityAdjustor.getLabel(type), MemberVisibilityAdjustor.getLabel(keyword) }), JavaStatusContext.create(type.getJavaScriptUnit(), type.getSourceRange())))); } } monitor.worked(1); for (final Iterator iterator= getMergedSet(typeReferences.keySet(), constructorReferences.keySet()).iterator(); iterator.hasNext();) { final IJavaScriptUnit unit= (IJavaScriptUnit) iterator.next(); final CompilationUnitRewrite targetRewrite= getCompilationUnitRewrite(unit); createCompilationUnitRewrite(bindings, targetRewrite, typeReferences, constructorReferences, adjustor.getAdjustments().containsKey(fType), fType.getJavaScriptUnit(), unit, false, status, monitor); if (unit.equals(fType.getJavaScriptUnit())) { try { adjustor.setStatus(new RefactoringStatus()); adjustor.rewriteVisibility(targetRewrite.getCu(), new SubProgressMonitor(monitor, 1)); } finally { adjustor.setStatus(status); } fNewSourceOfInputType= createNewSource(targetRewrite, unit); targetRewrite.clearASTAndImportRewrites(); createCompilationUnitRewrite(bindings, targetRewrite, typeReferences, constructorReferences, adjustor.getAdjustments().containsKey(fType), fType.getJavaScriptUnit(), unit, true, status, monitor); } adjustor.rewriteVisibility(targetRewrite.getCu(), new SubProgressMonitor(monitor, 1)); manager.manage(unit, targetRewrite.createChange()); } } finally { monitor.done(); } return manager; } private Change createCompilationUnitForMovedType(IProgressMonitor pm) throws CoreException { IJavaScriptUnit newCuWC= null; try { newCuWC= fType.getPackageFragment().getJavaScriptUnit(JavaModelUtil.getRenamedCUName(fType.getJavaScriptUnit(), fType.getElementName())).getWorkingCopy(null); String source= createSourceForNewCu(newCuWC, pm); return new CreateCompilationUnitChange(fType.getPackageFragment().getJavaScriptUnit(JavaModelUtil.getRenamedCUName(fType.getJavaScriptUnit(), fType.getElementName())), source, null); } finally { if (newCuWC != null) newCuWC.discardWorkingCopy(); } } private void createCompilationUnitRewrite(final ITypeBinding[] parameters, final CompilationUnitRewrite targetRewrite, final Map typeReferences, final Map constructorReferences, boolean visibilityWasAdjusted, final IJavaScriptUnit sourceUnit, final IJavaScriptUnit targetUnit, final boolean remove, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException { Assert.isNotNull(parameters); Assert.isNotNull(targetRewrite); Assert.isNotNull(typeReferences); Assert.isNotNull(constructorReferences); Assert.isNotNull(sourceUnit); Assert.isNotNull(targetUnit); final JavaScriptUnit root= targetRewrite.getRoot(); final ASTRewrite rewrite= targetRewrite.getASTRewrite(); if (targetUnit.equals(sourceUnit)) { final AbstractTypeDeclaration declaration= findTypeDeclaration(fType, root); final TextEditGroup qualifierGroup= fSourceRewrite.createGroupDescription(RefactoringCoreMessages.MoveInnerToTopRefactoring_change_qualifier); ITypeBinding binding= declaration.resolveBinding(); if (!remove) { if (!JdtFlags.isStatic(fType) && fCreateInstanceField) { if (JavaElementUtil.getAllConstructors(fType).length == 0) createConstructor(declaration, rewrite); else modifyConstructors(declaration, rewrite); addInheritedTypeQualifications(declaration, targetRewrite, qualifierGroup); addEnclosingInstanceDeclaration(declaration, rewrite); } fTypeImports= new HashSet(); fStaticImports= new HashSet(); ImportRewriteUtil.collectImports(fType.getJavaScriptProject(), declaration, fTypeImports, fStaticImports, false); if (binding != null) fTypeImports.remove(binding); } modifyAccessToEnclosingInstance(targetRewrite, declaration, status, monitor); if (binding != null) { modifyInterfaceMemberModifiers(binding); final ITypeBinding declaring= binding.getDeclaringClass(); if (declaring != null) declaration.accept(new TypeReferenceQualifier(binding, null)); } final TextEditGroup groupMove= targetRewrite.createGroupDescription(RefactoringCoreMessages.MoveInnerToTopRefactoring_change_label); if (remove) { rewrite.remove(declaration, groupMove); targetRewrite.getImportRemover().registerRemovedNode(declaration); } else { // Bug 101017/96308: Rewrite the visibility of the element to be // moved and add a warning. // Note that this cannot be done in the MemberVisibilityAdjustor, as the private and // static flags must always be cleared when moving to new type. int newFlags= JdtFlags.clearFlag(Modifier.STATIC, declaration.getModifiers()); if (!visibilityWasAdjusted) { if (Modifier.isPrivate(declaration.getModifiers()) || Modifier.isProtected(declaration.getModifiers())) { newFlags= JdtFlags.clearFlag(Modifier.PROTECTED | Modifier.PRIVATE, newFlags); final RefactoringStatusEntry entry= new RefactoringStatusEntry(RefactoringStatus.WARNING, Messages.format(RefactoringCoreMessages.MoveInnerToTopRefactoring_change_visibility_type_warning, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}), JavaStatusContext.create(fSourceRewrite.getCu())); if (!containsStatusEntry(status, entry)) status.addEntry(entry); } } ModifierRewrite.create(rewrite, declaration).setModifiers(newFlags, groupMove); } } ASTNode[] references= getReferenceNodesIn(root, typeReferences, targetUnit); for (int index= 0; index < references.length; index++) updateTypeReference(parameters, references[index], targetRewrite, targetUnit); references= getReferenceNodesIn(root, constructorReferences, targetUnit); for (int index= 0; index < references.length; index++) updateConstructorReference(parameters, references[index], targetRewrite, targetUnit); } private void createConstructor(final AbstractTypeDeclaration declaration, final ASTRewrite rewrite) throws CoreException { Assert.isNotNull(declaration); Assert.isNotNull(rewrite); final AST ast= declaration.getAST(); final FunctionDeclaration constructor= ast.newFunctionDeclaration(); constructor.setConstructor(true); constructor.setName(ast.newSimpleName(declaration.getName().getIdentifier())); final String comment= CodeGeneration.getMethodComment(fType.getJavaScriptUnit(), fType.getElementName(), fType.getElementName(), getNewConstructorParameterNames(), new String[0], null, null, StubUtility.getLineDelimiterUsed(fType.getJavaScriptProject())); if (comment != null && comment.length() > 0) { final JSdoc doc= (JSdoc) rewrite.createStringPlaceholder(comment, ASTNode.JSDOC); constructor.setJavadoc(doc); } if (fCreateInstanceField) { final SingleVariableDeclaration variable= ast.newSingleVariableDeclaration(); final String name= getNameForEnclosingInstanceConstructorParameter(); variable.setName(ast.newSimpleName(name)); variable.setType(createEnclosingType(ast)); constructor.parameters().add(variable); final Block body= ast.newBlock(); final Assignment assignment= ast.newAssignment(); if (fCodeGenerationSettings.useKeywordThis || fEnclosingInstanceFieldName.equals(fNameForEnclosingInstanceConstructorParameter)) { final FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); assignment.setLeftHandSide(access); } else assignment.setLeftHandSide(ast.newSimpleName(fEnclosingInstanceFieldName)); assignment.setRightHandSide(ast.newSimpleName(name)); final Statement statement= ast.newExpressionStatement(assignment); body.statements().add(statement); constructor.setBody(body); } else constructor.setBody(ast.newBlock()); rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertFirst(constructor, null); } // Map<IJavaScriptUnit, SearchMatch[]> private Map createConstructorReferencesMapping(IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException { SearchResultGroup[] groups= ConstructorReferenceFinder.getConstructorReferences(fType, pm, status); Map result= new HashMap(); for (int i= 0; i < groups.length; i++) { SearchResultGroup group= groups[i]; IJavaScriptUnit cu= group.getCompilationUnit(); if (cu == null) continue; result.put(cu, group.getSearchResults()); } return result; } private Expression createEnclosingInstanceCreationString(final ASTNode node, final IJavaScriptUnit cu) throws JavaScriptModelException { Assert.isTrue((node instanceof ClassInstanceCreation) || (node instanceof SuperConstructorInvocation)); Assert.isNotNull(cu); Expression expression= null; if (node instanceof ClassInstanceCreation) expression= ((ClassInstanceCreation) node).getExpression(); else expression= ((SuperConstructorInvocation) node).getExpression(); final AST ast= node.getAST(); if (expression != null) return expression; else if (JdtFlags.isStatic(fType)) return null; else if (isInsideSubclassOfDeclaringType(node)) return ast.newThisExpression(); else if ((node.getStartPosition() >= fType.getSourceRange().getOffset() && ASTNodes.getExclusiveEnd(node) <= fType.getSourceRange().getOffset() + fType.getSourceRange().getLength())) { if (fCodeGenerationSettings.useKeywordThis || fEnclosingInstanceFieldName.equals(fNameForEnclosingInstanceConstructorParameter)) { final FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); return access; } else return ast.newSimpleName(fEnclosingInstanceFieldName); } else if (isInsideTypeNestedInDeclaringType(node)) { final ThisExpression qualified= ast.newThisExpression(); qualified.setQualifier(ast.newSimpleName(fType.getDeclaringType().getElementName())); return qualified; } return null; } private Type createEnclosingType(final AST ast) throws JavaScriptModelException { Assert.isNotNull(ast); final Type type= ASTNodeFactory.newType(ast, JavaModelUtil.getTypeQualifiedName(fType.getDeclaringType())); return type; } private String createNewSource(final CompilationUnitRewrite targetRewrite, final IJavaScriptUnit unit) throws CoreException, JavaScriptModelException { Assert.isNotNull(targetRewrite); Assert.isNotNull(unit); TextChange change= targetRewrite.createChange(); if (change == null) change= new CompilationUnitChange("", unit); //$NON-NLS-1$ final String source= change.getPreviewContent(new NullProgressMonitor()); final ASTParser parser= ASTParser.newParser(AST.JLS3); parser.setProject(fType.getJavaScriptProject()); parser.setResolveBindings(false); parser.setSource(source.toCharArray()); final AbstractTypeDeclaration declaration= findTypeDeclaration(fType, (JavaScriptUnit) parser.createAST(null)); return source.substring(declaration.getStartPosition(), ASTNodes.getExclusiveEnd(declaration)); } private Expression createQualifiedReadAccessExpressionForEnclosingInstance(AST ast) { ThisExpression expression= ast.newThisExpression(); expression.setQualifier(ast.newName(new String[] { fType.getElementName()})); FieldAccess access= ast.newFieldAccess(); access.setExpression(expression); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); return access; } private Expression createReadAccessExpressionForEnclosingInstance(AST ast) { if (fCodeGenerationSettings.useKeywordThis || fEnclosingInstanceFieldName.equals(fNameForEnclosingInstanceConstructorParameter)) { final FieldAccess access= ast.newFieldAccess(); access.setExpression(ast.newThisExpression()); access.setName(ast.newSimpleName(fEnclosingInstanceFieldName)); return access; } return ast.newSimpleName(fEnclosingInstanceFieldName); } private String createSourceForNewCu(final IJavaScriptUnit unit, final IProgressMonitor monitor) throws CoreException { Assert.isNotNull(unit); Assert.isNotNull(monitor); try { monitor.beginTask("", 2); //$NON-NLS-1$ final String separator= StubUtility.getLineDelimiterUsed(fType.getJavaScriptProject()); final String block= getAlignedSourceBlock(unit, fNewSourceOfInputType); String content= CodeGeneration.getCompilationUnitContent(unit, null, block, separator); if (content == null || block.startsWith("/*") || block.startsWith("//")) { //$NON-NLS-1$//$NON-NLS-2$ final StringBuffer buffer= new StringBuffer(); if (!fType.getPackageFragment().isDefaultPackage()) { buffer.append("package ").append(fType.getPackageFragment().getElementName()).append(';'); //$NON-NLS-1$ } buffer.append(separator).append(separator); buffer.append(block); content= buffer.toString(); } unit.getBuffer().setContents(content); addImportsToTargetUnit(unit, new SubProgressMonitor(monitor, 1)); } finally { monitor.done(); } return unit.getSource(); } // Map<IJavaScriptUnit, SearchMatch[]> private Map createTypeReferencesMapping(IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException { final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(fType, IJavaScriptSearchConstants.ALL_OCCURRENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE)); engine.setFiltering(true, true); engine.setScope(RefactoringScopeFactory.create(fType)); engine.setStatus(status); engine.searchPattern(new SubProgressMonitor(pm, 1)); final SearchResultGroup[] groups= (SearchResultGroup[]) engine.getResults(); Map result= new HashMap(); for (int i= 0; i < groups.length; i++) { SearchResultGroup group= groups[i]; IJavaScriptUnit cu= group.getCompilationUnit(); if (cu == null) continue; result.put(cu, group.getSearchResults()); } return result; } private String getAlignedSourceBlock(final IJavaScriptUnit unit, final String block) { Assert.isNotNull(block); final String[] lines= Strings.convertIntoLines(block); Strings.trimIndentation(lines, unit.getJavaScriptProject(), false); return Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(fType.getJavaScriptProject())); } private CompilationUnitRewrite getCompilationUnitRewrite(final IJavaScriptUnit unit) { Assert.isNotNull(unit); if (unit.equals(fType.getJavaScriptUnit())) return fSourceRewrite; return new CompilationUnitRewrite(unit); } private FunctionDeclaration[] getConstructorDeclarationNodes(final AbstractTypeDeclaration declaration) { if (declaration instanceof TypeDeclaration) { final FunctionDeclaration[] declarations= ((TypeDeclaration) declaration).getMethods(); final List result= new ArrayList(2); for (int index= 0; index < declarations.length; index++) { if (declarations[index].isConstructor()) result.add(declarations[index]); } return (FunctionDeclaration[]) result.toArray(new FunctionDeclaration[result.size()]); } return new FunctionDeclaration[] {}; } public boolean getCreateInstanceField() { return fCreateInstanceField; } private int getEnclosingInstanceAccessModifiers() { if (fMarkInstanceFieldAsFinal) return Modifier.PRIVATE | Modifier.FINAL; else return Modifier.PRIVATE; } public String getEnclosingInstanceName() { return fEnclosingInstanceFieldName; } private String getInitialNameForEnclosingInstanceField() { IType enclosingType= fType.getDeclaringType(); if (enclosingType == null) return ""; //$NON-NLS-1$ String[] suggestedNames= StubUtility.getFieldNameSuggestions(fType.getDeclaringType(), getEnclosingInstanceAccessModifiers(), getFieldNames(fType)); if (suggestedNames.length > 0) return suggestedNames[0]; String name= enclosingType.getElementName(); if (name.equals("")) //$NON-NLS-1$ return ""; //$NON-NLS-1$ return Character.toLowerCase(name.charAt(0)) + name.substring(1); } public IType getInputType() { return fType; } /* * @see org.eclipse.wst.jsdt.internal.corext.refactoring.base.IRefactoring#getName() */ public String getName() { return RefactoringCoreMessages.MoveInnerToTopRefactoring_name; } private String getNameForEnclosingInstanceConstructorParameter() throws JavaScriptModelException { if (fNameForEnclosingInstanceConstructorParameter != null) return fNameForEnclosingInstanceConstructorParameter; String[] suggestedNames= StubUtility.getArgumentNameSuggestions(fType.getDeclaringType(), getParameterNamesOfAllConstructors(fType)); if (suggestedNames.length > 0) fNameForEnclosingInstanceConstructorParameter= suggestedNames[0]; else fNameForEnclosingInstanceConstructorParameter= fEnclosingInstanceFieldName; return fNameForEnclosingInstanceConstructorParameter; } private String[] getNewConstructorParameterNames() throws JavaScriptModelException { if (!fCreateInstanceField) return new String[0]; return new String[] { getNameForEnclosingInstanceConstructorParameter()}; } private ASTNode getNewQualifiedNameNode(ITypeBinding[] parameters, Name name) { final AST ast= name.getAST(); boolean raw= false; final ITypeBinding binding= name.resolveTypeBinding(); return ast.newName(fQualifiedTypeName); } private ASTNode getNewUnqualifiedTypeNode(ITypeBinding[] parameters, Name name) { final AST ast= name.getAST(); boolean raw= false; final ITypeBinding binding= name.resolveTypeBinding(); return ast.newSimpleType(ast.newSimpleName(fType.getElementName())); } private boolean insertExpressionAsParameter(ClassInstanceCreation cic, ASTRewrite rewrite, IJavaScriptUnit cu, TextEditGroup group) throws JavaScriptModelException { final Expression expression= createEnclosingInstanceCreationString(cic, cu); if (expression == null) return false; rewrite.getListRewrite(cic, ClassInstanceCreation.ARGUMENTS_PROPERTY).insertFirst(expression, group); return true; } private boolean insertExpressionAsParameter(SuperConstructorInvocation sci, ASTRewrite rewrite, IJavaScriptUnit cu, TextEditGroup group) throws JavaScriptModelException { final Expression expression= createEnclosingInstanceCreationString(sci, cu); if (expression == null) return false; rewrite.getListRewrite(sci, SuperConstructorInvocation.ARGUMENTS_PROPERTY).insertFirst(expression, group); return true; } public boolean isCreatingInstanceFieldMandatory() { return fIsInstanceFieldCreationMandatory; } public boolean isCreatingInstanceFieldPossible() { return fIsInstanceFieldCreationPossible; } private boolean isInAnonymousTypeInsideInputType(ASTNode node, AbstractTypeDeclaration declaration) { final AnonymousClassDeclaration anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(node, AnonymousClassDeclaration.class); return anonymous != null && ASTNodes.isParent(anonymous, declaration); } private boolean isInLocalTypeInsideInputType(ASTNode node, AbstractTypeDeclaration declaration) { final TypeDeclarationStatement statement= (TypeDeclarationStatement) ASTNodes.getParent(node, TypeDeclarationStatement.class); return statement != null && ASTNodes.isParent(statement, declaration); } private boolean isInNonStaticMemberTypeInsideInputType(ASTNode node, AbstractTypeDeclaration declaration) { final AbstractTypeDeclaration nested= (AbstractTypeDeclaration) ASTNodes.getParent(node, AbstractTypeDeclaration.class); return nested != null && !declaration.equals(nested) && !Modifier.isStatic(nested.getFlags()) && ASTNodes.isParent(nested, declaration); } private boolean isInsideSubclassOfDeclaringType(ASTNode node) { Assert.isTrue((node instanceof ClassInstanceCreation) || (node instanceof SuperConstructorInvocation)); final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(node, AbstractTypeDeclaration.class); Assert.isNotNull(declaration); final AnonymousClassDeclaration anonymous= (AnonymousClassDeclaration) ASTNodes.getParent(node, AnonymousClassDeclaration.class); boolean isAnonymous= anonymous != null && ASTNodes.isParent(anonymous, declaration); if (isAnonymous) return anonymous != null && isSubclassBindingOfEnclosingType(anonymous.resolveBinding()); return isSubclassBindingOfEnclosingType(declaration.resolveBinding()); } private boolean isInsideTypeNestedInDeclaringType(ASTNode node) { Assert.isTrue((node instanceof ClassInstanceCreation) || (node instanceof SuperConstructorInvocation)); final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(node, AbstractTypeDeclaration.class); Assert.isNotNull(declaration); ITypeBinding enclosing= declaration.resolveBinding(); while (enclosing != null) { if (isCorrespondingTypeBinding(enclosing, fType.getDeclaringType())) return true; enclosing= enclosing.getDeclaringClass(); } return false; } private boolean isInstanceFieldCreationMandatory() throws JavaScriptModelException { final MemberAccessNodeCollector collector= new MemberAccessNodeCollector(fType.getDeclaringType().newSupertypeHierarchy(new NullProgressMonitor())); findTypeDeclaration(fType, fSourceRewrite.getRoot()).accept(collector); return containsNonStatic(collector.getFieldAccesses()) || containsNonStatic(collector.getMethodInvocations()) || containsNonStatic(collector.getSimpleFieldNames()); } public boolean isInstanceFieldMarkedFinal() { return fMarkInstanceFieldAsFinal; } private boolean isSubclassBindingOfEnclosingType(ITypeBinding binding) { while (binding != null) { if (isCorrespondingTypeBinding(binding, fType.getDeclaringType())) return true; binding= binding.getSuperclass(); } return false; } /* * This method qualifies accesses from within the moved type to the (now former) enclosed * type of the moved type. Note that all visibility changes have already been scheduled * in the visibility adjustor. */ private void modifyAccessToEnclosingInstance(final CompilationUnitRewrite targetRewrite, final AbstractTypeDeclaration declaration, final RefactoringStatus status, final IProgressMonitor monitor) throws JavaScriptModelException { Assert.isNotNull(targetRewrite); Assert.isNotNull(declaration); Assert.isNotNull(monitor); final Set handledMethods= new HashSet(); final Set handledFields= new HashSet(); final MemberAccessNodeCollector collector= new MemberAccessNodeCollector(fType.getDeclaringType().newSupertypeHierarchy(new SubProgressMonitor(monitor, 1))); declaration.accept(collector); modifyAccessToMethodsFromEnclosingInstance(targetRewrite, handledMethods, collector.getMethodInvocations(), declaration, status); modifyAccessToFieldsFromEnclosingInstance(targetRewrite, handledFields, collector.getFieldAccesses(), declaration, status); modifyAccessToFieldsFromEnclosingInstance(targetRewrite, handledFields, collector.getSimpleFieldNames(), declaration, status); } private void modifyAccessToFieldsFromEnclosingInstance(CompilationUnitRewrite targetRewrite, Set handledFields, FieldAccess[] fieldAccesses, AbstractTypeDeclaration declaration, RefactoringStatus status) { FieldAccess access= null; for (int index= 0; index < fieldAccesses.length; index++) { access= fieldAccesses[index]; Assert.isNotNull(access.getExpression()); if (!(access.getExpression() instanceof ThisExpression) || (!(((ThisExpression) access.getExpression()).getQualifier() != null))) continue; final IVariableBinding binding= access.resolveFieldBinding(); if (binding != null) { targetRewrite.getASTRewrite().replace(access.getExpression(), createAccessExpressionToEnclosingInstanceFieldText(access, binding, declaration), null); targetRewrite.getImportRemover().registerRemovedNode(access.getExpression()); } } } private void modifyAccessToFieldsFromEnclosingInstance(CompilationUnitRewrite targetRewrite, Set handledFields, SimpleName[] simpleNames, AbstractTypeDeclaration declaration, RefactoringStatus status) { IBinding binding= null; SimpleName simpleName= null; IVariableBinding variable= null; for (int index= 0; index < simpleNames.length; index++) { simpleName= simpleNames[index]; binding= simpleName.resolveBinding(); if (binding != null && binding instanceof IVariableBinding && !(simpleName.getParent() instanceof FieldAccess)) { variable= (IVariableBinding) binding; final FieldAccess access= simpleName.getAST().newFieldAccess(); access.setExpression(createAccessExpressionToEnclosingInstanceFieldText(simpleName, variable, declaration)); access.setName(simpleName.getAST().newSimpleName(simpleName.getIdentifier())); targetRewrite.getASTRewrite().replace(simpleName, access, null); targetRewrite.getImportRemover().registerRemovedNode(simpleName); } } } private void modifyAccessToMethodsFromEnclosingInstance(CompilationUnitRewrite targetRewrite, Set handledMethods, FunctionInvocation[] methodInvocations, AbstractTypeDeclaration declaration, RefactoringStatus status) { IFunctionBinding binding= null; FunctionInvocation invocation= null; for (int index= 0; index < methodInvocations.length; index++) { invocation= methodInvocations[index]; binding= invocation.resolveMethodBinding(); if (binding != null) { final Expression target= invocation.getExpression(); if (target == null) { final Expression expression= createAccessExpressionToEnclosingInstanceFieldText(invocation, binding, declaration); targetRewrite.getASTRewrite().set(invocation, FunctionInvocation.EXPRESSION_PROPERTY, expression, null); } else { if (!(invocation.getExpression() instanceof ThisExpression) || !(((ThisExpression) invocation.getExpression()).getQualifier() != null)) continue; targetRewrite.getASTRewrite().replace(target, createAccessExpressionToEnclosingInstanceFieldText(invocation, binding, declaration), null); targetRewrite.getImportRemover().registerRemovedNode(target); } } } } private void modifyConstructors(AbstractTypeDeclaration declaration, ASTRewrite rewrite) throws CoreException { final FunctionDeclaration[] declarations= getConstructorDeclarationNodes(declaration); for (int index= 0; index < declarations.length; index++) { Assert.isTrue(declarations[index].isConstructor()); addParameterToConstructor(rewrite, declarations[index]); setEnclosingInstanceFieldInConstructor(rewrite, declarations[index]); } } private void modifyInterfaceMemberModifiers(final ITypeBinding binding) { Assert.isNotNull(binding); ITypeBinding declaring= binding.getDeclaringClass(); while (declaring != null) { declaring= declaring.getDeclaringClass(); } if (declaring != null) { final ASTNode node= ASTNodes.findDeclaration(binding, fSourceRewrite.getRoot()); if (node instanceof AbstractTypeDeclaration) { ModifierRewrite.create(fSourceRewrite.getASTRewrite(), node).setVisibility(Modifier.PUBLIC, null); } } } public void setCreateInstanceField(boolean create) { Assert.isTrue(fIsInstanceFieldCreationPossible); Assert.isTrue(!fIsInstanceFieldCreationMandatory); fCreateInstanceField= create; } private void setEnclosingInstanceFieldInConstructor(ASTRewrite rewrite, FunctionDeclaration decl) throws JavaScriptModelException { final AST ast= decl.getAST(); final Block body= decl.getBody(); final List statements= body.statements(); if (statements.isEmpty()) { final Assignment assignment= ast.newAssignment(); assignment.setLeftHandSide(createReadAccessExpressionForEnclosingInstance(ast)); assignment.setRightHandSide(ast.newSimpleName(getNameForEnclosingInstanceConstructorParameter())); rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY).insertFirst(ast.newExpressionStatement(assignment), null); } else { final Statement first= (Statement) statements.get(0); if (first instanceof ConstructorInvocation) { rewrite.getListRewrite(first, ConstructorInvocation.ARGUMENTS_PROPERTY).insertFirst(ast.newSimpleName(fEnclosingInstanceFieldName), null); } else { int index= 0; if (first instanceof SuperConstructorInvocation) index++; final Assignment assignment= ast.newAssignment(); assignment.setLeftHandSide(createReadAccessExpressionForEnclosingInstance(ast)); assignment.setRightHandSide(ast.newSimpleName(getNameForEnclosingInstanceConstructorParameter())); rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY).insertAt(ast.newExpressionStatement(assignment), index, null); } } } public void setEnclosingInstanceName(String name) { Assert.isNotNull(name); fEnclosingInstanceFieldName= name; } public void setMarkInstanceFieldAsFinal(boolean mark) { fMarkInstanceFieldAsFinal= mark; } private void updateConstructorReference(final ClassInstanceCreation creation, final CompilationUnitRewrite targetRewrite, final IJavaScriptUnit unit, TextEditGroup group) throws JavaScriptModelException { Assert.isNotNull(creation); Assert.isNotNull(targetRewrite); Assert.isNotNull(unit); final ASTRewrite rewrite= targetRewrite.getASTRewrite(); if (fCreateInstanceField) insertExpressionAsParameter(creation, rewrite, unit, group); final Expression expression= creation.getExpression(); if (expression != null) { rewrite.remove(expression, null); targetRewrite.getImportRemover().registerRemovedNode(expression); } } private void updateConstructorReference(ITypeBinding[] parameters, ASTNode reference, CompilationUnitRewrite targetRewrite, IJavaScriptUnit cu) throws CoreException { final TextEditGroup group= targetRewrite.createGroupDescription(RefactoringCoreMessages.MoveInnerToTopRefactoring_update_constructor_reference); if (reference instanceof SuperConstructorInvocation) updateConstructorReference((SuperConstructorInvocation) reference, targetRewrite, cu, group); else if (reference instanceof ClassInstanceCreation) updateConstructorReference((ClassInstanceCreation) reference, targetRewrite, cu, group); else if (reference.getParent() instanceof ClassInstanceCreation) updateConstructorReference((ClassInstanceCreation) reference.getParent(), targetRewrite, cu, group); } private void updateConstructorReference(final SuperConstructorInvocation invocation, final CompilationUnitRewrite targetRewrite, final IJavaScriptUnit unit, TextEditGroup group) throws CoreException { Assert.isNotNull(invocation); Assert.isNotNull(targetRewrite); Assert.isNotNull(unit); final ASTRewrite rewrite= targetRewrite.getASTRewrite(); if (fCreateInstanceField) insertExpressionAsParameter(invocation, rewrite, unit, group); final Expression expression= invocation.getExpression(); if (expression != null) { rewrite.remove(expression, null); targetRewrite.getImportRemover().registerRemovedNode(expression); } } private boolean updateNameReference(ITypeBinding[] parameters, Name name, CompilationUnitRewrite targetRewrite, TextEditGroup group) { if (ASTNodes.asString(name).equals(fType.getFullyQualifiedName('.'))) { targetRewrite.getASTRewrite().replace(name, getNewQualifiedNameNode(parameters, name), group); targetRewrite.getImportRemover().registerRemovedNode(name); return true; } targetRewrite.getASTRewrite().replace(name, getNewUnqualifiedTypeNode(parameters, name), group); targetRewrite.getImportRemover().registerRemovedNode(name); return true; } private boolean updateReference(ITypeBinding[] parameters, ASTNode node, CompilationUnitRewrite rewrite, TextEditGroup group) { if (node instanceof QualifiedName) return updateNameReference(parameters, (QualifiedName) node, rewrite, group); else if (node instanceof SimpleType) return updateNameReference(parameters, ((SimpleType) node).getName(), rewrite, group); else return false; } private void updateReferenceInImport(ImportDeclaration enclosingImport, ASTNode node, CompilationUnitRewrite rewrite) throws CoreException { final IBinding binding= enclosingImport.resolveBinding(); if (binding instanceof ITypeBinding) { final ITypeBinding type= (ITypeBinding) binding; final ImportRewrite rewriter= rewrite.getImportRewrite(); if (enclosingImport.isStatic()) { final String oldImport= ASTNodes.asString(node); final StringBuffer buffer= new StringBuffer(oldImport); final String typeName= fType.getDeclaringType().getElementName(); final int index= buffer.indexOf(typeName); if (index >= 0) { buffer.delete(index, index + typeName.length() + 1); final String newImport= buffer.toString(); if (enclosingImport.isOnDemand()) { rewriter.removeStaticImport(oldImport + ".*"); //$NON-NLS-1$ rewriter.addStaticImport(newImport, "*", false); //$NON-NLS-1$ } else { rewriter.removeStaticImport(oldImport); final int offset= newImport.lastIndexOf('.'); if (offset >= 0 && offset < newImport.length() - 1) { rewriter.addStaticImport(newImport.substring(0, offset), newImport.substring(offset + 1), false); } } } } else rewriter.removeImport(type.getQualifiedName()); } } private void updateTypeReference(ITypeBinding[] parameters, ASTNode node, CompilationUnitRewrite rewrite, IJavaScriptUnit cu) throws CoreException { ImportDeclaration enclosingImport= (ImportDeclaration) ASTNodes.getParent(node, ImportDeclaration.class); if (enclosingImport != null) updateReferenceInImport(enclosingImport, node, rewrite); else { final TextEditGroup group= rewrite.createGroupDescription(RefactoringCoreMessages.MoveInnerToTopRefactoring_update_type_reference); updateReference(parameters, node, rewrite, group); if (!fType.getPackageFragment().equals(cu.getParent())) { final String name= fType.getPackageFragment().getElementName() + '.' + fType.getElementName(); rewrite.getImportRemover().registerAddedImport(name); rewrite.getImportRewrite().addImport(name); } } } public RefactoringStatus initialize(final RefactoringArguments arguments) { if (arguments instanceof JavaRefactoringArguments) { final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); if (handle != null) { final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); if (element == null || !element.exists() || element.getElementType() != IJavaScriptElement.TYPE) return createInputFatalStatus(element, IJavaScriptRefactorings.CONVERT_MEMBER_TYPE); else { fType= (IType) element; fCodeGenerationSettings= JavaPreferencesSettings.getCodeGenerationSettings(fType.getJavaScriptProject()); try { initialize(); } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); } } } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); final String fieldName= extended.getAttribute(ATTRIBUTE_FIELD_NAME); if (fieldName != null && !"".equals(fieldName)) //$NON-NLS-1$ fEnclosingInstanceFieldName= fieldName; final String parameterName= extended.getAttribute(ATTRIBUTE_PARAMETER_NAME); if (parameterName != null && !"".equals(parameterName)) //$NON-NLS-1$ fNameForEnclosingInstanceConstructorParameter= parameterName; final String createField= extended.getAttribute(ATTRIBUTE_FIELD); if (createField != null) { fCreateInstanceField= Boolean.valueOf(createField).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FIELD)); final String markFinal= extended.getAttribute(ATTRIBUTE_FINAL); if (markFinal != null) { fMarkInstanceFieldAsFinal= Boolean.valueOf(markFinal).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_FINAL)); final String possible= extended.getAttribute(ATTRIBUTE_POSSIBLE); if (possible != null) { fIsInstanceFieldCreationPossible= Boolean.valueOf(possible).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_POSSIBLE)); final String mandatory= extended.getAttribute(ATTRIBUTE_MANDATORY); if (mandatory != null) fIsInstanceFieldCreationMandatory= Boolean.valueOf(mandatory).booleanValue(); else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_MANDATORY)); } else return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); return new RefactoringStatus(); } }