com.link_intersystems.lang.reflect.Method2.java Source code

Java tutorial

Introduction

Here is the source code for com.link_intersystems.lang.reflect.Method2.java

Source

/**
 * Copyright 2011 Link Intersystems GmbH <rene.link@link-intersystems.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.link_intersystems.lang.reflect;

import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Iterator;

import org.apache.commons.collections4.Predicate;

import com.link_intersystems.lang.Assert;
import com.link_intersystems.lang.Signature;
import com.link_intersystems.lang.reflect.criteria.ClassCriteria;
import com.link_intersystems.lang.reflect.criteria.ClassCriteria.ClassType;
import com.link_intersystems.lang.reflect.criteria.MemberCriteria;
import com.link_intersystems.lang.reflect.criteria.Result;

/**
 * Extends a {@link Method} by wrapping it and provides sophisticated methods
 * for common {@link Method} object based.
 * <p>
 * Features are:
 * <ul>
 * <li>Find out if a method overrides another method.</li>
 * <li>Find out if a method overloads another method.</li>
 * <li>Abstracting a method ({@link Invokable}) so that constructors and methods
 * can be treated in the same way.</li>
 * </ul>
 * </p>
 *
 *
 * @author Ren Link <a
 *         href="mailto:rene.link@link-intersystems.com">[rene.link@link-
 *         intersystems.com]</a>
 * @since 1.0.0.0
 */
public class Method2 extends Member2<Method> {

    /**
     *
     */
    private static final long serialVersionUID = -3824221999295830037L;

    /**
     *
     * @param method
     * @return a {@link Method2} object for the given {@link Method}.
     * @since 1.0.0.0
     */
    public static Method2 forMethod(Method method) {
        Assert.notNull("method", method);
        Class<?> declaringClass = method.getDeclaringClass();
        Class2<?> declaringClass2 = Class2.get(declaringClass);
        Method2 method2;
        try {
            method2 = declaringClass2.getMethod2(method);
            return method2;
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Implementation error.", e);
        }
    }

    private transient Signature signature;

