| /******************************************************************************* |
| * Copyright (c) 2006, 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.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.OperationCanceledException; |
| 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.GroupCategory; |
| import org.eclipse.ltk.core.refactoring.GroupCategorySet; |
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; |
| import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; |
| import org.eclipse.text.edits.MalformedTreeException; |
| import org.eclipse.text.edits.TextEdit; |
| 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.IMember; |
| 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.ASTRequestor; |
| import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.Block; |
| import org.eclipse.wst.jsdt.core.dom.Expression; |
| import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.dom.Modifier; |
| import org.eclipse.wst.jsdt.core.dom.Type; |
| import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.wst.jsdt.core.dom.rewrite.ITrackedNodePosition; |
| 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.SearchEngine; |
| import org.eclipse.wst.jsdt.core.search.SearchMatch; |
| import org.eclipse.wst.jsdt.core.search.SearchPattern; |
| 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.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.RefactoringAvailabilityTester; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages; |
| 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.rename.MethodChecks; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextEditBasedChangeManager; |
| 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.ui.JavaScriptElementLabels; |
| |
| /** |
| * Refactoring processor for the push down refactoring. |
| * |
| * |
| */ |
| public final class PushDownRefactoringProcessor extends HierarchyProcessor { |
| |
| public static class MemberActionInfo implements IMemberActionInfo { |
| |
| public static final int NO_ACTION= 2; |
| |
| public static final int PUSH_ABSTRACT_ACTION= 1; |
| |
| public static final int PUSH_DOWN_ACTION= 0; |
| |
| private static void assertValidAction(IMember member, int action) { |
| if (member instanceof IFunction) |
| Assert.isTrue(action == PUSH_ABSTRACT_ACTION || action == NO_ACTION || action == PUSH_DOWN_ACTION); |
| else if (member instanceof IField) |
| Assert.isTrue(action == NO_ACTION || action == PUSH_DOWN_ACTION); |
| } |
| |
| public static MemberActionInfo create(IMember member, int action) { |
| return new MemberActionInfo(member, action); |
| } |
| |
| static IMember[] getMembers(MemberActionInfo[] infos) { |
| IMember[] result= new IMember[infos.length]; |
| for (int i= 0; i < result.length; i++) { |
| result[i]= infos[i].getMember(); |
| } |
| return result; |
| } |
| |
| private int fAction; |
| |
| private final IMember fMember; |
| |
| private MemberActionInfo(IMember member, int action) { |
| assertValidAction(member, action); |
| Assert.isTrue(member instanceof IField || member instanceof IFunction); |
| fMember= member; |
| fAction= action; |
| } |
| |
| boolean copyJavadocToCopiesInSubclasses() { |
| return isToBeDeletedFromDeclaringClass(); |
| } |
| |
| public int getAction() { |
| return fAction; |
| } |
| |
| public int[] getAvailableActions() { |
| if (isFieldInfo()) |
| return new int[] { PUSH_DOWN_ACTION, NO_ACTION }; |
| |
| return new int[] { PUSH_DOWN_ACTION, PUSH_ABSTRACT_ACTION, NO_ACTION }; |
| } |
| |
| public IMember getMember() { |
| return fMember; |
| } |
| |
| int getNewModifiersForCopyInSubclass(int oldModifiers) throws JavaScriptModelException { |
| if (isFieldInfo()) |
| return oldModifiers; |
| if (isToBeDeletedFromDeclaringClass()) |
| return oldModifiers; |
| int modifiers= oldModifiers; |
| if (isNewMethodToBeDeclaredAbstract()) { |
| if (!JdtFlags.isPublic(fMember)) |
| modifiers= Modifier.PROTECTED | JdtFlags.clearAccessModifiers(modifiers); |
| } |
| return modifiers; |
| } |
| |
| int getNewModifiersForOriginal(int oldModifiers) throws JavaScriptModelException { |
| if (isFieldInfo()) |
| return oldModifiers; |
| if (isToBeDeletedFromDeclaringClass()) |
| return oldModifiers; |
| int modifiers= oldModifiers; |
| if (isNewMethodToBeDeclaredAbstract()) { |
| modifiers= JdtFlags.clearFlag(Modifier.FINAL | Modifier.NATIVE, oldModifiers); |
| modifiers|= Modifier.ABSTRACT; |
| |
| if (!JdtFlags.isPublic(fMember)) |
| modifiers= Modifier.PROTECTED | JdtFlags.clearAccessModifiers(modifiers); |
| } |
| return modifiers; |
| } |
| |
| public boolean isActive() { |
| return getAction() != NO_ACTION; |
| } |
| |
| public boolean isEditable() { |
| if (isFieldInfo()) |
| return false; |
| if (getAction() == MemberActionInfo.NO_ACTION) |
| return false; |
| return true; |
| } |
| |
| boolean isFieldInfo() { |
| return fMember instanceof IField; |
| } |
| |
| boolean isNewMethodToBeDeclaredAbstract() throws JavaScriptModelException { |
| return !isFieldInfo() && !JdtFlags.isAbstract(fMember) && fAction == PUSH_ABSTRACT_ACTION; |
| } |
| |
| boolean isToBeCreatedInSubclassesOfDeclaringClass() { |
| return fAction != NO_ACTION; |
| } |
| |
| boolean isToBeDeletedFromDeclaringClass() { |
| return isToBePushedDown(); |
| } |
| |
| public boolean isToBePushedDown() { |
| return fAction == PUSH_DOWN_ACTION; |
| } |
| |
| public void setAction(int action) { |
| assertValidAction(fMember, action); |
| if (isFieldInfo()) |
| Assert.isTrue(action != PUSH_ABSTRACT_ACTION); |
| fAction= action; |
| } |
| |
| } |
| |
| private static final String ATTRIBUTE_ABSTRACT= "abstract"; //$NON-NLS-1$ |
| |
| private static final String ATTRIBUTE_PUSH= "push"; //$NON-NLS-1$ |
| |
| /** The identifier of this processor */ |
| public static final String IDENTIFIER= "org.eclipse.wst.jsdt.ui.pushDownProcessor"; //$NON-NLS-1$ |
| |
| /** The push down group category set */ |
| private static final GroupCategorySet SET_PUSH_DOWN= new GroupCategorySet(new GroupCategory("org.eclipse.wst.jsdt.internal.corext.pushDown", //$NON-NLS-1$ |
| RefactoringCoreMessages.PushDownRefactoring_category_name, RefactoringCoreMessages.PushDownRefactoring_category_description)); |
| |
| private static MemberActionInfo[] createInfosForAllPushableFieldsAndMethods(IType type) throws JavaScriptModelException { |
| List result= new ArrayList(); |
| IMember[] pushableMembers= RefactoringAvailabilityTester.getPushDownMembers(type); |
| for (int i= 0; i < pushableMembers.length; i++) { |
| result.add(MemberActionInfo.create(pushableMembers[i], MemberActionInfo.NO_ACTION)); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| private static IMember[] getAbstractMembers(IMember[] members) throws JavaScriptModelException { |
| List result= new ArrayList(members.length); |
| for (int i= 0; i < members.length; i++) { |
| IMember member= members[i]; |
| if (JdtFlags.isAbstract(member)) |
| result.add(member); |
| } |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } |
| |
| private static CompilationUnitRewrite getCompilationUnitRewrite(final Map rewrites, final IJavaScriptUnit unit) { |
| Assert.isNotNull(rewrites); |
| Assert.isNotNull(unit); |
| CompilationUnitRewrite rewrite= (CompilationUnitRewrite) rewrites.get(unit); |
| if (rewrite == null) { |
| rewrite= new CompilationUnitRewrite(unit); |
| rewrites.put(unit, rewrite); |
| } |
| return rewrite; |
| } |
| |
| private static IJavaScriptElement[] getReferencingElementsFromSameClass(IMember member, IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException { |
| Assert.isNotNull(member); |
| final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(member, IJavaScriptSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE)); |
| engine.setFiltering(true, true); |
| engine.setScope(SearchEngine.createJavaSearchScope(new IJavaScriptElement[] { member.getDeclaringType() })); |
| engine.setStatus(status); |
| engine.searchPattern(new SubProgressMonitor(pm, 1)); |
| SearchResultGroup[] groups= (SearchResultGroup[]) engine.getResults(); |
| Set result= new HashSet(3); |
| for (int i= 0; i < groups.length; i++) { |
| SearchResultGroup group= groups[i]; |
| SearchMatch[] results= group.getSearchResults(); |
| for (int j= 0; j < results.length; j++) { |
| SearchMatch searchResult= results[i]; |
| result.add(SearchUtils.getEnclosingJavaElement(searchResult)); |
| } |
| } |
| return (IJavaScriptElement[]) result.toArray(new IJavaScriptElement[result.size()]); |
| } |
| |
| private ITypeHierarchy fCachedClassHierarchy; |
| |
| private MemberActionInfo[] fMemberInfos; |
| |
| /** |
| * Creates a new push down refactoring processor. |
| * |
| * @param members |
| * the members to pull up, or <code>null</code> if invoked by |
| * scripting |
| */ |
| public PushDownRefactoringProcessor(IMember[] members) { |
| super(members, null, false); |
| if (members != null) { |
| final IType type= RefactoringAvailabilityTester.getTopLevelType(members); |
| try { |
| if (type != null && RefactoringAvailabilityTester.getPushDownMembers(type).length != 0) { |
| fMembersToMove= new IMember[0]; |
| fCachedDeclaringType= type; |
| } |
| } catch (JavaScriptModelException exception) { |
| JavaScriptPlugin.log(exception); |
| } |
| } |
| } |
| |
| private void addAllRequiredPushableMembers(List queue, IMember member, IProgressMonitor monitor) throws JavaScriptModelException { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, 2); |
| IProgressMonitor sub= new SubProgressMonitor(monitor, 1); |
| sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, 2); |
| IFunction[] requiredMethods= ReferenceFinderUtil.getMethodsReferencedIn(new IJavaScriptElement[] { member }, new SubProgressMonitor(sub, 1)); |
| sub= new SubProgressMonitor(sub, 1); |
| sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, requiredMethods.length); |
| for (int index= 0; index < requiredMethods.length; index++) { |
| IFunction method= requiredMethods[index]; |
| if (!MethodChecks.isVirtual(method) && (method.getDeclaringType().equals(getDeclaringType()) && !queue.contains(method) && RefactoringAvailabilityTester.isPushDownAvailable(method))) |
| queue.add(method); |
| } |
| sub.done(); |
| IField[] requiredFields= ReferenceFinderUtil.getFieldsReferencedIn(new IJavaScriptElement[] { member }, new SubProgressMonitor(monitor, 1)); |
| for (int index= 0; index < requiredFields.length; index++) { |
| IField field= requiredFields[index]; |
| if (field.getDeclaringType().equals(getDeclaringType()) && !queue.contains(field) && RefactoringAvailabilityTester.isPushDownAvailable(field)) |
| queue.add(field); |
| } |
| monitor.done(); |
| } |
| |
| private RefactoringStatus checkAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForAbstract) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] abstractMembersToPushDown= getAbstractMembers(membersToPushDown); |
| for (int index= 0; index < destinationClassesForAbstract.length; index++) { |
| result.merge(MemberCheckUtil.checkMembersInDestinationType(abstractMembersToPushDown, destinationClassesForAbstract[index])); |
| } |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedFields(IType[] subclasses, IProgressMonitor pm) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| List pushedDownList= Arrays.asList(membersToPushDown); |
| IField[] accessedFields= ReferenceFinderUtil.getFieldsReferencedIn(membersToPushDown, pm); |
| for (int i= 0; i < subclasses.length; i++) { |
| IType targetClass= subclasses[i]; |
| ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null); |
| for (int j= 0; j < accessedFields.length; j++) { |
| IField field= accessedFields[j]; |
| boolean isAccessible= pushedDownList.contains(field) || canBeAccessedFrom(field, targetClass, targetSupertypes); |
| if (!isAccessible) { |
| String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_field_not_accessible, new String[] { JavaScriptElementLabels.getTextLabel(field, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getTextLabel(targetClass, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); |
| result.addError(message, JavaStatusContext.create(field)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedMethods(IType[] subclasses, IProgressMonitor pm) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| List pushedDownList= Arrays.asList(membersToPushDown); |
| IFunction[] accessedMethods= ReferenceFinderUtil.getMethodsReferencedIn(membersToPushDown, pm); |
| for (int index= 0; index < subclasses.length; index++) { |
| IType targetClass= subclasses[index]; |
| ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null); |
| for (int offset= 0; offset < accessedMethods.length; offset++) { |
| IFunction method= accessedMethods[offset]; |
| boolean isAccessible= pushedDownList.contains(method) || canBeAccessedFrom(method, targetClass, targetSupertypes); |
| if (!isAccessible) { |
| String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_method_not_accessible, new String[] { JavaScriptElementLabels.getTextLabel(method, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getTextLabel(targetClass, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); |
| result.addError(message, JavaStatusContext.create(method)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkAccessedTypes(IType[] subclasses, IProgressMonitor pm) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| IType[] accessedTypes= getTypesReferencedInMovedMembers(pm); |
| for (int index= 0; index < subclasses.length; index++) { |
| IType targetClass= subclasses[index]; |
| ITypeHierarchy targetSupertypes= targetClass.newSupertypeHierarchy(null); |
| for (int offset= 0; offset < accessedTypes.length; offset++) { |
| IType type= accessedTypes[offset]; |
| if (!canBeAccessedFrom(type, targetClass, targetSupertypes)) { |
| String message= Messages.format(RefactoringCoreMessages.PushDownRefactoring_type_not_accessible, new String[] { JavaScriptElementLabels.getTextLabel(type, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getTextLabel(targetClass, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); |
| result.addError(message, JavaStatusContext.create(type)); |
| } |
| } |
| } |
| pm.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkElementsAccessedByModifiedMembers(IProgressMonitor pm) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask(RefactoringCoreMessages.PushDownRefactoring_check_references, 3); |
| IType[] subclasses= getAbstractDestinations(new SubProgressMonitor(pm, 1)); |
| result.merge(checkAccessedTypes(subclasses, new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedFields(subclasses, new SubProgressMonitor(pm, 1))); |
| result.merge(checkAccessedMethods(subclasses, new SubProgressMonitor(pm, 1))); |
| pm.done(); |
| return result; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public RefactoringStatus checkFinalConditions(IProgressMonitor monitor, CheckConditionsContext context) throws CoreException, OperationCanceledException { |
| try { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 5); |
| clearCaches(); |
| IJavaScriptUnit unit= getDeclaringType().getJavaScriptUnit(); |
| if (fLayer) |
| unit= unit.findWorkingCopy(fOwner); |
| resetWorkingCopies(unit); |
| final RefactoringStatus result= new RefactoringStatus(); |
| result.merge(checkMembersInDestinationClasses(new SubProgressMonitor(monitor, 1))); |
| result.merge(checkElementsAccessedByModifiedMembers(new SubProgressMonitor(monitor, 1))); |
| result.merge(checkReferencesToPushedDownMembers(new SubProgressMonitor(monitor, 1))); |
| if (!JdtFlags.isAbstract(getDeclaringType()) && getAbstractDeclarationInfos().length != 0) |
| result.merge(checkConstructorCalls(getDeclaringType(), new SubProgressMonitor(monitor, 1))); |
| else |
| monitor.worked(1); |
| if (result.hasFatalError()) |
| return result; |
| List members= new ArrayList(fMemberInfos.length); |
| for (int index= 0; index < fMemberInfos.length; index++) { |
| if (fMemberInfos[index].getAction() != MemberActionInfo.NO_ACTION) |
| members.add(fMemberInfos[index].getMember()); |
| } |
| fMembersToMove= (IMember[]) members.toArray(new IMember[members.size()]); |
| fChangeManager= createChangeManager(new SubProgressMonitor(monitor, 1), result); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()), getRefactoring().getValidationContext())); |
| return result; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException, OperationCanceledException { |
| try { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 1); |
| RefactoringStatus status= new RefactoringStatus(); |
| status.merge(checkPossibleSubclasses(new SubProgressMonitor(monitor, 1))); |
| if (status.hasFatalError()) |
| return status; |
| status.merge(checkDeclaringType(new SubProgressMonitor(monitor, 1))); |
| if (status.hasFatalError()) |
| return status; |
| status.merge(checkIfMembersExist()); |
| if (status.hasFatalError()) |
| return status; |
| fMemberInfos= createInfosForAllPushableFieldsAndMethods(getDeclaringType()); |
| List list= Arrays.asList(fMembersToMove); |
| for (int offset= 0; offset < fMemberInfos.length; offset++) { |
| MemberActionInfo info= fMemberInfos[offset]; |
| if (list.contains(info.getMember())) |
| info.setAction(MemberActionInfo.PUSH_DOWN_ACTION); |
| } |
| return status; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private RefactoringStatus checkMembersInDestinationClasses(IProgressMonitor monitor) throws JavaScriptModelException { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 2); |
| RefactoringStatus result= new RefactoringStatus(); |
| IMember[] membersToPushDown= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| |
| IType[] destinationClassesForNonAbstract= getAbstractDestinations(new SubProgressMonitor(monitor, 1)); |
| result.merge(checkNonAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForNonAbstract)); |
| List list= Arrays.asList(getAbstractMembers(getAbstractDestinations(new SubProgressMonitor(monitor, 1)))); |
| |
| IType[] destinationClassesForAbstract= (IType[]) list.toArray(new IType[list.size()]); |
| result.merge(checkAbstractMembersInDestinationClasses(membersToPushDown, destinationClassesForAbstract)); |
| monitor.done(); |
| return result; |
| } |
| |
| private RefactoringStatus checkNonAbstractMembersInDestinationClasses(IMember[] membersToPushDown, IType[] destinationClassesForNonAbstract) throws JavaScriptModelException { |
| RefactoringStatus result= new RefactoringStatus(); |
| List list= new ArrayList(); // Arrays.asList does not support removing |
| list.addAll(Arrays.asList(membersToPushDown)); |
| list.removeAll(Arrays.asList(getAbstractMembers(membersToPushDown))); |
| IMember[] nonAbstractMembersToPushDown= (IMember[]) list.toArray(new IMember[list.size()]); |
| for (int i= 0; i < destinationClassesForNonAbstract.length; i++) { |
| result.merge(MemberCheckUtil.checkMembersInDestinationType(nonAbstractMembersToPushDown, destinationClassesForNonAbstract[i])); |
| } |
| return result; |
| } |
| |
| private RefactoringStatus checkPossibleSubclasses(IProgressMonitor pm) throws JavaScriptModelException { |
| IType[] modifiableSubclasses= getAbstractDestinations(pm); |
| if (modifiableSubclasses.length == 0) { |
| String msg= Messages.format(RefactoringCoreMessages.PushDownRefactoring_no_subclasses, new String[] { JavaScriptElementLabels.getTextLabel(getDeclaringType(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); |
| return RefactoringStatus.createFatalErrorStatus(msg); |
| } |
| return new RefactoringStatus(); |
| } |
| |
| private RefactoringStatus checkReferencesToPushedDownMembers(IProgressMonitor monitor) throws JavaScriptModelException { |
| List fields= new ArrayList(fMemberInfos.length); |
| for (int index= 0; index < fMemberInfos.length; index++) { |
| MemberActionInfo info= fMemberInfos[index]; |
| if (info.isToBePushedDown()) |
| fields.add(info.getMember()); |
| } |
| IMember[] membersToPush= (IMember[]) fields.toArray(new IMember[fields.size()]); |
| RefactoringStatus result= new RefactoringStatus(); |
| List movedMembers= Arrays.asList(MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass())); |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_check_references, membersToPush.length); |
| for (int index= 0; index < membersToPush.length; index++) { |
| IMember member= membersToPush[index]; |
| String label= createLabel(member); |
| IJavaScriptElement[] referencing= getReferencingElementsFromSameClass(member, new SubProgressMonitor(monitor, 1), result); |
| for (int offset= 0; offset < referencing.length; offset++) { |
| IJavaScriptElement element= referencing[offset]; |
| if (movedMembers.contains(element)) |
| continue; |
| if (!(element instanceof IMember)) |
| continue; |
| IMember referencingMember= (IMember) element; |
| Object[] keys= { label, createLabel(referencingMember) }; |
| String msg= Messages.format(RefactoringCoreMessages.PushDownRefactoring_referenced, keys); |
| result.addError(msg, JavaStatusContext.create(referencingMember)); |
| } |
| } |
| monitor.done(); |
| return result; |
| } |
| |
| public void computeAdditionalRequiredMembersToPushDown(IProgressMonitor monitor) throws JavaScriptModelException { |
| List list= Arrays.asList(getAdditionalRequiredMembers(monitor)); |
| for (int index= 0; index < fMemberInfos.length; index++) { |
| MemberActionInfo info= fMemberInfos[index]; |
| if (list.contains(info.getMember())) |
| info.setAction(MemberActionInfo.PUSH_DOWN_ACTION); |
| } |
| } |
| |
| private void copyBodyOfPushedDownMethod(ASTRewrite targetRewrite, IFunction method, FunctionDeclaration oldMethod, FunctionDeclaration newMethod, TypeVariableMaplet[] mapping) throws JavaScriptModelException { |
| Block body= oldMethod.getBody(); |
| if (body == null) { |
| newMethod.setBody(null); |
| return; |
| } |
| try { |
| final IDocument document= new Document(method.getJavaScriptUnit().getBuffer().getContents()); |
| final ASTRewrite rewriter= ASTRewrite.create(body.getAST()); |
| final ITrackedNodePosition position= rewriter.track(body); |
| body.accept(new TypeVariableMapper(rewriter, mapping)); |
| rewriter.rewriteAST(document, getDeclaringType().getJavaScriptUnit().getJavaScriptProject().getOptions(true)).apply(document, TextEdit.NONE); |
| String content= document.get(position.getStartPosition(), position.getLength()); |
| String[] lines= Strings.convertIntoLines(content); |
| Strings.trimIndentation(lines, method.getJavaScriptProject(), false); |
| content= Strings.concatenate(lines, StubUtility.getLineDelimiterUsed(method)); |
| newMethod.setBody((Block) targetRewrite.createStringPlaceholder(content, ASTNode.BLOCK)); |
| } catch (MalformedTreeException exception) { |
| JavaScriptPlugin.log(exception); |
| } catch (BadLocationException exception) { |
| JavaScriptPlugin.log(exception); |
| } |
| } |
| |
| private void copyMembers(Collection adjustors, Map adjustments, Map rewrites, RefactoringStatus status, MemberActionInfo[] infos, IType[] destinations, CompilationUnitRewrite sourceRewriter, CompilationUnitRewrite unitRewriter, IProgressMonitor monitor) throws JavaScriptModelException { |
| try { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 1); |
| IType type= null; |
| TypeVariableMaplet[] mapping= null; |
| for (int index= 0; index < destinations.length; index++) { |
| type= destinations[index]; |
| if (unitRewriter.getCu().equals(type.getJavaScriptUnit())) { |
| IMember member= null; |
| MemberVisibilityAdjustor adjustor= null; |
| AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, unitRewriter.getRoot()); |
| for (int offset= infos.length - 1; offset >= 0; offset--) { |
| member= infos[offset].getMember(); |
| adjustor= new MemberVisibilityAdjustor(type, member); |
| if (infos[offset].isNewMethodToBeDeclaredAbstract()) |
| adjustor.setIncoming(false); |
| adjustor.setRewrite(sourceRewriter.getASTRewrite(), sourceRewriter.getRoot()); |
| adjustor.setRewrites(rewrites); |
| |
| // TW: set to error if bug 78387 is fixed |
| adjustor.setFailureSeverity(RefactoringStatus.WARNING); |
| |
| adjustor.setStatus(status); |
| adjustor.setAdjustments(adjustments); |
| adjustor.adjustVisibility(new SubProgressMonitor(monitor, 1)); |
| adjustments.remove(member); |
| adjustors.add(adjustor); |
| status.merge(checkProjectCompliance(getCompilationUnitRewrite(rewrites, getDeclaringType().getJavaScriptUnit()), type, new IMember[] {infos[offset].getMember()})); |
| if (infos[offset].isFieldInfo()) { |
| final VariableDeclarationFragment oldField= ASTNodeSearchUtil.getFieldDeclarationFragmentNode((IField) infos[offset].getMember(), sourceRewriter.getRoot()); |
| if (oldField != null) { |
| FieldDeclaration newField= createNewFieldDeclarationNode(infos[offset], sourceRewriter.getRoot(), mapping, unitRewriter.getASTRewrite(), oldField); |
| unitRewriter.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newField, ASTNodes.getInsertionIndex(newField, declaration.bodyDeclarations()), unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PUSH_DOWN)); |
| ImportRewriteUtil.addImports(unitRewriter, oldField.getParent(), new HashMap(), new HashMap(), false); |
| } |
| } else { |
| final FunctionDeclaration oldMethod= ASTNodeSearchUtil.getMethodDeclarationNode((IFunction) infos[offset].getMember(), sourceRewriter.getRoot()); |
| if (oldMethod != null) { |
| FunctionDeclaration newMethod= createNewMethodDeclarationNode(infos[offset], sourceRewriter.getRoot(), mapping, unitRewriter, oldMethod); |
| unitRewriter.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty()).insertAt(newMethod, ASTNodes.getInsertionIndex(newMethod, declaration.bodyDeclarations()), unitRewriter.createCategorizedGroupDescription(RefactoringCoreMessages.HierarchyRefactoring_add_member, SET_PUSH_DOWN)); |
| ImportRewriteUtil.addImports(unitRewriter, oldMethod, new HashMap(), new HashMap(), false); |
| } |
| } |
| } |
| } |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { |
| try { |
| final Map arguments= new HashMap(); |
| String project= null; |
| final IType declaring= getDeclaringType(); |
| final IJavaScriptProject javaProject= declaring.getJavaScriptProject(); |
| if (javaProject != null) |
| project= javaProject.getElementName(); |
| int flags= JavaScriptRefactoringDescriptor.JAR_MIGRATION | JavaScriptRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; |
| try { |
| if (declaring.isLocal() || declaring.isAnonymous()) |
| flags|= JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| } catch (JavaScriptModelException exception) { |
| JavaScriptPlugin.log(exception); |
| } |
| final String description= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description_short_multi, fMembersToMove[0].getElementName()) : RefactoringCoreMessages.PushDownRefactoring_descriptor_description_short; |
| final String header= fMembersToMove.length == 1 ? Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description_full, new String[] { JavaScriptElementLabels.getElementLabel(fMembersToMove[0], JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getElementLabel(declaring, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }) : Messages.format(RefactoringCoreMessages.PushDownRefactoring_descriptor_description, new String[] { JavaScriptElementLabels.getElementLabel(declaring, JavaScriptElementLabels.ALL_FULLY_QUALIFIED) }); |
| final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); |
| final String[] settings= new String[fMembersToMove.length]; |
| for (int index= 0; index < settings.length; index++) |
| settings[index]= JavaScriptElementLabels.getElementLabel(fMembersToMove[index], JavaScriptElementLabels.ALL_FULLY_QUALIFIED); |
| comment.addSetting(JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.PushDownRefactoring_pushed_members_pattern, settings)); |
| addSuperTypeSettings(comment, true); |
| final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.PUSH_DOWN, project, description, comment.asString(), arguments, flags); |
| if (fCachedDeclaringType != null) |
| arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCachedDeclaringType)); |
| for (int index= 0; index < fMembersToMove.length; index++) { |
| arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + (index + 1), descriptor.elementToHandle(fMembersToMove[index])); |
| for (int offset= 0; offset < fMemberInfos.length; offset++) { |
| if (fMemberInfos[offset].getMember().equals(fMembersToMove[index])) { |
| switch (fMemberInfos[offset].getAction()) { |
| case MemberActionInfo.PUSH_ABSTRACT_ACTION: |
| arguments.put(ATTRIBUTE_ABSTRACT + (index + 1), Boolean.valueOf(true).toString()); |
| break; |
| case MemberActionInfo.PUSH_DOWN_ACTION: |
| arguments.put(ATTRIBUTE_PUSH + (index + 1), Boolean.valueOf(true).toString()); |
| break; |
| } |
| } |
| } |
| } |
| return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.PushDownRefactoring_change_name, fChangeManager.getAllChanges()); |
| } finally { |
| pm.done(); |
| clearCaches(); |
| } |
| } |
| |
| private TextEditBasedChangeManager createChangeManager(final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException { |
| Assert.isNotNull(monitor); |
| Assert.isNotNull(status); |
| try { |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, 7); |
| final IJavaScriptUnit source= getDeclaringType().getJavaScriptUnit(); |
| final CompilationUnitRewrite sourceRewriter= new CompilationUnitRewrite(source); |
| final Map rewrites= new HashMap(2); |
| rewrites.put(source, sourceRewriter); |
| IType[] types= getHierarchyOfDeclaringClass(new SubProgressMonitor(monitor, 1)).getSubclasses(getDeclaringType()); |
| final Set result= new HashSet(types.length + 1); |
| for (int index= 0; index < types.length; index++) |
| result.add(types[index].getJavaScriptUnit()); |
| result.add(source); |
| final Map adjustments= new HashMap(); |
| final List adjustors= new ArrayList(); |
| final IJavaScriptUnit[] units= (IJavaScriptUnit[]) result.toArray(new IJavaScriptUnit[result.size()]); |
| IJavaScriptUnit unit= null; |
| CompilationUnitRewrite rewrite= null; |
| final IProgressMonitor sub= new SubProgressMonitor(monitor, 4); |
| try { |
| sub.beginTask(RefactoringCoreMessages.PushDownRefactoring_checking, units.length * 4); |
| for (int index= 0; index < units.length; index++) { |
| unit= units[index]; |
| rewrite= getCompilationUnitRewrite(rewrites, unit); |
| if (unit.equals(sourceRewriter.getCu())) { |
| final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(getDeclaringType(), rewrite.getRoot()); |
| if (!JdtFlags.isAbstract(getDeclaringType()) && getAbstractDeclarationInfos().length != 0) |
| ModifierRewrite.create(rewrite.getASTRewrite(), declaration).setModifiers((Modifier.ABSTRACT | declaration.getModifiers()), rewrite.createCategorizedGroupDescription(RefactoringCoreMessages.PushDownRefactoring_make_abstract, SET_PUSH_DOWN)); |
| deleteDeclarationNodes(sourceRewriter, false, rewrite, Arrays.asList(getDeletableMembers()), SET_PUSH_DOWN); |
| MemberActionInfo[] methods= getAbstractDeclarationInfos(); |
| for (int offset= 0; offset < methods.length; offset++) |
| declareMethodAbstract(methods[offset], sourceRewriter, rewrite); |
| } |
| final IMember[] members= getAbstractMembers(getAbstractDestinations(new SubProgressMonitor(monitor, 1))); |
| final IType[] classes= new IType[members.length]; |
| for (int offset= 0; offset < members.length; offset++) |
| classes[offset]= (IType) members[offset]; |
| copyMembers(adjustors, adjustments, rewrites, status, getAbstractMemberInfos(), classes, sourceRewriter, rewrite, sub); |
| copyMembers(adjustors, adjustments, rewrites, status, getEffectedMemberInfos(), getAbstractDestinations(new SubProgressMonitor(monitor, 1)), sourceRewriter, rewrite, sub); |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| } |
| } finally { |
| sub.done(); |
| } |
| if (!adjustors.isEmpty() && !adjustments.isEmpty()) { |
| final MemberVisibilityAdjustor adjustor= (MemberVisibilityAdjustor) adjustors.get(0); |
| adjustor.rewriteVisibility(new SubProgressMonitor(monitor, 1)); |
| } |
| final TextEditBasedChangeManager manager= new TextEditBasedChangeManager(); |
| for (final Iterator iterator= rewrites.keySet().iterator(); iterator.hasNext();) { |
| unit= (IJavaScriptUnit) iterator.next(); |
| rewrite= (CompilationUnitRewrite) rewrites.get(unit); |
| if (rewrite != null) |
| manager.manage(unit, rewrite.createChange()); |
| } |
| return manager; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private FieldDeclaration createNewFieldDeclarationNode(MemberActionInfo info, JavaScriptUnit declaringCuNode, TypeVariableMaplet[] mapping, ASTRewrite rewrite, VariableDeclarationFragment oldFieldFragment) throws JavaScriptModelException { |
| Assert.isTrue(info.isFieldInfo()); |
| IField field= (IField) info.getMember(); |
| AST ast= rewrite.getAST(); |
| VariableDeclarationFragment newFragment= ast.newVariableDeclarationFragment(); |
| newFragment.setExtraDimensions(oldFieldFragment.getExtraDimensions()); |
| Expression initializer= oldFieldFragment.getInitializer(); |
| if (initializer != null) { |
| Expression newInitializer= null; |
| if (mapping.length > 0) |
| newInitializer= createPlaceholderForExpression(initializer, field.getJavaScriptUnit(), mapping, rewrite); |
| else |
| newInitializer= createPlaceholderForExpression(initializer, field.getJavaScriptUnit(), rewrite); |
| newFragment.setInitializer(newInitializer); |
| } |
| newFragment.setName(ast.newSimpleName(oldFieldFragment.getName().getIdentifier())); |
| FieldDeclaration newField= ast.newFieldDeclaration(newFragment); |
| FieldDeclaration oldField= ASTNodeSearchUtil.getFieldDeclarationNode(field, declaringCuNode); |
| if (info.copyJavadocToCopiesInSubclasses()) |
| copyJavadocNode(rewrite, field, oldField, newField); |
| copyAnnotations(oldField, newField); |
| newField.modifiers().addAll(ASTNodeFactory.newModifiers(ast, info.getNewModifiersForCopyInSubclass(oldField.getModifiers()))); |
| Type oldType= oldField.getType(); |
| IJavaScriptUnit cu= field.getJavaScriptUnit(); |
| Type newType= null; |
| if (mapping.length > 0) { |
| newType= createPlaceholderForType(oldType, cu, mapping, rewrite); |
| } else |
| newType= createPlaceholderForType(oldType, cu, rewrite); |
| newField.setType(newType); |
| return newField; |
| } |
| |
| private FunctionDeclaration createNewMethodDeclarationNode(MemberActionInfo info, JavaScriptUnit declaringCuNode, TypeVariableMaplet[] mapping, CompilationUnitRewrite rewriter, FunctionDeclaration oldMethod) throws JavaScriptModelException { |
| Assert.isTrue(!info.isFieldInfo()); |
| IFunction method= (IFunction) info.getMember(); |
| ASTRewrite rewrite= rewriter.getASTRewrite(); |
| AST ast= rewrite.getAST(); |
| FunctionDeclaration newMethod= ast.newFunctionDeclaration(); |
| copyBodyOfPushedDownMethod(rewrite, method, oldMethod, newMethod, mapping); |
| newMethod.setConstructor(oldMethod.isConstructor()); |
| newMethod.setExtraDimensions(oldMethod.getExtraDimensions()); |
| if (info.copyJavadocToCopiesInSubclasses()) |
| copyJavadocNode(rewrite, method, oldMethod, newMethod); |
| final IJavaScriptProject project= rewriter.getCu().getJavaScriptProject(); |
| copyAnnotations(oldMethod, newMethod); |
| newMethod.modifiers().addAll(ASTNodeFactory.newModifiers(ast, info.getNewModifiersForCopyInSubclass(oldMethod.getModifiers()))); |
| newMethod.setName(ast.newSimpleName(oldMethod.getName().getIdentifier())); |
| copyReturnType(rewrite, method.getJavaScriptUnit(), oldMethod, newMethod, mapping); |
| copyParameters(rewrite, method.getJavaScriptUnit(), oldMethod, newMethod, mapping); |
| copyThrownExceptions(oldMethod, newMethod); |
| return newMethod; |
| } |
| |
| private void declareMethodAbstract(MemberActionInfo info, CompilationUnitRewrite sourceRewrite, CompilationUnitRewrite unitRewrite) throws JavaScriptModelException { |
| Assert.isTrue(!info.isFieldInfo()); |
| IFunction method= (IFunction) info.getMember(); |
| if (JdtFlags.isAbstract(method)) |
| return; |
| final FunctionDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(method, sourceRewrite.getRoot()); |
| unitRewrite.getASTRewrite().remove(declaration.getBody(), null); |
| sourceRewrite.getImportRemover().registerRemovedNode(declaration.getBody()); |
| ModifierRewrite.create(unitRewrite.getASTRewrite(), declaration).setModifiers(info.getNewModifiersForOriginal(declaration.getModifiers()), null); |
| } |
| |
| private MemberActionInfo[] getAbstractDeclarationInfos() throws JavaScriptModelException { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int index= 0; index < fMemberInfos.length; index++) { |
| MemberActionInfo info= fMemberInfos[index]; |
| if (info.isNewMethodToBeDeclaredAbstract()) |
| result.add(info); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| private IType[] getAbstractDestinations(IProgressMonitor monitor) throws JavaScriptModelException { |
| IType[] allDirectSubclasses= getHierarchyOfDeclaringClass(monitor).getSubclasses(getDeclaringType()); |
| List result= new ArrayList(allDirectSubclasses.length); |
| for (int index= 0; index < allDirectSubclasses.length; index++) { |
| IType subclass= allDirectSubclasses[index]; |
| if (subclass.exists() && !subclass.isBinary() && !subclass.isReadOnly() && subclass.getJavaScriptUnit() != null && subclass.isStructureKnown()) |
| result.add(subclass); |
| } |
| return (IType[]) result.toArray(new IType[result.size()]); |
| } |
| |
| private MemberActionInfo[] getAbstractMemberInfos() throws JavaScriptModelException { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int index= 0; index < fMemberInfos.length; index++) { |
| MemberActionInfo info= fMemberInfos[index]; |
| if (info.isToBeCreatedInSubclassesOfDeclaringClass() && JdtFlags.isAbstract(info.getMember())) |
| result.add(info); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| public IMember[] getAdditionalRequiredMembers(IProgressMonitor monitor) throws JavaScriptModelException { |
| IMember[] members= MemberActionInfo.getMembers(getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass()); |
| monitor.beginTask(RefactoringCoreMessages.PushDownRefactoring_calculating_required, members.length);// not |
| // true, |
| // but |
| // not |
| // easy |
| // to |
| // give |
| // anything |
| // better |
| List queue= new ArrayList(members.length); |
| queue.addAll(Arrays.asList(members)); |
| if (queue.isEmpty()) |
| return new IMember[0]; |
| int i= 0; |
| IMember current; |
| do { |
| current= (IMember) queue.get(i); |
| addAllRequiredPushableMembers(queue, current, new SubProgressMonitor(monitor, 1)); |
| i++; |
| if (queue.size() == i) |
| current= null; |
| } while (current != null); |
| queue.removeAll(Arrays.asList(members));// report only additional |
| return (IMember[]) queue.toArray(new IMember[queue.size()]); |
| } |
| |
| private IMember[] getDeletableMembers() { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isToBeDeletedFromDeclaringClass()) |
| result.add(info.getMember()); |
| } |
| return (IMember[]) result.toArray(new IMember[result.size()]); |
| } |
| |
| private MemberActionInfo[] getEffectedMemberInfos() throws JavaScriptModelException { |
| List result= new ArrayList(fMemberInfos.length); |
| for (int i= 0; i < fMemberInfos.length; i++) { |
| MemberActionInfo info= fMemberInfos[i]; |
| if (info.isToBeCreatedInSubclassesOfDeclaringClass() && !JdtFlags.isAbstract(info.getMember())) |
| result.add(info); |
| } |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Object[] getElements() { |
| return fMembersToMove; |
| } |
| |
| private ITypeHierarchy getHierarchyOfDeclaringClass(IProgressMonitor monitor) throws JavaScriptModelException { |
| try { |
| if (fCachedClassHierarchy != null) |
| return fCachedClassHierarchy; |
| fCachedClassHierarchy= getDeclaringType().newTypeHierarchy(monitor); |
| return fCachedClassHierarchy; |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getIdentifier() { |
| return IDENTIFIER; |
| } |
| |
| private MemberActionInfo[] getInfosForMembersToBeCreatedInSubclassesOfDeclaringClass() throws JavaScriptModelException { |
| MemberActionInfo[] abs= getAbstractMemberInfos(); |
| MemberActionInfo[] nonabs= getEffectedMemberInfos(); |
| List result= new ArrayList(abs.length + nonabs.length); |
| result.addAll(Arrays.asList(abs)); |
| result.addAll(Arrays.asList(nonabs)); |
| return (MemberActionInfo[]) result.toArray(new MemberActionInfo[result.size()]); |
| } |
| |
| public MemberActionInfo[] getMemberActionInfos() { |
| return fMemberInfos; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getProcessorName() { |
| return RefactoringCoreMessages.PushDownRefactoring_name; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public RefactoringStatus initialize(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.PUSH_DOWN); |
| else |
| fCachedDeclaringType= (IType) element; |
| } |
| int count= 1; |
| final List elements= new ArrayList(); |
| final List infos= 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.PUSH_DOWN)); |
| else |
| elements.add(element); |
| if (extended.getAttribute(ATTRIBUTE_ABSTRACT + count) != null) |
| infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.PUSH_ABSTRACT_ACTION)); |
| else if (extended.getAttribute(ATTRIBUTE_PUSH + count) != null) |
| infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.PUSH_DOWN_ACTION)); |
| else |
| infos.add(MemberActionInfo.create((IMember) element, MemberActionInfo.NO_ACTION)); |
| count++; |
| attribute= JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + count; |
| } |
| fMembersToMove= (IMember[]) elements.toArray(new IMember[elements.size()]); |
| fMemberInfos= (MemberActionInfo[]) infos.toArray(new MemberActionInfo[infos.size()]); |
| if (!status.isOK()) |
| return status; |
| } else |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); |
| return new RefactoringStatus(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean isApplicable() throws CoreException { |
| return RefactoringAvailabilityTester.isPushDownAvailable(fMembersToMove); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| protected void rewriteTypeOccurrences(final TextEditBasedChangeManager manager, final ASTRequestor requestor, final CompilationUnitRewrite rewrite, final IJavaScriptUnit unit, final JavaScriptUnit node, final Set replacements, final IProgressMonitor monitor) throws CoreException { |
| // Not needed |
| } |
| } |