DefaultMethodOverrideCheck.java

/*
 *
 * The DbUnit Database Testing Framework
 * Copyright (C)2002-2026, DbUnit.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
package org.dbunit.annotation.runtime;

/**
 * Checks whether a type provides its own body for a method it would otherwise inherit -
 * either an interface default method ({@link #overridesDefaultMethod}), or a method a known
 * base class declares ({@link #declaresBelow}). Shared by every {@code org.dbunit.annotation}/
 * {@code org.dbunit.junit.jupiter} call site that needs to fail fast (or otherwise react)
 * when a configured attribute cannot actually reach a non-overriding implementation, instead
 * of silently doing nothing.
 *
 * <p>Public so a binding outside {@code org.dbunit.annotation.runtime} - such as
 * {@code DbUnitExtension} - can reach it too.
 *
 * @author Jeff Jensen
 * @since 3.6.0
 */
public class DefaultMethodOverrideCheck
{
    private DefaultMethodOverrideCheck()
    {
    }

    /**
     * Returns whether {@code implementationType} overrides the named default method
     * {@code declaringInterface} declares, rather than inheriting that interface's own
     * default body.
     *
     * <p>Known limitation: this check only asks which class declares the method, so it
     * cannot distinguish a real override from a dynamic proxy or a mocking-framework-generated
     * subclass - an unstubbed Mockito mock, for instance - that mechanically redeclares every
     * interface method, default ones included, no matter what its generated body actually
     * does. Such an instance reports {@code true} here for every method, silently defeating a
     * fail-fast check built on it. Narrow-audience: only reachable when a test injects such an
     * instance directly, rather than a real implementation.
     *
     * @param implementationType The runtime type to check.
     * @param declaringInterface The interface whose default method {@code methodName} names.
     * @param methodName The method's name.
     * @param parameterTypes The method's parameter types.
     * @return True when {@code implementationType} overrides the method.
     */
    public static boolean overridesDefaultMethod(final Class<?> implementationType,
            final Class<?> declaringInterface, final String methodName,
            final Class<?>... parameterTypes)
    {
        try
        {
            return implementationType.getMethod(methodName, parameterTypes)
                    .getDeclaringClass() != declaringInterface;
        } catch (final NoSuchMethodException e)
        {
            throw new IllegalStateException(declaringInterface.getName()
                    + " guarantees a public " + methodName
                    + " method; this should be unreachable.", e);
        }
    }

    /**
     * Returns whether {@code type}, or any class between it and {@code ceiling} (exclusive),
     * declares the named method - the "does a concrete class redeclare this method below a
     * known base class" question, as distinct from {@link #overridesDefaultMethod}'s "does a
     * type override an interface default". Walks {@code type}'s superclass chain, stopping
     * before {@code ceiling} and before {@link Object}.
     *
     * <p>Unlike {@link #overridesDefaultMethod}, a {@link NoSuchMethodException} for one class
     * in the range is not an error: that class simply does not declare the method, and the
     * walk continues to its superclass.
     *
     * @param type The concrete type to start the walk from.
     * @param ceiling The base class to stop before; a declaration on {@code ceiling} itself
     *            does not count.
     * @param methodName The method's name.
     * @param parameterTypes The method's parameter types.
     * @return True when some class in {@code [type, ceiling)} declares the method.
     */
    public static boolean declaresBelow(final Class<?> type, final Class<?> ceiling,
            final String methodName, final Class<?>... parameterTypes)
    {
        Class<?> current = type;
        while (current != null && current != ceiling && current != Object.class)
        {
            try
            {
                current.getDeclaredMethod(methodName, parameterTypes);
                return true;
            } catch (final NoSuchMethodException e)
            {
                current = current.getSuperclass();
            }
        }
        return false;
    }
}