    Method2(Method method) {
        super(method);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj == null) {
            return false;
        } else if (getClass() != obj.getClass()) {
            return false;
        } else {
            Method2 otherMethod2 = (Method2) obj;
            Method member = getMember();
            return member.equals(otherMethod2.getMember());
        }
    }

    /**
     * @return the definition method of this {@link Method2}. The definition is
     *         the first occurrence of the method signature within the class
     *         hierarchy. Interfaces method definitions take precedence over
     *         class method definitions.
     * @since 1.0.0.0
     */
    public Method getDefinition() {
        Class<?> declaringClass = getDeclaringClass();
        ClassCriteria classCriteria = new ClassCriteria();
        classCriteria.setSelection(ClassType.INTERFACES, ClassType.CLASSES);
        classCriteria.setTraverseClassesUniquely(true);
        classCriteria.setSeparatedClassTypeTraversal(true);
        Iterable<Class<?>> classIterable = classCriteria.getIterable(declaringClass);

        MemberCriteria methodCriteria = MemberCriteria.forMemberTypes(Method.class);
        methodCriteria.named(getName());
        Predicate predicate = new SignaturePredicate(this);
        methodCriteria.add(predicate);
        methodCriteria.setResult(Result.FIRST);

        Iterator<Member> iterator = methodCriteria.getIterable(classIterable).iterator();

        Method declaringMethod = (Method) iterator.next();
        return declaringMethod;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.0.0.0
     */
    public Type[] getGenericParameterTypes() {
        return getMember().getGenericParameterTypes();
    }

    /**
     *
     * @param target
     * @return
     * @since 1.2.0.0
     */
    public Invokable getInvokable(Object target) {
        return new Method2Invokable(target, this);
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.0.0.0
     */
    public Class<?>[] getParameterTypes() {
        return getMember().getParameterTypes();
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.0.0.0
     */
    protected Class<?> getReturnType() {
        return getMember().getReturnType();
    }

    /**
     * @return a {@link Signature} that represents this method. Clients might
     *         use the {@link Signature} to compare it to other signatures. The
     *         returned {@link Signature}'s equality attributes are
     *         <ul>
     *         <li>name</li>
     *         <li>parameter types</li>
     *         </ul>
     *         Two {@link Signature}s are equal if these attributes are equal.
     *         Exactly as defined by the java language specificatio, eexcept the
     *         generic part which is not implemented ye.:
     *
     *         <pre>
     *         <h2>8.4.2 Method Signature</h2>
     * It is a compile-time error to declare two methods with override-equivalent signatures (defined below) in a class.
     * Two methods have the same signature if they have the same name and argument types.
     *
     * Two method or constructor declarations M and N have the same argument types if all of the following conditions hold:
     *
     * They have the same number of formal parameters (possibly zero)
     * They have the same number of type parameters (possibly zero)
     *
     * Let <A1,...,An> be the formal type parameters of M and let <B1,...,Bn> be the formal type parameters of N. After renaming
     * each occurrence of a Bi in N's type to Ai the bounds of corresponding type variables and the argument types of M and N
     * are the same.
     *
     * The signature of a method m1 is a subsignature of the signature of a method m2 if either
     *
     * m2 has the same signature as m1, or
     * the signature of m1 is the same as the erasure of the signature of m2.
     * </pre>
     * @since 1.0.0.0
     */
    public Signature getSignature() {
        if (signature == null) {
            signature = new Method2Signature(this);
        }
        return signature;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + getMember().hashCode();
        return result;
    }

    public boolean isOverriddenBy(Method method) {
        Method2 method2 = Method2.forMethod(method);
        return isOverriddenBy(method2);
    }

    public boolean isOverriddenBy(Method2 method) {
        boolean overrides = method.overrides(getMember());
        return overrides;
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.2.0.0
     */
    @Override
    public Class<?>[] getDeclaredExceptionTypes() {
        return getMember().getExceptionTypes();
    }

    /**
     * {@inheritDoc}
     *
     * @since 1.0.0.0
     */
    public boolean isVarArgs() {
        return getMember().isVarArgs();
    }

    /**
     * @param method
     * @return true, if the {@link Method} of this {@link Method2} overloads the
     *         given {@link Method2}. Overloading is determined as specified by
     *         the java specification section 8.4.9.
     *
     *         <pre>
     * <h2>8.4.9 Overloading</h2>
     * If two methods of a class (whether both declared in the same class, or both inherited by a class, or one declared and one
     * inherited) have the same name but signatures that are not override-equivalent, then the method name is said to be
     * overloaded. This fact causes no difficulty and never of itself results in a compile-time error. There is no required
     * relationship between the return types or between the throws clauses of two methods with the same name, unless their
     * signatures are override-equivalent.
     *
     * Methods are overridden on a signature-by-signature basis.
     *
     * If, for example, a class declares two public methods with the same name, and a subclass overrides one of them, the subclass
     * still inherits the other method.
     *
     * When a method is invoked (15.12), the number of actual arguments (and any explicit type arguments) and the compile-time
     * types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (15.12.2).
     * If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time,
     * using dynamic method lookup (15.12.4).
     * <hr/>
     * <h2>8.4.10.2 Example: Overloading, Overriding, and Hiding</h2>
     * In the example:
     *
     *     class Point {
     *        int x = 0, y = 0;
     *        void move(int dx, int dy) { x += dx; y += dy; }
     *        int color;
     *     }
     *     class RealPoint extends Point {
     *        float x = 0.0f, y = 0.0f;
     *        void move(int dx, int dy) { move((float)dx, (float)dy); }
     *        void move(float dx, float dy) { x += dx; y += dy; }
     *     }
     *
     * the class RealPoint hides the declarations of the int instance variables x and y of class Point with its own float instance
     * variables x and y, and overrides the method move of class Point with its own move method. It also overloads the name move
     * with another method with a different signature (8.4.2).
     *
     * In this example, the members of the class RealPoint include the instance variable color inherited from the class Point, the
     * float instance variables x and y declared in RealPoint, and the two move methods declared in RealPoint.
     *
     * Which of these overloaded move methods of class RealPoint will be chosen for any particular method invocation will be
     * determined at compile time by the overloading resolution procedure described in 15.12.
     * </pre>
     * @since 1.0.0.0
     */
    public boolean overloads(Method2 method) {
        Method2 higher = method;
        return isNameEqual(higher) && isReturnTypeAssignableFrom(higher) && !isInterfaceImplementation(higher)
                && !method.overrides(this);
    }

    /**
     *
     * @param method
     * @return true, if the {@link Method} of this {@link Method2} overrides the
     *         given method. The override logic is based upon the java
     *         specification section 8.4.2 Method signature.
     *
     *         <pre>
     *         <h2>8.4.2 Method Signature</h2>
     * It is a compile-time error to declare two methods with override-equivalent signatures (defined below) in a class.
     *
     * Two methods have the same signature if they have the same name and argument types.
     *
     * Two method or constructor declarations M and N have the same argument types if all of the following conditions hold:
     *
     * They have the same number of formal parameters (possibly zero)
     * They have the same number of type parameters (possibly zero)
     *
     * Let <A1,...,An> be the formal type parameters of M and let <B1,...,Bn> be the formal type parameters of N. After renaming
     * each occurrence of a Bi in N's type to Ai the bounds of corresponding type variables and the argument types of M and N
     * are the same.
     *
     * The signature of a method m1 is a subsignature of the signature of a method m2 if either
     *
     * m2 has the same signature as m1, or
     * the signature of m1 is the same as the erasure of the signature of m2.
     *
     * <hr/>
     * <h3>Discussion</h3>
     * The notion of subsignature defined here is designed to express a relationship between two methods whose signatures are
     * not identical, but in which one may override the other.
     *
     * Specifically, it allows a method whose signature does not use generic types to override any generified version of that method.
     * This is important so that library designers may freely generify methods independently of clients that define subclasses or
     * subinterfaces of the library.
     *
     * Consider the example:
     *
     *       class CollectionConverter {
     *           List toList(Collection c) {...}
     *       }
     *       class Overrider extends CollectionConverter{
     * List toList(Collection c) {...}
     *      }
     *
     * Now, assume this code was written before the introduction of genericity, and now the author of class CollectionConverter decides
     * to generify the code, thus:
     *
     *       class CollectionConverter {
     *          <T> List<T> toList(Collection<T> c) {...}
     *       }
     *
     * Without special dispensation, Overrider.toList() would no longer override CollectionConverter.toList(). Instead, the code
     * would be illegal. This would significantly inhibit the use of genericity, since library writers would hesitate to migrate existing code.
     *
     * Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.
     *
     * The example:
     *
     *       class Point implements Move {
     *          int x, y;
     *          abstract void move(int dx, int dy);
     *          void move(int dx, int dy) { x += dx; y += dy; }
     *       }
     *
     * causes a compile-time error because it declares two move methods with the same (and hence, override-equivalent) signature.
     * This is an error even though one of the declarations is abstract.
     * <hr/>
     * <h2>8.4.10.1 Example: Overriding</h2>
     * In the example:
     *
     *      class Point {
     *         int x = 0, y = 0;
     *         void move(int dx, int dy) { x += dx; y += dy; }
     *      }
     *      class SlowPoint extends Point {
     *         int xLimit, yLimit;
     *         void move(int dx, int dy) {
     *            super.move(limit(dx, xLimit), limit(dy, yLimit));
     *         }
     *         static int limit(int d, int limit) {
     *            return d > limit ? limit : d < -limit ? -limit : d;
     *         }
     *      }
     *
     * the class SlowPoint overrides the declarations of method move of class Point with its own move method, which limits the
     * distance that the point can move on each invocation of the method. When the move method is invoked for an instance of
     * class SlowPoint, the overriding definition in class SlowPoint will always be called, even if the reference to the
     * SlowPoint object is taken from a variable whose type is Point.
     * </pre>
     * @since 1.0.0.0
     */
    public boolean overrides(Method method) {
        Method2 referenceMethod = new Method2(method);

        if (referenceMethod.isDeclaringClassIsAnInterface()) {
            return false;
        }

        if (!isNameEqual(referenceMethod)) {
            return false;
        }

        if (!isAccessModifierOverriddingCompatible(referenceMethod)) {
            return false;
        }

        if (!isDeclaringClassAssignableFrom(referenceMethod)) {
            return false;
        }

        if (!areParametersEqual(referenceMethod)) {
            return false;
        }

        if (!isReturnTypeAssignableFrom(referenceMethod)) {
            return false;
        }

        return true;
    }

    /**
     * Same semantics as {@link #overrides(Method)}.
     *
     * @param methodInfo
     * @return
     */
    public boolean overrides(Method2 methodInvokable) {
        return overrides(methodInvokable.getMember());
    }

    @Override
    public String toString() {
        String methodAsString = "Method2: ";
        methodAsString += getMember().toGenericString();
        return methodAsString;
    }

}