| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.jdt.internal.corext.refactoring.rename; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| 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.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| |
| import org.eclipse.jdt.internal.corext.Assert; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| |
| public class RenameVirtualMethodProcessor extends RenameMethodProcessor { |
| |
| private IMethod fOriginalMethod; |
| private boolean fActivationChecked; |
| private ITypeHierarchy fCachedHierarchy= null; |
| |
| public RenameVirtualMethodProcessor(IMethod method) { |
| super(method); |
| fOriginalMethod= getMethod(); |
| } |
| |
| public IMethod getOriginalMethod() { |
| return fOriginalMethod; |
| } |
| |
| private ITypeHierarchy getCachedHierarchy(IType declaring, IProgressMonitor monitor) throws JavaModelException { |
| if (fCachedHierarchy != null && declaring.equals(fCachedHierarchy.getType())) |
| return fCachedHierarchy; |
| fCachedHierarchy= declaring.newTypeHierarchy(new SubProgressMonitor(monitor, 1)); |
| return fCachedHierarchy; |
| } |
| |
| public boolean isApplicable() throws CoreException { |
| return RefactoringAvailabilityTester.isRenameVirtualMethodAvailable(getMethod()); |
| } |
| |
| //------------ preconditions ------------- |
| |
| public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException { |
| RefactoringStatus result= super.checkInitialConditions(monitor); |
| if (result.hasFatalError()) |
| return result; |
| try{ |
| monitor.beginTask("", 3); //$NON-NLS-1$ |
| if (!fActivationChecked) { |
| fActivationChecked= true; |
| |
| IMethod method= getMethod(); |
| // super check activation might change the method to be changed. |
| fOriginalMethod= method; |
| |
| ITypeHierarchy hierarchy= null; |
| final IType declaringType= method.getDeclaringType(); |
| if (!declaringType.isInterface()) { |
| hierarchy= getCachedHierarchy(declaringType, new SubProgressMonitor(monitor, 1)); |
| IMethod inInterface= MethodChecks.isDeclaredInInterface(method, hierarchy, monitor); |
| if (inInterface != null && !inInterface.equals(method)) { |
| initialize(inInterface); |
| return result; |
| } |
| } |
| if (hierarchy == null) |
| hierarchy= declaringType.newSupertypeHierarchy(monitor); |
| IMethod overrides= MethodChecks.overridesAnotherMethod(method, hierarchy); |
| if (overrides != null && !overrides.equals(method)) { |
| initialize(overrides); |
| return result; |
| } |
| } |
| } finally{ |
| monitor.done(); |
| } |
| return result; |
| } |
| |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext checkContext) throws CoreException { |
| try{ |
| pm.beginTask("", 9); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| |
| result.merge(super.checkFinalConditions(new SubProgressMonitor(pm, 7), checkContext)); |
| if (result.hasFatalError()) |
| return result; |
| |
| final IMethod method= getMethod(); |
| final IType declaring= method.getDeclaringType(); |
| final ITypeHierarchy hierarchy= getCachedHierarchy(declaring, new SubProgressMonitor(pm, 1)); |
| final String name= getNewElementName(); |
| if (declaring.isInterface()) { |
| if (isSpecialCase()) |
| result.addError(RefactoringCoreMessages.RenameMethodInInterfaceRefactoring_special_case); |
| pm.worked(1); |
| IMethod[] relatedMethods= relatedTypeDeclaresMethodName(new SubProgressMonitor(pm, 1), method, name); |
| for (int i= 0; i < relatedMethods.length; i++) { |
| IMethod relatedMethod= relatedMethods[i]; |
| RefactoringStatusContext context= JavaStatusContext.create(relatedMethod); |
| result.addError(RefactoringCoreMessages.RenameMethodInInterfaceRefactoring_already_defined, context); |
| } |
| } else { |
| if (classesDeclareOverridingNativeMethod(hierarchy.getAllSubtypes(declaring))) { |
| result.addError(Messages.format( |
| RefactoringCoreMessages.RenameVirtualMethodRefactoring_requieres_renaming_native, //$NON-NLS-1$ |
| new String[]{method.getElementName(), "UnsatisfiedLinkError"})); //$NON-NLS-1$ |
| } |
| |
| IMethod[] hierarchyMethods= hierarchyDeclaresMethodName(new SubProgressMonitor(pm, 1), hierarchy, method, name); |
| for (int i= 0; i < hierarchyMethods.length; i++) { |
| IMethod hierarchyMethod= hierarchyMethods[i]; |
| RefactoringStatusContext context= JavaStatusContext.create(hierarchyMethod); |
| if (Checks.compareParamTypes(method.getParameterTypes(), hierarchyMethod.getParameterTypes())) { |
| result.addError(Messages.format( |
| RefactoringCoreMessages.RenameVirtualMethodRefactoring_hierarchy_declares2, //$NON-NLS-1$ |
| name), context); |
| } else { |
| result.addWarning(Messages.format( |
| RefactoringCoreMessages.RenameVirtualMethodRefactoring_hierarchy_declares1, //$NON-NLS-1$ |
| name), context); |
| } |
| } |
| } |
| fCachedHierarchy= null; |
| return result; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| //---- Interface checks ------------------------------------- |
| |
| private IMethod[] relatedTypeDeclaresMethodName(IProgressMonitor pm, IMethod method, String newName) throws CoreException { |
| try{ |
| Set result= new HashSet(); |
| Set types= getRelatedTypes(); |
| pm.beginTask("", types.size()); //$NON-NLS-1$ |
| for (Iterator iter= types.iterator(); iter.hasNext(); ) { |
| final IMethod found= Checks.findMethod(method, (IType)iter.next()); |
| final IType declaring= found.getDeclaringType(); |
| result.addAll(Arrays.asList(hierarchyDeclaresMethodName(new SubProgressMonitor(pm, 1), declaring.newTypeHierarchy(new SubProgressMonitor(pm, 1)), found, newName))); |
| } |
| return (IMethod[]) result.toArray(new IMethod[result.size()]); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private boolean isSpecialCase() throws CoreException { |
| String[] noParams= new String[0]; |
| String[] specialNames= new String[]{"toString", "toString", "toString", "toString", "equals", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "equals", "getClass", "getClass", "hashCode", "notify", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "notifyAll", "wait", "wait", "wait"}; //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| String[][] specialParamTypes= new String[][]{noParams, noParams, noParams, noParams, |
| {"QObject;"}, {"Qjava.lang.Object;"}, noParams, noParams, //$NON-NLS-2$ //$NON-NLS-1$ |
| noParams, noParams, noParams, {Signature.SIG_LONG, Signature.SIG_INT}, |
| {Signature.SIG_LONG}, noParams}; |
| String[] specialReturnTypes= new String[]{"QString;", "QString;", "Qjava.lang.String;", "Qjava.lang.String;", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| Signature.SIG_BOOLEAN, Signature.SIG_BOOLEAN, "QClass;", "Qjava.lang.Class;", //$NON-NLS-2$ //$NON-NLS-1$ |
| Signature.SIG_INT, Signature.SIG_VOID, Signature.SIG_VOID, Signature.SIG_VOID, |
| Signature.SIG_VOID, Signature.SIG_VOID}; |
| Assert.isTrue((specialNames.length == specialParamTypes.length) && (specialParamTypes.length == specialReturnTypes.length)); |
| for (int i= 0; i < specialNames.length; i++){ |
| if (specialNames[i].equals(getNewElementName()) |
| && Checks.compareParamTypes(getMethod().getParameterTypes(), specialParamTypes[i]) |
| && !specialReturnTypes[i].equals(getMethod().getReturnType())){ |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Set getRelatedTypes() { |
| Set methods= getMethodsToRename(); |
| Set result= new HashSet(methods.size()); |
| for (Iterator iter= methods.iterator(); iter.hasNext(); ){ |
| result.add(((IMethod)iter.next()).getDeclaringType()); |
| } |
| return result; |
| } |
| |
| //---- Class checks ------------------------------------- |
| |
| private boolean classesDeclareOverridingNativeMethod(IType[] classes) throws CoreException { |
| for (int i= 0; i < classes.length; i++){ |
| IMethod[] methods= classes[i].getMethods(); |
| for (int j= 0; j < methods.length; j++){ |
| if ((!methods[j].equals(getMethod())) |
| && (JdtFlags.isNative(methods[j])) |
| && (null != Checks.findSimilarMethod(getMethod(), new IMethod[]{methods[j]}))) |
| return true; |
| } |
| } |
| return false; |
| } |
| } |