/* * The MIT License (MIT) * * Copyright (c) 2015 Lachlan Dowding * * 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package permafrost.tundra.lang; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; public final class ClassHelper { /** * Disallow instantiation of this class. */ private ClassHelper() {} /** * Converts the given object to a Class. * * @param object The object to be converted. * @return The converted object. */ public static Class normalize(Object object) { Class value = null; if (object instanceof Class) { value = (Class)object; } else if (object instanceof String) { try { value = Class.forName((String)object); } catch(ClassNotFoundException ex) { throw new RuntimeException(ex); } } return value; } /** * Returns an array of Class objects associated with the class or interface with the given names. * * @param classNames A list of class or interface names. * @return A list of Class objects associated with the given names. * @throws ClassNotFoundException If a class with the given name cannot be found. */ public static Class[] forName(String[] classNames) throws ClassNotFoundException { if (classNames == null) return null; Class[] classes = new Class[classNames.length]; for (int i = 0; i < classNames.length; i++) { if (classNames[i] != null) { classes[i] = Class.forName(classNames[i]); } } return classes; } /** * Returns the one dimensional array class associated with the given component class. * * @param componentClass The component class to use for the array class. * @param <T> The component class of the array. * @return The one dimensional array class associated with the given component class. */ public static <T> Class<?> getArrayClass(Class<T> componentClass) { return getArrayClass(componentClass, 1); } /** * Returns the array class associated with the given component class and number of dimensions. * * @param componentClass The component class to use for the array class. * @param dimensions The array dimensions to use. * @param <T> The component class of the array. * @return The array class associated with the given component class and number of dimensions. */ public static <T> Class<?> getArrayClass(Class<T> componentClass, int dimensions) { if (dimensions < 1) throw new IllegalArgumentException("array dimensions must be >= 1"); int[] dimensionArray = new int[dimensions]; Arrays.fill(dimensionArray, 0); return Array.newInstance(componentClass, dimensionArray).getClass(); } /** * Converts the given list of class names to a list of classes. * * @param classNames A list of class names. * @return A list of classes that correspond to the given names. * @throws ClassNotFoundException If a class name cannot be not found. */ static Class<?>[] toClassArray(String[] classNames) throws ClassNotFoundException { if (classNames == null) return null; Class<?>[] classes = new Class<?>[classNames.length]; for (int i = 0; i < classes.length; i++) { classes[i] = Class.forName(classNames[i]); } return classes; } /** * Returns all the ancestor classes from nearest to furthest for the given class. * * @param klass A class to fetch the ancestors of. * @return All the ancestor classes from nearest to furthest for the given class. */ static Set<Class<?>> getAncestors(Class<?> klass) { Set<Class<?>> ancestors = new LinkedHashSet<Class<?>>(); Set<Class<?>> parents = new LinkedHashSet<Class<?>>(); parents.add(klass); do { ancestors.addAll(parents); Set<Class<?>> children = new LinkedHashSet<Class<?>>(parents); parents.clear(); for (Class<?> child : children) { Class<?> parent = child.getSuperclass(); if (parent != null) parents.add(parent); Collections.addAll(parents, child.getInterfaces()); } } while (!parents.isEmpty()); return ancestors; } /** * Returns all the common ancestor classes from the given set of class names. * * @param classNames A list of class names. * @return All the common ancestor classes from the given set of class names. * @throws ClassNotFoundException If a class name cannot be found. */ public static Class<?>[] getAncestors(String... classNames) throws ClassNotFoundException { return getAncestors(toClassArray(classNames)); } /** * Returns all the common ancestor classes from the given set of classes. * * @param classes A list of classes. * @return All the common ancestor classes from the given set of class names. */ public static Class<?>[] getAncestors(Class<?>... classes) { Set<Class<?>> ancestors = getAncestors(Arrays.asList(classes)); return ancestors.toArray(new Class<?>[ancestors.size()]); } /** * Returns all the common ancestor classes from the given set of classes. * * @param classes A collection of classes. * @return All the common ancestor classes from the given set of class names. */ public static Set<Class<?>> getAncestors(Collection<Class<?>> classes) { Set<Class<?>> intersection = new LinkedHashSet<Class<?>>(); for (Class<?> klass : classes) { if (intersection.size() == 0) { intersection.addAll(getAncestors(klass)); } else { intersection.retainAll(getAncestors(klass)); } } return intersection; } /** * Returns the nearest class which is an ancestor to all the classes in the given set. * * @param classNames A set of class names for which the nearest ancestor will be returned. * @return The nearest ancestor class which is an ancestor to all the classes in the given * set. * @throws ClassNotFoundException If any of the class names cannot be found. */ public static Class<?> getNearestAncestor(String... classNames) throws ClassNotFoundException { return getNearestAncestor(toClassArray(classNames)); } /** * Returns the nearest class which is an ancestor to all the classes in the given set. * * @param classes A set of classes for which the nearest ancestor will be returned. * @return The nearest ancestor class which is an ancestor to all the classes in the given set. */ public static Class<?> getNearestAncestor(Class<?>... classes) { return ObjectHelper.getNearestAncestor(Arrays.asList(classes)); } /** * Returns the nearest class which is an ancestor to all the classes in the given set. * * @param classes A set of classes for which the nearest ancestor will be returned. * @return The nearest ancestor class which is an ancestor to all the classes in the given set. */ public static Class<?> getNearestAncestor(Set<Class<?>> classes) { Class<?> nearest = null; Set<Class<?>> ancestors = getAncestors(classes); if (ancestors.size() > 0) { nearest = ancestors.iterator().next(); } if (nearest == null) nearest = Object.class; return nearest; } }