/*******************************************************************************
* Copyright (c) 2000, 2006 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.rubypeople.rdt.internal.corext.util;
import java.util.HashMap;
import java.util.Map;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.ITypeHierarchy;
import org.rubypeople.rdt.core.RubyModelException;
public class MethodOverrideTester {
private static class Substitutions {
public static final Substitutions EMPTY_SUBST= new Substitutions();
private HashMap fMap;
public Substitutions() {
fMap= null;
}
public void addSubstitution(String typeVariable, String substitution, String erasure) {
if (fMap == null) {
fMap= new HashMap(3);
}
fMap.put(typeVariable, new String[] { substitution, erasure });
}
private String[] getSubstArray(String typeVariable) {
if (fMap != null) {
return (String[]) fMap.get(typeVariable);
}
return null;
}
public String getSubstitution(String typeVariable) {
String[] subst= getSubstArray(typeVariable);
if (subst != null) {
return subst[0];
}
return null;
}
public String getErasure(String typeVariable) {
String[] subst= getSubstArray(typeVariable);
if (subst != null) {
return subst[1];
}
return null;
}
}
private final IType fFocusType;
private final ITypeHierarchy fHierarchy;
private Map /* <IMethod, Substitutions> */ fMethodSubstitutions;
private Map /* <IType, Substitutions> */ fTypeVariableSubstitutions;
public MethodOverrideTester(IType focusType, ITypeHierarchy hierarchy) {
fFocusType= focusType;
fHierarchy= hierarchy;
fTypeVariableSubstitutions= null;
fMethodSubstitutions= null;
}
public IType getFocusType() {
return fFocusType;
}
public ITypeHierarchy getTypeHierarchy() {
return fHierarchy;
}
/**
* Finds the method that declares the given method. A declaring method is the 'original' method declaration that does
* not override nor implement a method. <code>null</code> is returned it the given method does not override
* a method. When searching, super class are examined before implemented interfaces.
* @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible.
* @throws RubyModelException
*/
public IMethod findDeclaringMethod(IMethod overriding, boolean testVisibility) throws RubyModelException {
IMethod result= null;
IMethod overridden= findOverriddenMethod(overriding, testVisibility);
while (overridden != null) {
result= overridden;
overridden= findOverriddenMethod(result, testVisibility);
}
return result;
}
/**
* Finds the method that is overridden by the given method.
* First the super class is examined and then the implemented interfaces.
* @param testVisibility If true the result is tested on visibility. Null is returned if the method is not visible.
* @throws RubyModelException
*/
public IMethod findOverriddenMethod(IMethod overriding, boolean testVisibility) throws RubyModelException {
if (overriding.getVisibility() == IMethod.PRIVATE || overriding.isSingleton() || overriding.isConstructor()) {
return null;
}
IType type= overriding.getDeclaringType();
IType superClass= fHierarchy.getSuperclass(type);
if (superClass != null) {
IMethod res= findOverriddenMethodInHierarchy(superClass, overriding);
if (res != null && res.getVisibility() != IMethod.PRIVATE) {
if (!testVisibility || RubyModelUtil.isVisibleInHierarchy(res, type.getSourceFolder())) {
return res;
}
}
}
if (!overriding.isConstructor()) {
IType[] interfaces= fHierarchy.getSuperModules(type);
for (int i= 0; i < interfaces.length; i++) {
IMethod res= findOverriddenMethodInHierarchy(interfaces[i], overriding);
if (res != null) {
return res; // methods from interfaces are always public and therefore visible
}
}
}
return null;
}
/**
* Finds the directly overridden method in a type and its super types. First the super class is examined and then the implemented interfaces.
* With generics it is possible that 2 methods in the same type are overidden at the same time. In that case, the first overridden method found is returned.
* @param type The type to find methods in
* @param overriding The overriding method
* @return The first overridden method or <code>null</code> if no method is overridden
* @throws RubyModelException
*/
public IMethod findOverriddenMethodInHierarchy(IType type, IMethod overriding) throws RubyModelException {
IMethod method= findOverriddenMethodInType(type, overriding);
if (method != null) {
return method;
}
IType superClass= fHierarchy.getSuperclass(type);
if (superClass != null) {
IMethod res= findOverriddenMethodInHierarchy(superClass, overriding);
if (res != null) {
return res;
}
}
if (!overriding.isConstructor()) {
IType[] superInterfaces= fHierarchy.getSuperModules(type);
for (int i= 0; i < superInterfaces.length; i++) {
IMethod res= findOverriddenMethodInHierarchy(superInterfaces[i], overriding);
if (res != null) {
return res;
}
}
}
return method;
}
/**
* Finds an overridden method in a type. WWith generics it is possible that 2 methods in the same type are overidden at the same time.
* In that case the first overridden method found is returned.
* @param overriddenType The type to find methods in
* @param overriding The overriding method
* @return The first overridden method or <code>null</code> if no method is overridden
* @throws RubyModelException
*/
public IMethod findOverriddenMethodInType(IType overriddenType, IMethod overriding) throws RubyModelException {
IMethod[] overriddenMethods= overriddenType.getMethods();
for (int i= 0; i < overriddenMethods.length; i++) {
if (isSubsignature(overriding, overriddenMethods[i])) {
return overriddenMethods[i];
}
}
return null;
}
/**
* Finds an overriding method in a type.
* @param overridingType The type to find methods in
* @param overridden The overridden method
* @return The overriding method or <code>null</code> if no method is overriding.
* @throws RubyModelException
*/
public IMethod findOverridingMethodInType(IType overridingType, IMethod overridden) throws RubyModelException {
IMethod[] overridingMethods= overridingType.getMethods();
for (int i= 0; i < overridingMethods.length; i++) {
if (isSubsignature(overridingMethods[i], overridden)) {
return overridingMethods[i];
}
}
return null;
}
/**
* Tests if a method is a subsignature of another method.
* @param overriding overriding method (m1)
* @param overridden overridden method (m2)
* @return <code>true</code> iff the method <code>m1</code> is a subsignature of the method <code>m2</code>.
* This is one of the requirements for m1 to override m2.
* Accessibility and return types are not taken into account.
* Note that subsignature is <em>not</em> symmetric!
* @throws RubyModelException
*/
public boolean isSubsignature(IMethod overriding, IMethod overridden) throws RubyModelException {
if (!overridden.getElementName().equals(overriding.getElementName())) {
return false;
}
int nParameters= overridden.getNumberOfParameters();
if (nParameters != overriding.getNumberOfParameters()) {
return false;
}
return nParameters == 0;
}
}