001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.test.valueobjects.contract;
017
018import static org.junit.jupiter.api.Assertions.assertNotEquals;
019import static org.junit.jupiter.api.Assertions.assertNotNull;
020
021import java.lang.reflect.Method;
022
023import lombok.experimental.UtilityClass;
024
025/**
026 * Simple Helper for reflection-api. Like many other methods of this
027 * test-framework, the methods use rather asserts / {@link AssertionError} to
028 * check preconditions and states than {@link Exception}
029 *
030 * @author Oliver Wolff
031 */
032@UtilityClass
033public final class ReflectionUtil {
034
035    static final String METHOD_NAME_OBJECT_EQUALS = "equals";
036    static final String METHOD_NAME_OBJECT_HASH_CODE = "hashCode";
037    static final String METHOD_NAME_OBJECT_TO_STRING = "toString";
038
039    /**
040     * Verify the class implements {@link Object#equals(Object)}
041     *
042     * @param clazz class to be checked, msut not be null
043     */
044    public static void assertEqualsMethodIsOverriden(final Class<?> clazz) {
045        // equals method need an object as parameter
046        final Class<?>[] args1 = new Class[1];
047        args1[0] = Object.class;
048
049        // get equals method of the class
050        final var method = getMethodFromClass(clazz, METHOD_NAME_OBJECT_EQUALS, args1);
051        final var assertText = "Method 'equals' not implemented in the class : " + clazz.getName();
052        assertJavaLangObjectMethodWasOverridden(assertText, method);
053    }
054
055    /**
056     * Verify the class implements {@link Object#hashCode()}
057     *
058     * @param clazz class to be checked, must not be null
059     */
060    public static void assertHashCodeMethodIsOverriden(final Class<?> clazz) {
061        final var method = getMethodFromClass(clazz, METHOD_NAME_OBJECT_HASH_CODE, null);
062        final var assertText = "Method 'hashCode' not implemented in the class : " + clazz.getName();
063        assertJavaLangObjectMethodWasOverridden(assertText, method);
064    }
065
066    /**
067     * Verify the class implements {@link Object#hashCode()}
068     *
069     * @param clazz class to be checked, must not be null
070     */
071    public static void assertToStringMethodIsOverriden(final Class<?> clazz) {
072        final var method = getMethodFromClass(clazz, METHOD_NAME_OBJECT_TO_STRING, null);
073        final var assertText = "Method 'toString' not implemented in the class : " + clazz.getName();
074        assertJavaLangObjectMethodWasOverridden(assertText, method);
075    }
076
077    /**
078     * Check if the method is not a {@link Object} implementation
079     *
080     * @param assertText text to display on fail
081     * @param method     {@linkplain Method} to verify
082     */
083    private static void assertJavaLangObjectMethodWasOverridden(final String assertText, final Method method) {
084
085        assertNotNull(method, assertText);
086        // does java.lang.Object provide the method?
087        assertNotEquals(Object.class, method.getDeclaringClass(), assertText);
088    }
089
090    /**
091     * Retrieve method from class according parameters
092     *
093     * @param clazz      class object under test
094     * @param methodName string name of method
095     * @param args1      parameter object for method
096     * @return {@link Method} if exists. if not an assertionError will be thrown.
097     */
098    private static Method getMethodFromClass(final Class<?> clazz, final String methodName, final Class<?>[] args1) {
099        assertNotNull(clazz, "Target for test is null");
100        Method result = null;
101        try {
102            if (null != args1) {
103                result = clazz.getMethod(methodName, args1);
104            } else {
105                result = clazz.getMethod(methodName);
106            }
107        } catch (final SecurityException | NoSuchMethodException e) {
108            throw new AssertionError(e);
109        }
110        assertNotNull(result, "Method " + methodName + " does not exist on " + clazz);
111        return result;
112    }
113}