/******************************************************************************* * 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.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; 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.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.MoveArguments; import org.eclipse.ltk.core.refactoring.participants.MoveProcessor; import org.eclipse.ltk.core.refactoring.participants.ParticipantManager; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant; import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; import org.eclipse.ltk.core.refactoring.participants.SharableParticipants; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.wst.jsdt.core.Flags; import org.eclipse.wst.jsdt.core.IField; import org.eclipse.wst.jsdt.core.IFunction; import org.eclipse.wst.jsdt.core.IInitializer; 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.IMember; import org.eclipse.wst.jsdt.core.ISourceRange; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTVisitor; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; 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.Modifier; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.TypeDeclaration; 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.ITrackedNodePosition; import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite; import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor; import org.eclipse.wst.jsdt.core.refactoring.descriptors.MoveStaticMembersDescriptor; 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.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder; 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.RefactoringAvailabilityTester; 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.DynamicValidationRefactoringChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ScriptableRefactoring; import org.eclipse.wst.jsdt.internal.corext.refactoring.delegates.DelegateFieldCreator; import org.eclipse.wst.jsdt.internal.corext.refactoring.delegates.DelegateMethodCreator; import org.eclipse.wst.jsdt.internal.corext.refactoring.participants.JavaProcessors; import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment; import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.ICommentProvider; import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IDelegateUpdating; import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IScriptableRefactoring; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavaElementUtil; 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.ui.JavaScriptElementLabels; import org.eclipse.wst.jsdt.ui.refactoring.IRefactoringProcessorIds; public final class MoveStaticMembersProcessor extends MoveProcessor implements IDelegateUpdating, IScriptableRefactoring, ICommentProvider { private static final String ATTRIBUTE_DELEGATE="delegate"; //$NON-NLS-1$ private static final String ATTRIBUTE_DEPRECATE="deprecate"; //$NON-NLS-1$ private static final String TRACKED_POSITION_PROPERTY= "MoveStaticMembersProcessor.trackedPosition"; //$NON-NLS-1$ private IMember[] fMembersToMove; private IType fDestinationType; private String fDestinationTypeName; private CodeGenerationSettings fPreferences; private CompositeChange fChange; private CompilationUnitRewrite fSource; private ITypeBinding fSourceBinding; private CompilationUnitRewrite fTarget; private IBinding[] fMemberBindings; private BodyDeclaration[] fMemberDeclarations; private boolean fDelegateUpdating; private boolean fDelegateDeprecation; private String fComment; private static class TypeReferenceFinder extends ASTVisitor { List fResult= new ArrayList(); Set fDefined= new HashSet(); public static List perform(ASTNode root) { TypeReferenceFinder visitor= new TypeReferenceFinder(); root.accept(visitor); return visitor.fResult; } public boolean visit(TypeDeclaration node) { fDefined.add(node.resolveBinding()); return true; } public boolean visit(SimpleName node) { IBinding binding= node.resolveBinding(); if (!(binding instanceof ITypeBinding)) return true; if (!fDefined.contains(binding)) fResult.add(binding); return true; } } /** * Creates a new move static members processor. * @param members the members to move, or <code>null</code> if invoked by scripting * @param settings the code generation settings, or <code>null</code> if invoked by scripting */ public MoveStaticMembersProcessor(IMember[] members, CodeGenerationSettings settings) { fMembersToMove= members; fPreferences= settings; fDelegateUpdating= false; fDelegateDeprecation= true; } /** * {@inheritDoc} */ public boolean isApplicable() throws CoreException { return RefactoringAvailabilityTester.isMoveStaticMembersAvailable(fMembersToMove); } /** * {@inheritDoc} */ public Object[] getElements() { Object[] result= new Object[fMembersToMove.length]; System.arraycopy(fMembersToMove, 0, result, 0, fMembersToMove.length); return result; } /** * {@inheritDoc} */ public String getIdentifier() { return IRefactoringProcessorIds.MOVE_STATIC_MEMBERS_PROCESSOR; } /** * {@inheritDoc} */ public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException { List result= new ArrayList(); MoveArguments args= new MoveArguments(fDestinationType, true); String[] natures= JavaProcessors.computeAffectedNaturs(fMembersToMove); for (int i= 0; i < fMembersToMove.length; i++) { IMember member= fMembersToMove[i]; result.addAll(Arrays.asList(ParticipantManager.loadMoveParticipants( status, this, member, args, natures, sharedParticipants))); } return (RefactoringParticipant[])result.toArray(new RefactoringParticipant[result.size()]); } //------------------- IDelegateUpdating ---------------------- public boolean canEnableDelegateUpdating() { try { for (int i= 0; i < fMembersToMove.length; i++) { if (isDelegateCreationAvailable(fMembersToMove[i])) return true; } } catch (JavaScriptModelException e) { return false; } return false; } private boolean isDelegateCreationAvailable(IMember member) throws JavaScriptModelException { if (member instanceof IFunction) return true; if (member instanceof IField && RefactoringAvailabilityTester.isDelegateCreationAvailable(((IField)member))) return true; return false; } public boolean getDelegateUpdating() { return fDelegateUpdating; } public void setDelegateUpdating(boolean updating) { fDelegateUpdating= updating; } public boolean getDeprecateDelegates() { return fDelegateDeprecation; } public void setDeprecateDelegates(boolean deprecate) { fDelegateDeprecation= deprecate; } public String getProcessorName() { return RefactoringCoreMessages.MoveMembersRefactoring_Move_Members; } public IType getDestinationType() { return fDestinationType; } public void setDestinationTypeFullyQualifiedName(String fullyQualifiedTypeName) throws JavaScriptModelException { Assert.isNotNull(fullyQualifiedTypeName); fDestinationType= resolveType(fullyQualifiedTypeName); //workaround for bug 36032: IJavaScriptProject#findType(..) doesn't find secondary type fDestinationTypeName= fullyQualifiedTypeName; } public IMember[] getMembersToMove() { return fMembersToMove; } public IType getDeclaringType() { //all methods declared in same type - checked in precondition return fMembersToMove[0].getDeclaringType(); //index safe - checked in areAllMoveable() } private IType resolveType(String qualifiedTypeName) throws JavaScriptModelException{ IType type= getDeclaringType().getJavaScriptProject().findType(qualifiedTypeName); if (type == null) type= getDeclaringType().getJavaScriptProject().findType(getDeclaringType().getPackageFragment().getElementName(), qualifiedTypeName); return type; } //---- Activation checking ------------------------------------ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { try { pm.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_checking, 1); RefactoringStatus result= new RefactoringStatus(); result.merge(checkDeclaringType()); pm.worked(1); if (result.hasFatalError()) return result; fSource= new CompilationUnitRewrite(fMembersToMove[0].getJavaScriptUnit()); fSourceBinding= (ITypeBinding)((SimpleName)NodeFinder.perform(fSource.getRoot(), fMembersToMove[0].getDeclaringType().getNameRange())).resolveBinding(); fMemberBindings= getMemberBindings(); if (fSourceBinding == null || hasUnresolvedMemberBinding()) { result.addFatalError(Messages.format( RefactoringCoreMessages.MoveMembersRefactoring_compile_errors, fSource.getCu().getElementName())); } fMemberDeclarations= getASTMembers(result); return result; } finally { pm.done(); } } private boolean hasUnresolvedMemberBinding() { for (int i= 0; i < fMemberBindings.length; i++) { if (fMemberBindings[i] == null) return true; } return false; } private RefactoringStatus checkDeclaringType(){ IType declaringType= getDeclaringType(); if (JavaModelUtil.getFullyQualifiedName(declaringType).equals("java.lang.Object")) //$NON-NLS-1$ return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveMembersRefactoring_Object); if (declaringType.isBinary()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveMembersRefactoring_binary); if (declaringType.isReadOnly()) return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveMembersRefactoring_read_only); return null; } //---- Input checking ------------------------------------ public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException { fTarget= null; try { pm.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_checking, 10); RefactoringStatus result= new RefactoringStatus(); fSource.clearASTAndImportRewrites(); result.merge(checkDestinationType()); if (result.hasFatalError()) return result; result.merge(checkDestinationInsideTypeToMove()); if (result.hasFatalError()) return result; result.merge(MemberCheckUtil.checkMembersInDestinationType(fMembersToMove, fDestinationType)); if (result.hasFatalError()) return result; if (result.hasFatalError()) return result; List modifiedCus= new ArrayList(); createChange(modifiedCus, result, new SubProgressMonitor(pm, 7)); IFile[] changedFiles= getAllFilesToModify(modifiedCus); ResourceChangeChecker checker= (ResourceChangeChecker)context.getChecker(ResourceChangeChecker.class); for (int i= 0; i < changedFiles.length; i++) { checker.getDeltaFactory().change(changedFiles[i]); } return result; } finally { pm.done(); } } private IFile[] getAllFilesToModify(List modifiedCus) { Set result= new HashSet(); IResource resource= fDestinationType.getJavaScriptUnit().getResource(); result.add(resource); for (int i= 0; i < fMembersToMove.length; i++) { resource= fMembersToMove[i].getJavaScriptUnit().getResource(); if (resource != null) result.add(resource); } for (Iterator iter= modifiedCus.iterator(); iter.hasNext();) { IJavaScriptUnit unit= (IJavaScriptUnit)iter.next(); if (unit.getResource() != null) result.add(unit.getResource()); } return (IFile[]) result.toArray(new IFile[result.size()]); } private RefactoringStatus checkDestinationType() throws JavaScriptModelException { if (fDestinationType == null){ String message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_not_found, fDestinationTypeName); return RefactoringStatus.createFatalErrorStatus(message); } if (fDestinationType.equals(getDeclaringType())){ String message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_same, JavaElementUtil.createSignature(fDestinationType)); return RefactoringStatus.createFatalErrorStatus(message); } if (! fDestinationType.exists()){ String message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_not_exist, JavaElementUtil.createSignature(fDestinationType)); return RefactoringStatus.createFatalErrorStatus(message); } if (fDestinationType.isBinary()){ String message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_dest_binary, JavaElementUtil.createSignature(fDestinationType)); return RefactoringStatus.createFatalErrorStatus(message); } RefactoringStatus result= new RefactoringStatus(); if (result.hasFatalError()) return result; // no checking required for moving interface fields to classes if (! ((JdtFlags.isStatic(fDestinationType)) || (fDestinationType.getDeclaringType() == null))){ String message= RefactoringCoreMessages.MoveMembersRefactoring_static_declaration; result.addError(message); } return result; } private RefactoringStatus checkDestinationInsideTypeToMove() throws JavaScriptModelException { RefactoringStatus result= new RefactoringStatus(); for (int i= 0; i < fMembersToMove.length; i++) { if (! (fMembersToMove[i] instanceof IType)) continue; IType type= (IType) fMembersToMove[i]; if (fDestinationType.equals(type) || JavaElementUtil.isAncestorOf(type, fDestinationType)) { String message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_inside, new String[] {JavaModelUtil.getFullyQualifiedName(type), JavaModelUtil.getFullyQualifiedName(fDestinationType)}); RefactoringStatusContext context= JavaStatusContext.create(fDestinationType.getJavaScriptUnit(), fDestinationType.getNameRange()); result.addFatalError(message, context); return result; } } return result; } private RefactoringStatus checkFieldsForInterface() throws JavaScriptModelException { //could be more clever and make field final if it is only written once... RefactoringStatus result= new RefactoringStatus(); for (int i= 0; i < fMembersToMove.length; i++) { if (! canMoveToInterface(fMembersToMove[i])) { String message= RefactoringCoreMessages.MoveMembersRefactoring_only_public_static; result.addError(message, JavaStatusContext.create(fMembersToMove[i])); } } return result; } private boolean canMoveToInterface(IMember member) throws JavaScriptModelException { int flags= member.getFlags(); switch (member.getElementType()) { case IJavaScriptElement.FIELD: VariableDeclarationFragment declaration= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField) member, fSource.getRoot()); if (declaration != null) return declaration.getInitializer() != null; case IJavaScriptElement.TYPE: { return (Flags.isPublic(flags) && Flags.isStatic(flags)); } default: return false; } } private RefactoringStatus checkMovedMemberAvailability(IMember memberToMove, IProgressMonitor pm) throws JavaScriptModelException{ RefactoringStatus result= new RefactoringStatus(); if (memberToMove instanceof IType) { // recursively check accessibility of member type's members IJavaScriptElement[] typeMembers= ((IType) memberToMove).getChildren(); pm.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_checking, typeMembers.length + 1); for (int i= 0; i < typeMembers.length; i++) { if (typeMembers[i] instanceof IInitializer) pm.worked(1); else result.merge(checkMovedMemberAvailability((IMember) typeMembers[i], new SubProgressMonitor(pm, 1))); } } else { pm.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_checking, 1); } IType[] blindAccessorTypes= getTypesNotSeeingMovedMember(memberToMove, new SubProgressMonitor(pm, 1), result); for (int k= 0; k < blindAccessorTypes.length; k++) { String message= createNonAccessibleMemberMessage(memberToMove, blindAccessorTypes[k],/*moved*/true); result.addError(message, JavaStatusContext.create(memberToMove)); } pm.done(); return result; } private IType[] getTypesNotSeeingMovedMember(IMember member, IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException { if (JdtFlags.isPublic(member) && JdtFlags.isPublic(fDestinationType)) return new IType[0]; HashSet blindAccessorTypes= new HashSet(); // referencing, but access to destination type illegal SearchResultGroup[] references= getReferences(member, new SubProgressMonitor(pm, 1), status); for (int i = 0; i < references.length; i++) { SearchMatch[] searchResults= references[i].getSearchResults(); for (int k= 0; k < searchResults.length; k++) { SearchMatch searchResult= searchResults[k]; IJavaScriptElement element= SearchUtils.getEnclosingJavaElement(searchResult); IType type= (IType) element.getAncestor(IJavaScriptElement.TYPE); if (type != null //reference can e.g. be an import declaration && ! blindAccessorTypes.contains(type) && ! isWithinMemberToMove(searchResult) && ! isVisibleFrom(member, getDestinationType(), type)) { blindAccessorTypes.add(type); } } } if (fDelegateUpdating && isDelegateCreationAvailable(member)) { // ensure moved member is visible from the delegate IType type= member.getDeclaringType(); if (!blindAccessorTypes.contains(type) && !isVisibleFrom(member, getDestinationType(), type)) blindAccessorTypes.add(type); } return (IType[]) blindAccessorTypes.toArray(new IType[blindAccessorTypes.size()]); } private String createNonAccessibleMemberMessage(IMember member, IType accessingType, boolean moved){ //Non-visibility can have various reasons and always displaying all visibility //flags for all enclosing elements would be too heavy. Context reveals exact cause. IType declaringType= moved ? getDestinationType() : getDeclaringType(); String message; switch (member.getElementType()){ case IJavaScriptElement.FIELD: { if (moved) message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_moved_field, new String[]{JavaElementUtil.createFieldSignature((IField)member), JavaModelUtil.getFullyQualifiedName(accessingType), JavaModelUtil.getFullyQualifiedName(declaringType)}); else message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_accessed_field, new String[]{JavaElementUtil.createFieldSignature((IField)member), JavaModelUtil.getFullyQualifiedName(accessingType)}); return message; } case IJavaScriptElement.METHOD: { if (moved) message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_moved_method, new String[]{JavaElementUtil.createMethodSignature((IFunction)member), JavaModelUtil.getFullyQualifiedName(accessingType), JavaModelUtil.getFullyQualifiedName(declaringType)}); else message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_accessed_method, new String[]{JavaElementUtil.createMethodSignature((IFunction)member), JavaModelUtil.getFullyQualifiedName(accessingType)}); return message; } case IJavaScriptElement.TYPE:{ if (moved) message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_moved_type, new String[]{JavaModelUtil.getFullyQualifiedName(((IType)member)), JavaModelUtil.getFullyQualifiedName(accessingType), JavaModelUtil.getFullyQualifiedName(declaringType)}); else message= Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_accessed_type, new String[]{JavaModelUtil.getFullyQualifiedName(((IType)member)), JavaModelUtil.getFullyQualifiedName(accessingType)}); return message; } default: Assert.isTrue(false); return null; } } private static SearchResultGroup[] getReferences(IMember member, IProgressMonitor monitor, RefactoringStatus status) throws JavaScriptModelException { final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(member, IJavaScriptSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE)); engine.setFiltering(true, true); engine.setScope(RefactoringScopeFactory.create(member)); engine.setStatus(status); engine.searchPattern(new SubProgressMonitor(monitor, 1)); return (SearchResultGroup[]) engine.getResults(); } private static boolean isVisibleFrom(IMember member, IType newMemberDeclaringType, IType accessingType) throws JavaScriptModelException{ int memberVisibility= JdtFlags.getVisibilityCode(newMemberDeclaringType); IType declaringType= newMemberDeclaringType.getDeclaringType(); while (declaringType != null) { //get lowest visibility in all parent types of newMemberDeclaringType memberVisibility= JdtFlags.getLowerVisibility( memberVisibility, JdtFlags.getVisibilityCode(declaringType)); declaringType= declaringType.getDeclaringType(); } switch (memberVisibility) { case Modifier.PRIVATE : return isEqualOrEnclosedType(accessingType, newMemberDeclaringType); case Modifier.NONE : return JavaModelUtil.isSamePackage(accessingType.getPackageFragment(), newMemberDeclaringType.getPackageFragment()); case Modifier.PROTECTED : return JavaModelUtil.isSamePackage(accessingType.getPackageFragment(), newMemberDeclaringType.getPackageFragment()) || accessingType.newSupertypeHierarchy(null).contains(newMemberDeclaringType); case Modifier.PUBLIC : return true; default: Assert.isTrue(false); return false; } } private static boolean isEqualOrEnclosedType(IType inner, IType outer) { while (inner != null) { if (inner.equals(outer)) return true; else inner= inner.getDeclaringType(); } return false; } private boolean isWithinMemberToMove(SearchMatch result) throws JavaScriptModelException { IJavaScriptUnit referenceCU= SearchUtils.getCompilationUnit(result); if (! referenceCU.equals(fSource.getCu())) return false; int referenceStart= result.getOffset(); for (int i= 0; i < fMembersToMove.length; i++) { ISourceRange range= fMembersToMove[i].getSourceRange(); if (range.getOffset() <= referenceStart && range.getOffset() + range.getLength() >= referenceStart) return true; } return false; } public Change createChange(IProgressMonitor pm) throws CoreException { pm.done(); return fChange; } private void createChange(List modifiedCus, RefactoringStatus status, IProgressMonitor monitor) throws CoreException { monitor.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_creating, 5); final IMember[] members= getMembersToMove(); String project= null; final IJavaScriptProject javaProject= getDeclaringType().getJavaScriptProject(); if (javaProject != null) project= javaProject.getElementName(); String header= null; if (members.length == 1) header= Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_descriptor_description_single, new String[] { JavaScriptElementLabels.getElementLabel(members[0], JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getElementLabel(fDestinationType, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); else header= Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_descriptor_description_multi, new String[] { String.valueOf(members.length), JavaScriptElementLabels.getElementLabel(fDestinationType, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); int flags= JavaScriptRefactoringDescriptor.JAR_MIGRATION | JavaScriptRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; final IType declaring= members[0].getDeclaringType(); try { if (declaring.isLocal() || declaring.isAnonymous()) flags|= JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; } catch (JavaScriptModelException exception) { JavaScriptPlugin.log(exception); } final String description= members.length == 1 ? Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_description_descriptor_short_multi, members[0].getElementName()) : RefactoringCoreMessages.MoveMembersRefactoring_move_members; final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); comment.addSetting(Messages.format(RefactoringCoreMessages.MoveStaticMembersProcessor_target_element_pattern, JavaScriptElementLabels.getElementLabel(fDestinationType, JavaScriptElementLabels.ALL_FULLY_QUALIFIED))); final MoveStaticMembersDescriptor descriptor= new MoveStaticMembersDescriptor(); descriptor.setProject(project); descriptor.setDescription(description); descriptor.setComment(comment.asString()); descriptor.setFlags(flags); descriptor.setDestinationType(fDestinationType); descriptor.setKeepOriginal(fDelegateUpdating); descriptor.setDeprecateDelegate(fDelegateDeprecation); descriptor.setMembers(members); fChange= new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.MoveMembersRefactoring_move_members); fTarget= getCuRewrite(fDestinationType.getJavaScriptUnit()); ITypeBinding targetBinding= getDestinationBinding(); if (targetBinding == null) { status.addFatalError(Messages.format(RefactoringCoreMessages.MoveMembersRefactoring_compile_errors, fTarget.getCu().getElementName())); monitor.done(); return; } try { Map adjustments= new HashMap(); IMember member= null; SubProgressMonitor sub= new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); sub.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_creating, fMembersToMove.length); Set rewritten= new HashSet(); for (int index= 0; index < fMembersToMove.length; index++) { member= fMembersToMove[index]; final MemberVisibilityAdjustor adjustor= new MemberVisibilityAdjustor(fDestinationType, member); adjustor.setAdjustments(adjustments); adjustor.setStatus(status); adjustor.setVisibilitySeverity(RefactoringStatus.WARNING); adjustor.setFailureSeverity(RefactoringStatus.WARNING); adjustor.setRewrite(fSource.getASTRewrite(), fSource.getRoot()); adjustor.adjustVisibility(new NullProgressMonitor()); if (fDelegateUpdating && isDelegateCreationAvailable(member)) { // Add a visibility adjustment so the moved member // will be visible from within the delegate ModifierKeyword threshold= adjustor.getVisibilityThreshold(member, fDestinationType, new NullProgressMonitor()); IncomingMemberVisibilityAdjustment adjustment= (IncomingMemberVisibilityAdjustment) adjustments.get(member); ModifierKeyword kw= (adjustment != null) ? adjustment.getKeyword() : ModifierKeyword.fromFlagValue(JdtFlags.getVisibilityCode(member)); if (MemberVisibilityAdjustor.hasLowerVisibility(kw, threshold)) { adjustments.put(member, new MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment(member, threshold, RefactoringStatus.createWarningStatus(Messages.format(MemberVisibilityAdjustor.getMessage(member), new String[] { MemberVisibilityAdjustor.getLabel(member), MemberVisibilityAdjustor.getLabel(threshold)}), JavaStatusContext.create(member)))); } } // Check if destination type is visible from references -> // error message if not (for example, when moving into a private type) status.merge(checkMovedMemberAvailability(member, new SubProgressMonitor(sub, 1))); // Put rewrite info into code and into status for (final Iterator iterator= rewritten.iterator(); iterator.hasNext();) { adjustments.remove(iterator.next()); } rewritten.addAll(adjustments.keySet()); adjustor.rewriteVisibility(new NullProgressMonitor()); } // First update references in moved members, in order to extract the // source. String[] memberSources= getUpdatedMemberSource(status, fMemberDeclarations, targetBinding); monitor.worked(1); if (status.hasFatalError()) return; final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(); engine.setPattern(fMembersToMove, IJavaScriptSearchConstants.ALL_OCCURRENCES); engine.setGranularity(RefactoringSearchEngine2.GRANULARITY_COMPILATION_UNIT); engine.setFiltering(true, true); engine.setScope(RefactoringScopeFactory.create(fMembersToMove)); engine.setStatus(status); engine.searchPattern(new NullProgressMonitor()); IJavaScriptUnit[] units= engine.getAffectedCompilationUnits(); modifiedCus.addAll(Arrays.asList(units)); final MemberVisibilityAdjustor adjustor= new MemberVisibilityAdjustor(fDestinationType, fDestinationType); sub= new SubProgressMonitor(monitor, 1); sub.beginTask(RefactoringCoreMessages.MoveMembersRefactoring_creating, units.length); for (int index= 0; index < units.length; index++) { IJavaScriptUnit unit= units[index]; CompilationUnitRewrite rewrite= getCuRewrite(unit); adjustor.setRewrites(Collections.singletonMap(unit, rewrite)); adjustor.setAdjustments(adjustments); adjustor.rewriteVisibility(unit, new SubProgressMonitor(sub, 1)); ReferenceAnalyzer analyzer= new ReferenceAnalyzer(rewrite, fMemberBindings, targetBinding, fSourceBinding); rewrite.getRoot().accept(analyzer); status.merge(analyzer.getStatus()); if (status.hasFatalError()) { fChange= null; return; } if (!(fSource.getCu().equals(unit) || fTarget.getCu().equals(unit))) fChange.add(rewrite.createChange()); } status.merge(moveMembers(fMemberDeclarations, memberSources)); fChange.add(fSource.createChange()); modifiedCus.add(fSource.getCu()); if (!fSource.getCu().equals(fTarget.getCu())) { fChange.add(fTarget.createChange()); modifiedCus.add(fTarget.getCu()); } monitor.worked(1); } catch (BadLocationException exception) { JavaScriptPlugin.log(exception); } } private CompilationUnitRewrite getCuRewrite(IJavaScriptUnit unit) { if (fSource.getCu().equals(unit)) return fSource; if (fTarget != null && fTarget.getCu().equals(unit)) return fTarget; return new CompilationUnitRewrite(unit); } private ITypeBinding getDestinationBinding() throws JavaScriptModelException { ASTNode node= NodeFinder.perform(fTarget.getRoot(), fDestinationType.getNameRange()); if (!(node instanceof SimpleName)) return null; IBinding binding= ((SimpleName)node).resolveBinding(); if (!(binding instanceof ITypeBinding)) return null; return (ITypeBinding)binding; } private IBinding[] getMemberBindings() throws JavaScriptModelException { IBinding[] result= new IBinding[fMembersToMove.length]; for (int i= 0; i < fMembersToMove.length; i++) { IMember member= fMembersToMove[i]; SimpleName name= (SimpleName)NodeFinder.perform(fSource.getRoot(), member.getNameRange()); result[i]= name.resolveBinding(); } return result; } private String[] getUpdatedMemberSource(RefactoringStatus status, BodyDeclaration[] members, ITypeBinding target) throws CoreException, BadLocationException { List typeRefs= new ArrayList(); boolean targetNeedsSourceImport= false; boolean isSourceNotTarget= fSource != fTarget; Set exclude= new HashSet(); for (int i= 0; i < members.length; i++) { BodyDeclaration declaration= members[i]; if (declaration instanceof AbstractTypeDeclaration) { AbstractTypeDeclaration type= (AbstractTypeDeclaration) declaration; ITypeBinding binding= type.resolveBinding(); if (binding != null) exclude.add(binding); } else if (declaration instanceof FunctionDeclaration) { FunctionDeclaration method= (FunctionDeclaration) declaration; IFunctionBinding binding= method.resolveBinding(); if (binding != null) exclude.add(binding); } else if (declaration instanceof FieldDeclaration) { FieldDeclaration field= (FieldDeclaration) declaration; for (final Iterator iterator= field.fragments().iterator(); iterator.hasNext();) { VariableDeclarationFragment fragment= (VariableDeclarationFragment) iterator.next(); IVariableBinding binding= fragment.resolveBinding(); if (binding != null) exclude.add(binding); } } } for (int i= 0; i < members.length; i++) { BodyDeclaration declaration= members[i]; if (isSourceNotTarget) typeRefs.addAll(TypeReferenceFinder.perform(declaration)); MovedMemberAnalyzer analyzer= new MovedMemberAnalyzer(fSource, fMemberBindings, fSourceBinding, target); declaration.accept(analyzer); ImportRewriteUtil.addImports(fTarget, declaration, new HashMap(), new HashMap(), exclude, false); ITrackedNodePosition trackedPosition= fSource.getASTRewrite().track(declaration); declaration.setProperty(TRACKED_POSITION_PROPERTY, trackedPosition); targetNeedsSourceImport|= analyzer.targetNeedsSourceImport(); status.merge(analyzer.getStatus()); } // Adjust imports if (targetNeedsSourceImport && isSourceNotTarget) { fTarget.getImportRewrite().addImport(fSourceBinding); } if (isSourceNotTarget) { for (Iterator iter= typeRefs.iterator(); iter.hasNext();) { ITypeBinding binding= (ITypeBinding) iter.next(); fTarget.getImportRewrite().addImport(binding); } } // extract updated members String[] updatedMemberSources= new String[members.length]; IDocument document= new Document(fSource.getCu().getBuffer().getContents()); TextEdit edit= fSource.getASTRewrite().rewriteAST(document, fSource.getCu().getJavaScriptProject().getOptions(true)); edit.apply(document, TextEdit.UPDATE_REGIONS); for (int i= 0; i < members.length; i++) { updatedMemberSources[i]= getUpdatedMember(document, members[i]); } fSource.clearASTRewrite(); return updatedMemberSources; } private String getUpdatedMember(IDocument document, BodyDeclaration declaration) throws BadLocationException { ITrackedNodePosition trackedPosition= (ITrackedNodePosition) declaration.getProperty(TRACKED_POSITION_PROPERTY); return Strings.trimIndentation(document.get(trackedPosition.getStartPosition(), trackedPosition.getLength()), fPreferences.tabWidth, fPreferences.indentWidth, false); } private RefactoringStatus moveMembers(BodyDeclaration[] members, String[] sources) throws CoreException { RefactoringStatus result= new RefactoringStatus(); AbstractTypeDeclaration destination= (AbstractTypeDeclaration) ASTNodes.getParent( NodeFinder.perform(fTarget.getRoot(), fDestinationType.getNameRange()), AbstractTypeDeclaration.class); ListRewrite containerRewrite= fTarget.getASTRewrite().getListRewrite(destination, destination.getBodyDeclarationsProperty()); TextEditGroup delete= fSource.createGroupDescription(RefactoringCoreMessages.MoveMembersRefactoring_deleteMembers); TextEditGroup add= fTarget.createGroupDescription(RefactoringCoreMessages.MoveMembersRefactoring_addMembers); for (int i= 0; i < members.length; i++) { BodyDeclaration declaration= members[i]; ASTNode removeImportsOf= null; boolean addedDelegate= false; if (fDelegateUpdating) { if (declaration instanceof FunctionDeclaration) { DelegateMethodCreator creator= new DelegateMethodCreator(); creator.setDeclaration(declaration); creator.setDeclareDeprecated(fDelegateDeprecation); creator.setSourceRewrite(fSource); creator.setCopy(false); creator.setNewLocation(getDestinationBinding()); creator.prepareDelegate(); creator.createEdit(); removeImportsOf= ((FunctionDeclaration) declaration).getBody(); addedDelegate= true; } if (declaration instanceof FieldDeclaration) { // Note: this FieldDeclaration only has one fragment (@see #getASTMembers(RefactoringStatus)) final VariableDeclarationFragment frag= (VariableDeclarationFragment) ((FieldDeclaration) declaration).fragments().get(0); if (!Modifier.isFinal(declaration.getModifiers())) { // Don't create a delegate for non-final fields result.addInfo(Messages.format(RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_not_final, frag.getName()), null); } else if (frag.getInitializer() == null) { // Don't create a delegate without an initializer. result.addInfo(Messages.format(RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_no_initializer, frag.getName()), null); } else { DelegateFieldCreator creator= new DelegateFieldCreator(); creator.setDeclaration(declaration); creator.setDeclareDeprecated(fDelegateDeprecation); creator.setSourceRewrite(fSource); creator.setCopy(false); creator.setNewLocation(getDestinationBinding()); creator.prepareDelegate(); creator.createEdit(); removeImportsOf= frag.getInitializer(); addedDelegate= true; } } if (declaration instanceof AbstractTypeDeclaration) { result.addInfo(Messages.format(RefactoringCoreMessages.DelegateCreator_cannot_create_delegate_for_type, ((AbstractTypeDeclaration) declaration).getName().getIdentifier()), null); } } if (!addedDelegate) { fSource.getASTRewrite().remove(declaration, delete); removeImportsOf= declaration; } if (removeImportsOf != null && fSource != fTarget) fSource.getImportRemover().registerRemovedNode(removeImportsOf); ASTNode node= fTarget.getASTRewrite().createStringPlaceholder(sources[i], declaration.getNodeType()); List container= containerRewrite.getRewrittenList(); int insertionIndex= ASTNodes.getInsertionIndex((BodyDeclaration) node, container); containerRewrite.insertAt(node, insertionIndex, add); } return result; } private BodyDeclaration[] getASTMembers(RefactoringStatus status) throws JavaScriptModelException { BodyDeclaration[] result= new BodyDeclaration[fMembersToMove.length]; for (int i= 0; i < fMembersToMove.length; i++) { IMember member= fMembersToMove[i]; ASTNode node= NodeFinder.perform(fSource.getRoot(), member.getNameRange()); result[i]= (BodyDeclaration)ASTNodes.getParent(node, BodyDeclaration.class); //Fix for bug 42383: exclude multiple VariableDeclarationFragments ("int a=1, b=2") //ReferenceAnalyzer#visit(FieldDeclaration node) depends on fragments().size() != 1 ! if (result[i] instanceof FieldDeclaration && ((FieldDeclaration) result[i]).fragments().size() != 1) { status.addFatalError(RefactoringCoreMessages.MoveMembersRefactoring_multi_var_fields); return result; } } //Sorting members is important for field declarations referring to previous fields. Arrays.sort(result, new Comparator() { public int compare(Object o1, Object o2) { return ((BodyDeclaration) o1).getStartPosition() - ((BodyDeclaration) o2).getStartPosition(); } }); return result; } public RefactoringStatus initialize(final RefactoringArguments arguments) { if (arguments instanceof JavaRefactoringArguments) { final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; 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 ScriptableRefactoring.createInputFatalStatus(element, getRefactoring().getName(), IJavaScriptRefactorings.MOVE_STATIC_MEMBERS); else { fDestinationType= (IType) element; fDestinationTypeName= fDestinationType.getFullyQualifiedName(); } } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); final String delegate= extended.getAttribute(ATTRIBUTE_DELEGATE); if (delegate != null) { fDelegateUpdating= Boolean.valueOf(delegate).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELEGATE)); final String deprecate= extended.getAttribute(ATTRIBUTE_DEPRECATE); if (deprecate != null) { fDelegateDeprecation= Boolean.valueOf(deprecate).booleanValue(); } else return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DEPRECATE)); int count= 1; final List elements= new ArrayList(); String attribute= JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count; final RefactoringStatus status= new RefactoringStatus(); while ((handle= extended.getAttribute(attribute)) != null) { final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); if (element == null || !element.exists()) status.merge(ScriptableRefactoring.createInputWarningStatus(element, getRefactoring().getName(), IJavaScriptRefactorings.MOVE_STATIC_MEMBERS)); else elements.add(element); count++; attribute= JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count; } fMembersToMove= (IMember[]) elements.toArray(new IMember[elements.size()]); if (elements.isEmpty()) return ScriptableRefactoring.createInputFatalStatus(null, getRefactoring().getName(), IJavaScriptRefactorings.MOVE_STATIC_MEMBERS); IJavaScriptProject project= null; if (fMembersToMove.length > 0) project= fMembersToMove[0].getJavaScriptProject(); fPreferences= JavaPreferencesSettings.getCodeGenerationSettings(project); if (!status.isOK()) return status; } else return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); return new RefactoringStatus(); } public boolean canEnableComment() { return true; } public String getComment() { return fComment; } public void setComment(String comment) { fComment= comment; } public String getDelegateUpdatingTitle(boolean plural) { if (plural) return RefactoringCoreMessages.DelegateMethodCreator_keep_original_moved_plural; else return RefactoringCoreMessages.DelegateMethodCreator_keep_original_moved_singular; } }