All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.libj.util.Classes Maven / Gradle / Ivy

Go to download

Supplementary utilities for classes that belong to java.util, or are considered essential as to justify existence in java.util.

There is a newer version: 0.9.1
Show newest version
/* Copyright (c) 2006 LibJ
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * You should have received a copy of The MIT License (MIT) along with this
 * program. If not, see .
 */

package org.libj.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericSignatureFormatError;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
 * Utility providing implementations of methods missing from the API of
 * {@link Class}.
 */
public final class Classes {
  /**
   * Returns the name of the declaring class of the specified class name.
   * 
    *
  • If the specified class name represents an inner class, the name of the * declaring class will be returned.
  • *
  • If the specified class name represents a regular class, the specified * class name will be returned. *
*
* * * * * * * * * *
Examples
classNamereturns
{@code foo.bar.One}{@code foo.bar.One}
{@code foo.bar.One$Two}{@code foo.bar.One}
{@code foo.bar.One$Two$Three}{@code foo.bar.One$Two}
{@code foo.bar.One.$Two$}{@code foo.bar.One.$Two$}
{@code foo.bar.One.$Two$$Three$$$Four}{@code foo.bar.One.$Two$$Three}
{@code foo.bar.One.$Two.$$Three$}{@code foo.bar.One.$Two.$$Three$}
*
* * @param className The class name for which to return the name of the * declaring class. * @return The name of the declaring class of the specified class name. * @throws IllegalArgumentException If {@code className} is not a valid * Java * Identifier. * @throws NullPointerException If {@code className} is null. */ public static String getDeclaringClassName(final String className) { if (!Identifiers.isValid(className)) throw new IllegalArgumentException("Not a valid java identifier: " + className); int index = className.length() - 1; for (char ch; (index = className.lastIndexOf('$', index - 1)) > 1 && ((ch = className.charAt(index - 1)) == '.' || ch == '$');); return index <= 0 ? className : className.substring(0, index); } /** * Returns the name of the root declaring class for the specified class name. *
    *
  • If the specified class name represents an inner class of an inner class * of an inner class, the name of the root declaring class will be returned * (i.e. the name of the class corresponding to the name of the {@code .java} * file in which the inner class is defined).
  • *
  • If the specified class name represents a regular class, the specified * class name will be returned. *
*
* * * * * * * * * *
Examples
classNamereturns
{@code foo.bar.One}{@code foo.bar.One}
{@code foo.bar.One$Two}{@code foo.bar.One}
{@code foo.bar.One$Two$Three}{@code foo.bar.One}
{@code foo.bar.One.$Two$}{@code foo.bar.One.$Two$}
{@code foo.bar.One.$Two$$Three$$$Four}{@code foo.bar.One.$Two}
{@code foo.bar.One.$Two.$$Three$}{@code foo.bar.One.$Two.$$Three$}
*
* * @param className The class name for which to return the name of the root * declaring class. * @return The name of the root declaring class for the specified class name. * @throws IllegalArgumentException If {@code className} is not a valid * Java * Identifier. * @throws NullPointerException If {@code className} is null. */ public static String getRootDeclaringClassName(final String className) { if (!Identifiers.isValid(className)) throw new IllegalArgumentException("Not a valid java identifier: " + className); final int limit = className.length() - 1; int index = 0; while ((index = className.indexOf('$', index + 1)) > 1) { final char ch = className.charAt(index - 1); if (index == limit) return className; if (ch != '.' && ch != '$') break; } return index == -1 ? className : className.substring(0, index); } /** * Returns the canonical name of the specified class name, as defined by the Java * Language Specification. *
* * * * * * * * * *
Examples
classNamereturns
{@code foo.bar.One}{@code foo.bar.One}
{@code foo.bar.One$Two}{@code foo.bar.One.Two}
{@code foo.bar.One$Two$Three}{@code foo.bar.One.Two.Three}
{@code foo.bar.One.$Two$}{@code foo.bar.One.$Two$}
{@code foo.bar.One.$Two$$Three$$$Four}{@code foo.bar.One.$Two.$Three.$$Four}
{@code foo.bar.One.$Two.$$Three$}{@code foo.bar.One.$Two.$$Three$}
*
* * @param className The class name. * @return The canonical name of the underlying specified class name. * @throws IllegalArgumentException If {@code className} is not a valid * Java * Identifier. * @throws NullPointerException If {@code className} is null. * @see 6.7. * Fully Qualified Names and Canonical Names */ public static String toCanonicalClassName(final String className) { if (!Identifiers.isValid(className)) throw new IllegalArgumentException("Not a valid java identifier: " + className); final StringBuilder builder = new StringBuilder(); builder.append(className.charAt(0)); builder.append(className.charAt(1)); char last = '\0'; for (int i = 2; i < className.length() - 1; ++i) { final char ch = className.charAt(i); builder.append(last != '.' && last != '$' && ch == '$' ? '.' : ch); last = ch; } if (className.length() > 2) builder.append(className.charAt(className.length() - 1)); return builder.toString(); } /** * Returns the "Compound Name" of the class or interface represented by * {@code cls}. *

* The "Compound Name" is the fully qualified name of a class * ({@link Class#getName()} with its package name * ({@link Class#getPackage()}.getName()) removed. *

* For example: *

    *
  1. The "Compound Name" of {@code java.lang.String} is {@code String}.
  2. *
  3. The "Compound Name" of {@code java.lang.Map.Entry} is * {@code Map$Entry}.
  4. *
* * @param cls The class or interface. * @return The "Compound Name" of the class or interface represented by * {@code cls}. * @throws NullPointerException If {@code cls} is null. */ public static String getCompoundName(final Class cls) { final String pkg = cls.getPackage().getName(); return pkg.length() == 0 ? cls.getName() : cls.getName().substring(pkg.length() + 1); } /** * Returns the canonical "Compound Name" of the class or interface represented * by {@code cls}. *

* The canonical "Compound Name" is the fully qualified name of a class * ({@link Class#getCanonicalName()} with its package name * ({@link Class#getPackage()}.getName()) removed. *

* For example: *

    *
  1. The canonical "Compound Name" of {@code java.lang.String} is * {@code String}.
  2. *
  3. The canonical "Compound Name" of {@code java.lang.Map.Entry} is * {@code Map.Entry}.
  4. *
* * @param cls The class or interface. * @return The canonical "Compound Name" of the class or interface represented * by {@code cls}. * @throws NullPointerException If {@code cls} is null. */ public static String getCanonicalCompoundName(final Class cls) { final String pkg = cls.getPackage().getName(); return pkg.length() == 0 ? cls.getCanonicalName() : cls.getCanonicalName().substring(pkg.length() + 1); } /** * Returns the {@code Class} array most accurately reflecting the actual type * parameters used in the source code for the generic superclass of the * specified {@code Class}, or {@code null} if no generic superclass exist. * * @param cls The {@code Class}. * @return The {@code Class} array most accurately reflecting the actual type * parameters used in the source code for the generic superclass of * the specified {@code Class}, or {@code null} if no generic * superclass exist. * @throws GenericSignatureFormatError If the generic class signature does not * conform to the format specified in The Java™ Virtual * Machine Specification. * @throws TypeNotPresentException If the generic superclass refers to a * non-existent type declaration. * @throws MalformedParameterizedTypeException If the generic superclass * refers to a parameterized type that cannot be instantiated for * any reason. * @throws NullPointerException If {@code cls} is null. */ public static Type[] getSuperclassGenericTypes(final Class cls) { return cls.getGenericSuperclass() instanceof ParameterizedType ? ((ParameterizedType)cls.getGenericSuperclass()).getActualTypeArguments() : null; } /** * Traverses and returns the class hierarchy of the specified {@code Class}. * This method visits the superclasses and superinterfaces with Breadth First * Search. * * @param cls The {@code Class}. * @param forEach The {@link Predicate} called for each visited superclass and * superinterface. If the {@link Predicate} returns {@code false}, * traversal will terminate, and the method will return the set of * classes that had been visited before termination. * @return The class hierarchy of the specified {@code Class}. * @throws NullPointerException If {@code cls} or {@code forEach} is null. */ public static Set> getClassHierarchy(Class cls, final Predicate> forEach) { final Set> visited = new LinkedHashSet<>(); final Queue> queue = new LinkedList<>(); if (!visitSuperclass(cls, queue, visited, forEach)) return visited; do { if (!visitSuperclass(cls.getSuperclass(), queue, visited, forEach)) return visited; for (final Class superInterface : cls.getInterfaces()) if (!visitSuperclass(superInterface, queue, visited, forEach)) return visited; } while ((cls = queue.poll()) != null); return visited; } private static boolean visitSuperclass(final Class cls, final Queue> queue, final Set> visited, final Predicate> forEach) { if (cls == null || !visited.add(cls)) return true; if (!forEach.test(cls)) return false; queue.add(cls); return true; } /** * Returns the array of generic type classes for the specified field. If the * field is not a parameterized type, this method will return an empty array. * * @param field The {@code Field} * @return The array of generic type classes for the specified field. * @throws NullPointerException If {@code field} is null. */ public static Class[] getGenericClasses(final Field field) { final Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return new Class[0]; final Type[] types = ((ParameterizedType)genericType).getActualTypeArguments(); final Class[] classes = new Class[types.length]; for (int i = 0; i < classes.length; ++i) { if (types[i] instanceof Class) classes[i] = (Class)types[i]; else if (types[i] instanceof ParameterizedType) classes[i] = (Class)((ParameterizedType)types[i]).getRawType(); } return classes; } private static Field getField(final Class cls, final String fieldName, final boolean declared) { final Field[] fields = declared ? cls.getDeclaredFields() : cls.getFields(); for (final Field field : fields) if (fieldName.equals(field.getName())) return field; return null; } /** * Returns a {@code Field} object that reflects the specified public member * field of the class or interface represented by {@code cls} (including * inherited fields). The {@code name} parameter is a {@code String} * specifying the simple name of the desired field. *

* The field to be reflected is determined by the algorithm that follows. Let * C be the class or interface represented by this object: *

    *
  1. If C declares a public field with the name specified, that is the field * to be reflected.
  2. *
  3. If no field was found in step 1 above, this algorithm is applied * recursively to each direct superinterface of C. The direct superinterfaces * are searched in the order they were declared.
  4. *
  5. If no field was found in steps 1 and 2 above, and C has a superclass S, * then this algorithm is invoked recursively upon S. If C has no superclass, * then this method returns null.
  6. *
*

* If this {@code Class} object represents an array type, then this method * does not find the {@code length} field of the array type. *

* This method differentiates itself from {@link Class#getField(String)} by * returning null when a field is not found, instead of throwing * {@link NoSuchFieldException}. * * @param cls The class in which to find the public field. * @param name The field name. * @return A {@code Field} object that reflects the specified public member * field of the class or interface represented by {@code cls} * (including inherited fields). The {@code name} parameter is a * {@code String} specifying the simple name of the desired field. * @throws NullPointerException If {@code cls} or {@code name} is null. * @throws SecurityException If a security manager, s, is present and * the caller's class loader is not the same as or an ancestor of * the class loader for the current class and invocation of * {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} * denies access to the package of this class. */ public static Field getField(final Class cls, final String name) { return Classes.getField(cls, name, false); } /** * Returns a {@code Field} object that reflects the specified declared member * field of the class or interface represented by {@code cls} (excluding * inherited fields). The {@code name} parameter is a {@code String} * specifying the simple name of the desired field. *

* Declared fields include public, protected, default (package) access, * and private visibility. *

* If this {@code Class} object represents an array type, then this method * does not find the {@code length} field of the array type. *

* This method differentiates itself from {@link Class#getDeclaredField(String)} by * returning null when a field is not found, instead of throwing * {@link NoSuchFieldException}. * * @param cls The class in which to find the declared field. * @param name The field name. * @return A {@code Field} object that reflects the specified public member * field of the class or interface represented by {@code cls} * (excluding inherited fields). The {@code name} parameter is a * {@code String} specifying the simple name of the desired field. * @throws NullPointerException If {@code cls} or {@code name} is null. * @throws SecurityException If a security manager, s, is present and * the caller's class loader is not the same as or an ancestor of * the class loader for the current class and invocation of * {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} * denies access to the package of this class. */ public static Field getDeclaredField(final Class cls, final String name) { return Classes.getField(cls, name, true); } /** * Returns a {@code Field} object that reflects the specified declared member * field of the class or interface represented by {@code cls} (including * inherited fields). The {@code name} parameter is a {@code String} * specifying the simple name of the desired field. *

* Declared fields include public, protected, default (package) access, * and private visibility. *

* If this {@code Class} object represents an array type, then this method * does not find the {@code length} field of the array type. *

* This method differentiates itself from {@link Class#getDeclaredField(String)} by * returning null when a field is not found, instead of throwing * {@link NoSuchFieldException}. * * @param cls The class in which to find the declared field. * @param name The field name. * @return A {@code Field} object that reflects the specified public member * field of the class or interface represented by {@code cls} * (including inherited fields). The {@code name} parameter is a * {@code String} specifying the simple name of the desired field. * @throws NullPointerException If {@code cls} or {@code name} is null. * @throws SecurityException If a security manager, s, is present and * the caller's class loader is not the same as or an ancestor of * the class loader for the current class and invocation of * {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} * denies access to the package of this class. */ public static Field getDeclaredFieldDeep(Class cls, final String name) { Field field; do field = getField(cls, name, true); while (field == null && (cls = cls.getSuperclass()) != null); return field; } /** * Returns a Constructor object that reflects the specified public constructor * of the class or interface represented by {@code cls} (including inherited * constructors), or null if the constructor is not found. *

* The {@code parameterTypes} parameter is an array of Class objects that * identify the constructor's formal parameter types, in declared order. If * {@code cls} represents an inner class declared in a non-static context, the * formal parameter types include the explicit enclosing instance as the first * parameter. *

* This method differentiates itself from * {@link Class#getConstructor(Class...)} by returning null when a method is * not found, instead of throwing {@link NoSuchMethodException}. * * @param cls The class in which to find the public constructor. * @param parameterTypes The parameter array. * @return A Constructor object that reflects the specified public constructor * of the class or interface represented by {@code cls} (including * inherited constructors), or null if the constructor is not found. * @throws NullPointerException If {@code cls} is null. */ public static Constructor getConstructor(final Class cls, final Class ... parameterTypes) { final Constructor[] constructors = cls.getConstructors(); for (final Constructor constructor : constructors) if (Arrays.equals(constructor.getParameterTypes(), parameterTypes)) return constructor; return null; } /** * Returns a Constructor object that reflects the specified declared * constructor of the class or interface represented by {@code cls} (excluding * inherited constructors), or null if the constructor is not found. *

* Declared constructors include public, protected, default (package) access, * and private visibility. *

* The {@code parameterTypes} parameter is an array of Class objects that * identify the constructor's formal parameter types, in declared order. If * {@code cls} represents an inner class declared in a non-static context, the * formal parameter types include the explicit enclosing instance as the first * parameter. *

* This method differentiates itself from * {@link Class#getDeclaredConstructor(Class...)} by returning null when a * method is not found, instead of throwing {@link NoSuchMethodException}. * * @param cls The class in which to find the declared constructor. * @param parameterTypes The parameter array. * @return A Constructor object that reflects the specified declared * constructor of the class or interface represented by {@code cls} * (excluding inherited constructors), or null if the constructor is * not found. * @throws NullPointerException If {@code cls} is null. */ public static Constructor getDeclaredConstructor(final Class cls, final Class ... parameterTypes) { final Constructor[] constructors = cls.getDeclaredConstructors(); for (final Constructor constructor : constructors) if (Arrays.equals(constructor.getParameterTypes(), parameterTypes)) return constructor; return null; } /** * Changes the annotation value for {@code key} in {@code annotation} to * {@code newValue}, and returns the previous value. * * @param Type parameter of the value. * @param annotation The annotation. * @param key The key. * @param newValue The new value. * @return The previous value assigned to {@code key}. * @throws IllegalArgumentException If {@code newValue} does not match the * required type of the value for {@code key}. * @throws NullPointerException If {@code annotation}, {@code key}, or * {@code newValue} is null. */ @SuppressWarnings("unchecked") public static T setAnnotationValue(final Annotation annotation, final String key, final T newValue) { final Object handler = Proxy.getInvocationHandler(annotation); Objects.requireNonNull(key); Objects.requireNonNull(newValue); final Field field; final Map memberValues; try { field = handler.getClass().getDeclaredField("memberValues"); field.setAccessible(true); memberValues = (Map)field.get(handler); } catch (final IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) { throw new IllegalStateException(e); } final T oldValue = (T)memberValues.get(key); if (oldValue == null) throw new IllegalArgumentException(key + " is not a valid key"); if (newValue.getClass() != oldValue.getClass()) throw new IllegalArgumentException(newValue.getClass().getName() + " does not match the required type " + oldValue.getClass().getName()); memberValues.put(key, newValue); return oldValue; } private interface SuperclassRecurser extends Repeat.Recurser,M,A> { @Override default Class next(final Class container) { return container.getSuperclass(); } } private interface DeclaredFieldRecurser extends SuperclassRecurser { @Override default Field[] members(final Class container) { return container.getDeclaredFields(); } } private interface DeclaredMethodRecurser extends SuperclassRecurser { @Override default Method[] members(final Class container) { return container.getDeclaredMethods(); } } private interface DeclaredClassRecurser extends SuperclassRecurser,A> { @Override default Class[] members(final Class container) { return container.getDeclaredClasses(); } } private static final Repeat.Recurser,Field,Object> declaredFieldRecurser = new DeclaredFieldRecurser() { @Override public boolean test(final Field member, final Object arg) { return true; } }; private static final Repeat.Recurser,Field,Predicate> declaredFieldWithPredicateRecurser = new DeclaredFieldRecurser>() { @Override public boolean test(final Field member, final Predicate arg) { return arg.test(member); } }; private static final Repeat.Recurser,Field,Class> declaredFieldWithAnnotationRecurser = new DeclaredFieldRecurser>() { @Override public boolean test(final Field member, final Class arg) { return member.getAnnotation(arg) != null; } }; private static final Repeat.Recurser,Method,Object> declaredMethodRecurser = new DeclaredMethodRecurser() { @Override public boolean test(final Method member, final Object arg) { return true; } }; private static final Repeat.Recurser,Method,Predicate> declaredMethodWithPredicateRecurser = new DeclaredMethodRecurser>() { @Override public boolean test(final Method member, final Predicate arg) { return arg.test(member); } }; private static final Repeat.Recurser,Method,Class> declaredMethodWithAnnotationRecurser = new DeclaredMethodRecurser>() { @Override public boolean test(final Method member, final Class arg) { return member.getAnnotation(arg) != null; } }; private static final Repeat.Recurser,Class,Class> classWithAnnotationRecurser = new DeclaredClassRecurser>() { @Override public boolean test(final Class member, final Class arg) { return member.getAnnotation(arg) != null; } }; private static final BiPredicate> declaredFieldWithAnnotationFilter = (m, a) -> m.getAnnotation(a) != null; private static final BiPredicate> declaredMethodWithAnnotationFilter = (m, a) -> m.getAnnotation(a) != null; private static final BiPredicate,Class> classWithAnnotationFilter = (m, a) -> m.getAnnotation(a) != null; /** * Returns an array of Field objects declared in {@code cls} (including * inherited fields). *

* Declared fields include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared fields, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited fields are first, and member fields * are last. * * @param cls The class in which to find declared fields. * @return An array of Field objects declared in {@code cls} (including * inherited fields). * @throws NullPointerException If {@code cls} is null. */ public static Field[] getDeclaredFieldsDeep(final Class cls) { return Repeat.Recursive.,Field,Object>inverted(cls, cls.getDeclaredFields(), Field.class, declaredFieldRecurser, null); } /** * Returns an array of Field objects declared in {@code cls} (including * inherited fields), for which the provided {@link Predicate} returns * {@code true}. *

* Declared fields include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared fields, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited fields are first, and member fields * are last. * * @param cls The class in which to find declared fields. * @param predicate The {@link Predicate} used to decide whether the field * should be included in the returned array. * @return An array of Field objects declared in {@code cls} (including * inherited fields). * @throws NullPointerException If {@code cls} or {@code predicate} is null. */ public static Field[] getDeclaredFieldsDeep(final Class cls, final Predicate predicate) { return Repeat.Recursive.,Field,Predicate>inverted(cls, cls.getDeclaredFields(), Field.class, declaredFieldWithPredicateRecurser, Objects.requireNonNull(predicate)); } /** * Returns an array of Field objects declared in {@code cls} (excluding * inherited fields) that have an annotation of {@code annotationType}. *

* Declared fields include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared fields, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are not sorted and are not in any * particular order. * * @param cls The class in which to find declared fields. * @param annotationType The type of the annotation to match. * @return An array of Field objects declared in {@code cls} (excluding * inherited fields) that have an annotation of * {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ public static Field[] getDeclaredFieldsWithAnnotation(final Class cls, final Class annotationType) { return Repeat.Recursive.>ordered(cls.getDeclaredFields(), Field.class, declaredFieldWithAnnotationFilter, Objects.requireNonNull(annotationType)); } /** * Returns an array of Field objects declared in {@code cls} (including * inherited fields) that have an annotation of {@code annotationType}. *

* Declared fields include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared fields, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited fields are first, and member fields * are last. * * @param cls The class in which to find declared fields. * @param annotationType The type of the annotation to match. * @return An array of Field objects declared in {@code cls} (including * inherited fields) that have an annotation of * {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ public static Field[] getDeclaredFieldsWithAnnotationDeep(final Class cls, final Class annotationType) { return Repeat.Recursive.,Field,Class>inverted(cls, cls.getDeclaredFields(), Field.class, declaredFieldWithAnnotationRecurser, Objects.requireNonNull(annotationType)); } /** * Returns a Method object that reflects the specified declared method of the * class or interface represented by {@code cls} (excluding inherited * methods), or null if the method is not found. *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* The {@code name} parameter is a String that specifies the simple name of * the desired method, and the {@code parameterTypes} parameter is an array of * Class objects that identify the method's formal parameter types, in * declared order. If more than one method with the same parameter types is * declared in a class, and one of these methods has a return type that is * more specific than any of the others, that method is returned; otherwise * one of the methods is chosen arbitrarily. If the name is "<init>"or * "<clinit>" this method returns null. If this Class object represents * an array type, then this method does not find the clone() method. *

* This method differentiates itself from * {@link Class#getDeclaredMethod(String,Class...)} by returning null when a * method is not found, instead of throwing {@link NoSuchMethodException}. * * @param cls The class in which to find the declared method. * @param name The simple name of the method. * @param parameterTypes The parameter array. * @return A Method object that reflects the specified declared method of the * class or interface represented by {@code cls} (excluding inherited * methods), or null if the method is not found. * @throws NullPointerException If {@code cls} or {@code name} is null. */ public static Method getDeclaredMethod(final Class cls, final String name, final Class ... parameterTypes) { final Method[] methods = cls.getDeclaredMethods(); for (final Method method : methods) if (name.equals(method.getName()) && Arrays.equals(method.getParameterTypes(), parameterTypes)) return method; return null; } /** * Returns a Method object that reflects the specified declared method of the * class or interface represented by {@code cls} (including inherited * methods), or null if the method is not found. *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* The {@code name} parameter is a String that specifies the simple name of * the desired method, and the {@code parameterTypes} parameter is an array of * Class objects that identify the method's formal parameter types, in * declared order. If more than one method with the same parameter types is * declared in a class, and one of these methods has a return type that is * more specific than any of the others, that method is returned; otherwise * one of the methods is chosen arbitrarily. If the name is "<init>"or * "<clinit>" this method returns null. If this Class object represents * an array type, then this method does not find the clone() method. *

* This method differentiates itself from * {@link Class#getDeclaredMethod(String,Class...)} by returning null when a * method is not found, instead of throwing {@link NoSuchMethodException}. * * @param cls The class in which to find the declared method. * @param name The simple name of the method. * @param parameterTypes The parameter array. * @return A Method object that reflects the specified declared method of the * class or interface represented by {@code cls} (including inherited * methods), or null if the method is not found. * @throws NullPointerException If {@code cls} or {@code name} is null. */ public static Method getDeclaredMethodDeep(Class cls, final String name, final Class ... parameterTypes) { Method method; do method = getDeclaredMethod(cls, name, parameterTypes); while (method == null && (cls = cls.getSuperclass()) != null); return method; } /** * Returns an array of Method objects declared in {@code cls} (including * inherited methods). *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited methods are first, and member methods * are last. * * @param cls The class in which to find declared methods. * @return An array of Method objects declared in {@code cls} (including * inherited methods). * @throws NullPointerException If {@code cls} is null. */ public static Method[] getDeclaredMethodsDeep(final Class cls) { return Repeat.Recursive.,Method,Object>inverted(cls, cls.getDeclaredMethods(), Method.class, declaredMethodRecurser, null); } /** * Returns an array of Method objects declared in {@code cls} (including * inherited methods), for which the provided {@link Predicate} returns * {@code true}. *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited methods are first, and member methods * are last. * * @param cls The class in which to find declared methods. * @param predicate The {@link Predicate} used to decide whether the method * should be included in the returned array. * @return An array of Method objects declared in {@code cls} (including * inherited methods). * @throws NullPointerException If {@code cls} or {@code predicate} is null. */ public static Method[] getDeclaredMethodsDeep(final Class cls, final Predicate predicate) { return Repeat.Recursive.,Method,Predicate>inverted(cls, cls.getDeclaredMethods(), Method.class, declaredMethodWithPredicateRecurser, Objects.requireNonNull(predicate)); } /** * Returns an array of Method objects declared in {@code cls} (excluding * inherited methods) that have an annotation of {@code annotationType}. *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared methods, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are not sorted and are not in any * particular order. * * @param cls The class in which to find declared methods. * @param annotationType The type of the annotation to match. * @return An array of Method objects declared in {@code cls} (excluding * inherited methods) that have an annotation of {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ public static Method[] getDeclaredMethodsWithAnnotation(final Class cls, final Class annotationType) { return Repeat.Recursive.>ordered(cls.getDeclaredMethods(), Method.class, declaredMethodWithAnnotationFilter, Objects.requireNonNull(annotationType)); } /** * Returns an array of Method objects declared in {@code cls} (including * inherited methods) that have an annotation of {@code annotationType}. *

* Declared methods include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited methods are first, and member methods * are last. * * @param cls The class in which to find declared methods. * @param annotationType The type of the annotation to match. * @return An array of Method objects declared in {@code cls} (including * inherited methods) that have an annotation of {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ public static Method[] getDeclaredMethodsWithAnnotationDeep(final Class cls, final Class annotationType) { return Repeat.Recursive.,Method,Class>inverted(cls, cls.getDeclaredMethods(), Method.class, declaredMethodWithAnnotationRecurser, Objects.requireNonNull(annotationType)); } /** * Returns an array of Class objects declared in {@code cls} (excluding * inherited classes) that have an annotation of {@code annotationType}. *

* Declared classes include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents a class or interface with no declared classes, * then this method returns an array of length 0. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are not sorted and are not in any * particular order. * * @param cls The class in which to find declared methods. * @param annotationType The type of the annotation to match. * @return An array of Class objects declared in {@code cls} (excluding * inherited classes) that have an annotation of * {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ @SuppressWarnings("unchecked") public static Class[] getDeclaredClassesWithAnnotation(final Class cls, final Class annotationType) { return Repeat.Recursive.,Class>ordered(cls.getDeclaredClasses(), (Class>)Class.class.getClass(), classWithAnnotationFilter, Objects.requireNonNull(annotationType)); } /** * Returns an array of Class objects declared in {@code cls} (including * inherited classes) that have an annotation of {@code annotationType}. *

* Declared classes include public, protected, default (package) access, and * private visibility. *

* If {@code cls} represents an array type, a primitive type, or void, then * this method returns an array of length 0. *

* The elements in the returned array are sorted reflecting the inheritance * graph of {@code cls}, whereby inherited classes are first, and member classes * are last. * * @param cls The class in which to find declared methods. * @param annotationType The type of the annotation to match. * @return An array of Class objects declared in {@code cls} (including * inherited classes) that have an annotation of {@code annotationType}. * @throws NullPointerException If {@code cls} or {@code annotationType} is null. */ @SuppressWarnings("unchecked") public static Class[] getDeclaredClassesWithAnnotationDeep(final Class cls, final Class annotationType) { return Repeat.Recursive.,Class,Class>inverted(cls, cls.getDeclaredClasses(), (Class>)Class.class.getClass(), classWithAnnotationRecurser, Objects.requireNonNull(annotationType)); } /** * Returns the greatest common superclass of the specified array of classes. * * @param classes The array of classes. * @return The greatest common superclass of the specified array of classes. * @throws IllegalArgumentException If the number of arguments in the * {@code classes} parameter is 0. * @throws NullPointerException If {@code classes}, or a member of * {@code classes} is null. */ public static Class getGreatestCommonSuperclass(final Class ... classes) { if (classes.length == 0) throw new IllegalArgumentException("Argument length must be greater than 0"); if (classes.length == 1) return classes[0]; Class gcc = getGreatestCommonSuperclass(classes[0], classes[1]); for (int i = 2; i < classes.length && gcc != null; ++i) gcc = getGreatestCommonSuperclass(gcc, classes[i]); return gcc; } /** * Returns the greatest common superclass of the classes of the specified * array of objects. * * @param The type parameter of the specified array of objects. * @param objects The array of objects. * @return The greatest common superclass of the classes of the specified * array of objects. * @throws IllegalArgumentException If the number of arguments in the * {@code objects} parameter is 0. * @throws NullPointerException If {@code objects}, or a member of * {@code objects} is null. */ @SafeVarargs public static Class getGreatestCommonSuperclass(final T ... objects) { if (objects.length == 0) throw new IllegalArgumentException("Argument length must be greater than 0"); return getGreatestCommonSuperclass0(objects); } /** * Returns the greatest common superclass of the classes of the specified * {@code Collection} of objects. * * @param The type parameter of the specified {@code Collection} of * objects. * @param objects The array of objects. * @return The greatest common superclass of the classes of the specified * {@code Collection} of objects. * @throws IllegalArgumentException If the number of elements in the * {@code objects} collection is 0. * @throws NullPointerException If {@code objects}, or an element of * {@code objects} is null. */ public static Class getGreatestCommonSuperclass(final Collection objects) { if (objects.size() == 0) throw new IllegalArgumentException("Collection size must be greater than 0"); return getGreatestCommonSuperclass0(objects.toArray()); } private static Class getGreatestCommonSuperclass(Class c1, Class c2) { final Class c0 = c2; do { do if (c1.isAssignableFrom(c2)) return c1; while ((c2 = c2.getSuperclass()) != null); c2 = c0; } while ((c1 = c1.getSuperclass()) != null); return null; } @SafeVarargs private static Class getGreatestCommonSuperclass0(final T ... objects) { if (objects.length == 1) return objects[0].getClass(); Class gcc = getGreatestCommonSuperclass(objects[0].getClass(), objects[1].getClass()); for (int i = 2; i < objects.length && gcc != null; ++i) gcc = getGreatestCommonSuperclass(gcc, objects[i].getClass()); return gcc; } private static class CallingClass extends SecurityManager { @Override public Class[] getClassContext() { return super.getClassContext(); } } /** * Returns the current execution stack as an array of classes. *

* The length of the array is the number of methods on the execution stack. * The element at index {@code 0} is the class of the currently executing * method, the element at index {@code 1} is the class of that method's * caller, and so on. * * @return The current execution stack as an array of classes. */ public static Class[] getExecutionStack() { return ArrayUtil.subArray(new CallingClass().getClassContext(), 3); } private Classes() { } }