| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| |
| *******************************************************************************/ |
| |
| package org.eclipse.dltk.internal.core.util; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.dltk.core.Flags; |
| import org.eclipse.dltk.core.IMethod; |
| import org.eclipse.dltk.core.IType; |
| import org.eclipse.dltk.core.ITypeHierarchy; |
| import org.eclipse.dltk.core.ModelException; |
| |
| public class MethodOverrideTester { |
| |
| private final IType fFocusType; |
| private final ITypeHierarchy fHierarchy; |
| |
| public MethodOverrideTester(IType focusType, ITypeHierarchy hierarchy) { |
| fFocusType = focusType; |
| fHierarchy = hierarchy; |
| |
| } |
| |
| 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 ModelException |
| */ |
| public IMethod findDeclaringMethod(IMethod overriding, |
| boolean testVisibility) throws ModelException { |
| 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 ModelException |
| */ |
| public IMethod findOverriddenMethod(IMethod overriding, |
| boolean testVisibility) throws ModelException { |
| int flags = overriding.getFlags(); |
| if (Flags.isPrivate(flags) || Flags.isStatic(flags) |
| || overriding.isConstructor()) { |
| return null; |
| } |
| |
| IType type = overriding.getDeclaringType(); |
| IType[] superClass = fHierarchy.getSuperclass(type); |
| if (superClass != null) { |
| for (int q = 0; q < superClass.length; ++q) { |
| IMethod res = findOverriddenMethodInHierarchy(superClass[q], |
| overriding); |
| if (res != null && !Flags.isPrivate(res.getFlags())) { |
| |
| return res; |
| } |
| } |
| } |
| |
| 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 ModelException |
| */ |
| public IMethod findOverriddenMethodInHierarchy(IType type, |
| IMethod overriding) throws ModelException { |
| return innerFindOverriddenMethodInHierarchy(type, overriding, |
| new HashSet<IType>()); |
| } |
| |
| private IMethod innerFindOverriddenMethodInHierarchy(IType type, |
| IMethod overriding, Set<IType> processedTypes) |
| throws ModelException { |
| if (!processedTypes.add(type)) { |
| return null; |
| } |
| |
| IMethod method = findOverriddenMethodInType(type, overriding); |
| if (method != null) { |
| return method; |
| } |
| IType[] superClass = fHierarchy.getSuperclass(type); |
| if (superClass != null) { |
| for (int q = 0; q < superClass.length; ++q) { |
| IMethod res = innerFindOverriddenMethodInHierarchy( |
| superClass[q], overriding, processedTypes); |
| 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 ModelException |
| */ |
| public IMethod findOverriddenMethodInType(IType overriddenType, |
| IMethod overriding) throws ModelException { |
| 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 ModelException |
| */ |
| public IMethod findOverridingMethodInType(IType overridingType, |
| IMethod overridden) throws ModelException { |
| 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. Note that subsignature is |
| * <em>not</em> symmetric! |
| * @throws ModelException |
| */ |
| public boolean isSubsignature(IMethod overriding, IMethod overridden) |
| throws ModelException { |
| if (!overridden.getElementName().equals(overriding.getElementName())) { |
| return false; |
| } |
| return true; |
| } |
| } |