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

org.scijava.util.ClassUtils Maven / Gradle / Ivy

/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2014 Board of Regents of the University of
 * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
 * Institute of Molecular Cell Biology and Genetics.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.util;

import com.googlecode.gentyref.GenericTypeReflector;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * Useful methods for working with {@link Class} objects and primitive types.
 * 
 * @author Curtis Rueden
 */
public final class ClassUtils {

	private ClassUtils() {
		// prevent instantiation of utility class
	}

	// -- Type conversion and casting --

	/**
	 * @deprecated use {@link ConversionUtils#convert(Object, Class)}
	 */
	@Deprecated
	public static  T convert(final Object value, final Class type) {
		return ConversionUtils.convert(value, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#canConvert(Class, Class)}
	 */
	@Deprecated
	public static boolean canConvert(final Class c, final Class type) {
		return ConversionUtils.canConvert(c, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#canConvert(Object, Class)}
	 */
	@Deprecated
	public static boolean canConvert(final Object value, final Class type) {
		return ConversionUtils.canConvert(value, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#cast(Object, Class)}
	 */
	@Deprecated
	public static  T cast(final Object obj, final Class type) {
		return ConversionUtils.cast(obj, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#canCast(Class, Class)}
	 */
	@Deprecated
	public static boolean canCast(final Class c, final Class type) {
		return ConversionUtils.canCast(c, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#canCast(Object, Class)}
	 */
	@Deprecated
	public static boolean canCast(final Object obj, final Class type) {
		return ConversionUtils.canCast(obj, type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#getNonprimitiveType(Class)}
	 */
	@Deprecated
	public static  Class getNonprimitiveType(final Class type) {
		return ConversionUtils.getNonprimitiveType(type);
	}

	/**
	 * @deprecated use {@link ConversionUtils#getNullValue(Class)}
	 */
	@Deprecated
	public static  T getNullValue(final Class type) {
		return ConversionUtils.getNullValue(type);
	}

	// -- Class loading, querying and reflection --

	/**
	 * Loads the class with the given name, using the current thread's context
	 * class loader, or null if it cannot be loaded.
	 * 
	 * @see #loadClass(String, ClassLoader)
	 */
	public static Class loadClass(final String className) {
		return loadClass(className, null);
	}

	/**
	 * Loads the class with the given name, using the specified
	 * {@link ClassLoader}, or null if it cannot be loaded.
	 * 

* This method is capable of parsing several different class name syntaxes. * In particular, array classes (including primitives) represented using * either square brackets or internal Java array name syntax are supported. * Examples: *

*
    *
  • {@code boolean} is loaded as {@code boolean.class}
  • *
  • {@code Z} is loaded as {@code boolean.class}
  • *
  • {@code double[]} is loaded as {@code double[].class}
  • *
  • {@code string[]} is loaded as {@code java.lang.String.class}
  • *
  • {@code [F} is loaded as {@code float[].class}
  • *
* * @param name The name of the class to load. * @param classLoader The class loader with which to load the class; if null, * the current thread's context class loader will be used. */ public static Class loadClass(final String name, final ClassLoader classLoader) { // handle primitive types if (name.equals("Z") || name.equals("boolean")) return boolean.class; if (name.equals("B") || name.equals("byte")) return byte.class; if (name.equals("C") || name.equals("char")) return char.class; if (name.equals("D") || name.equals("double")) return double.class; if (name.equals("F") || name.equals("float")) return float.class; if (name.equals("I") || name.equals("int")) return int.class; if (name.equals("J") || name.equals("long")) return long.class; if (name.equals("S") || name.equals("short")) return short.class; if (name.equals("V") || name.equals("void")) return void.class; // handle built-in class shortcuts final String className; if (name.equals("string")) className = "java.lang.String"; else className = name; // handle source style arrays (e.g.: "java.lang.String[]") if (name.endsWith("[]")) { final String elementClassName = name.substring(0, name.length() - 2); return getArrayClass(loadClass(elementClassName, classLoader)); } // handle non-primitive internal arrays (e.g.: "[Ljava.lang.String;") if (name.startsWith("[L") && name.endsWith(";")) { final String elementClassName = name.substring(2, name.length() - 1); return getArrayClass(loadClass(elementClassName, classLoader)); } // handle other internal arrays (e.g.: "[I", "[[I", "[[Ljava.lang.String;") if (name.startsWith("[")) { final String elementClassName = name.substring(1); return getArrayClass(loadClass(elementClassName, classLoader)); } // load the class! try { final ClassLoader cl = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; return cl.loadClass(className); } catch (final ClassNotFoundException e) { return null; } } /** * Gets the array class corresponding to the given element type. *

* For example, {@code getArrayClass(double.class)} returns * {@code double[].class}. *

*/ public static Class getArrayClass(final Class elementClass) { if (elementClass == null) return null; // NB: It appears the reflection API has no built-in way to do this. // So unfortunately, we must allocate a new object and then inspect it. try { return Array.newInstance(elementClass, 0).getClass(); } catch (final IllegalArgumentException exc) { return null; } } /** Checks whether a class with the given name exists. */ public static boolean hasClass(final String className) { return hasClass(className, null); } /** Checks whether a class with the given name exists. */ public static boolean hasClass(final String className, final ClassLoader classLoader) { return loadClass(className, classLoader) != null; } /** * Gets the base location of the given class. *

* If the class is directly on the file system (e.g., * "/path/to/my/package/MyClass.class") then it will return the base directory * (e.g., "/path/to"). *

*

* If the class is within a JAR file (e.g., * "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the * path to the JAR (e.g., "/path/to/my-jar.jar"). *

* * @param className The name of the class whose location is desired. * @see FileUtils#urlToFile(URL) to convert the result to a {@link File}. */ public static URL getLocation(final String className) { return getLocation(className, null); } /** * Gets the base location of the given class. *

* If the class is directly on the file system (e.g., * "/path/to/my/package/MyClass.class") then it will return the base directory * (e.g., "/path/to"). *

*

* If the class is within a JAR file (e.g., * "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the * path to the JAR (e.g., "/path/to/my-jar.jar"). *

* * @param className The name of the class whose location is desired. * @param classLoader The class loader to use when loading the class. * @see FileUtils#urlToFile(URL) to convert the result to a {@link File}. */ public static URL getLocation(final String className, final ClassLoader classLoader) { final Class c = loadClass(className, classLoader); return getLocation(c); } /** * Gets the base location of the given class. *

* If the class is directly on the file system (e.g., * "/path/to/my/package/MyClass.class") then it will return the base directory * (e.g., "file:/path/to"). *

*

* If the class is within a JAR file (e.g., * "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the * path to the JAR (e.g., "file:/path/to/my-jar.jar"). *

* * @param c The class whose location is desired. * @see FileUtils#urlToFile(URL) to convert the result to a {@link File}. */ public static URL getLocation(final Class c) { if (c == null) return null; // could not load the class // try the easy way first try { final URL codeSourceLocation = c.getProtectionDomain().getCodeSource().getLocation(); if (codeSourceLocation != null) return codeSourceLocation; } catch (final SecurityException e) { // NB: Cannot access protection domain. } catch (final NullPointerException e) { // NB: Protection domain or code source is null. } // NB: The easy way failed, so we try the hard way. We ask for the class // itself as a resource, then strip the class's path from the URL string, // leaving the base path. // get the class's raw resource path final URL classResource = c.getResource(c.getSimpleName() + ".class"); if (classResource == null) return null; // cannot find class resource final String url = classResource.toString(); final String suffix = c.getCanonicalName().replace('.', '/') + ".class"; if (!url.endsWith(suffix)) return null; // weird URL // strip the class's path from the URL string final String base = url.substring(0, url.length() - suffix.length()); String path = base; // remove the "jar:" prefix and "!/" suffix, if present if (path.startsWith("jar:")) path = path.substring(4, path.length() - 2); try { return new URL(path); } catch (final MalformedURLException e) { e.printStackTrace(); return null; } } /** * Gets the given class's {@link Field}s marked with the annotation of the * specified class. *

* Unlike {@link Class#getFields()}, the result will include any non-public * fields, including fields defined in supertypes of the given class. *

* * @param c The class to scan for annotated fields. * @param annotationClass The type of annotation for which to scan. * @return A new list containing all fields with the requested annotation. */ public static List getAnnotatedFields( final Class c, final Class annotationClass) { final ArrayList fields = new ArrayList(); getAnnotatedFields(c, annotationClass, fields); return fields; } /** * Gets the given class's {@link Field}s marked with the annotation of the * specified class. *

* Unlike {@link Class#getFields()}, the result will include any non-public * fields, including fields defined in supertypes of the given class. *

* * @param c The class to scan for annotated fields. * @param annotationClass The type of annotation for which to scan. * @param fields The list to which matching fields will be added. */ public static
void getAnnotatedFields( final Class c, final Class annotationClass, final List fields) { if (c == null) return; // check supertypes for annotated fields first getAnnotatedFields(c.getSuperclass(), annotationClass, fields); for (final Class iface : c.getInterfaces()) { getAnnotatedFields(iface, annotationClass, fields); } for (final Field f : c.getDeclaredFields()) { final A ann = f.getAnnotation(annotationClass); if (ann != null) fields.add(f); } } /** * Gets the specified field of the given class, or null if it does not exist. */ public static Field getField(final String className, final String fieldName) { return getField(loadClass(className), fieldName); } /** * Gets the specified field of the given class, or null if it does not exist. */ public static Field getField(final Class c, final String fieldName) { if (c == null) return null; try { return c.getDeclaredField(fieldName); } catch (final NoSuchFieldException e) { return null; } } /** * Returns the "safe" type(s) of the given field, as viewed from the specified * type. This may be narrower than what {@link Field#getType()} returns, if * the field is declared in a superclass, or {@code type} has a type parameter * that is used in the type of the field. *

* For example, suppose we have the following three classes: *

* *
	 * public class Thing<T> {
	 * 	public T thing;
	 * }
	 * 
	 * public class NumberThing<N extends Number> extends Thing<N> { }
	 * 
	 * public class IntegerThing extends NumberThing<Integer> { }
	 * 
* * Then this method operates as follows: * *
	 * field = ClassUtils.getField(Thing.class, "thing");
	 * 
	 * field.getType(); // Object
	 * 
	 * ClassUtils.getTypes(field, Thing.class).get(0); // Object
	 * ClassUtils.getTypes(field, NumberThing.class).get(0); // Number
	 * ClassUtils.getTypes(field, IntegerThing.class).get(0); // Integer
	 * 
* *

* In cases of complex generics which take the intersection of * multiple types using the {@code &} operator, there may be multiple types * returned by this method. For example: *

* *
	 * public class ComplexThing<T extends Serializable & Cloneable> extends Thing<T> { }
	 * 
	 * ClassUtils.getTypes(field, ComplexThing.class); // Serializable, Cloneable
	 * 
* * @see #getGenericType(Field, Class) */ public static List> getTypes(final Field field, final Class type) { final Type genericType = getGenericType(field, type); return GenericTypeReflector.getUpperBoundClassAndInterfaces(genericType); } /** * Returns the "safe" generic type of the given field, as viewed from the * given type. This may be narrower than what {@link Field#getGenericType()} * returns, if the field is declared in a superclass, or {@code type} has a * type parameter that is used in the type of the field. *

* For example, suppose we have the following three classes: *

* *
	 * public class Thing<T> {
	 * 	public T thing;
	 * }
	 * 
	 * public class NumberThing<N extends Number> extends Thing<N> { }
	 * 
	 * public class IntegerThing extends NumberThing<Integer> { }
	 * 
* * Then this method operates as follows: * *
	 * field = ClassUtils.getField(Thing.class, "thing");
	 * 
	 * field.getType(); // Object
	 * field.getGenericType(); // T
	 * 
	 * ClassUtils.getGenericType(field, Thing.class); // T
	 * ClassUtils.getGenericType(field, NumberThing.class); // N extends Number
	 * ClassUtils.getGenericType(field, IntegerThing.class); // Integer
	 * 
* * @see #getTypes(Field, Class) */ public static Type getGenericType(final Field field, final Class type) { final Type wildType = GenericTypeReflector.addWildcardParameters(type); return GenericTypeReflector.getExactFieldType(field, wildType); } /** * Gets the given field's value of the specified object instance, or null if * the value cannot be obtained. */ public static Object getValue(final Field field, final Object instance) { try { field.setAccessible(true); return field.get(instance); } catch (final IllegalAccessException e) { return null; } } /** * Sets the given field's value of the specified object instance. * * @throws IllegalArgumentException if the value cannot be set. */ public static void setValue(final Field field, final Object instance, final Object value) { try { field.setAccessible(true); field.set(instance, ConversionUtils .convert(value, field.getGenericType())); } catch (final IllegalAccessException e) { throw new IllegalArgumentException("No access to field: " + field.getName(), e); } } // -- Type querying -- public static boolean isBoolean(final Class type) { return type == boolean.class || Boolean.class.isAssignableFrom(type); } public static boolean isByte(final Class type) { return type == byte.class || Byte.class.isAssignableFrom(type); } public static boolean isCharacter(final Class type) { return type == char.class || Character.class.isAssignableFrom(type); } public static boolean isDouble(final Class type) { return type == double.class || Double.class.isAssignableFrom(type); } public static boolean isFloat(final Class type) { return type == float.class || Float.class.isAssignableFrom(type); } public static boolean isInteger(final Class type) { return type == int.class || Integer.class.isAssignableFrom(type); } public static boolean isLong(final Class type) { return type == long.class || Long.class.isAssignableFrom(type); } public static boolean isShort(final Class type) { return type == short.class || Short.class.isAssignableFrom(type); } public static boolean isNumber(final Class type) { return Number.class.isAssignableFrom(type) || type == byte.class || type == double.class || type == float.class || type == int.class || type == long.class || type == short.class; } public static boolean isText(final Class type) { return String.class.isAssignableFrom(type) || isCharacter(type); } // -- Comparison -- /** * Compares two {@link Class} objects using their fully qualified names. *

* Note: this method provides a natural ordering that may be inconsistent with * equals. Specifically, two unequal classes may return 0 when compared in * this fashion if they represent the same class loaded using two different * {@link ClassLoader}s. Hence, if this method is used as a basis for * implementing {@link Comparable#compareTo} or * {@link java.util.Comparator#compare}, that implementation may want to * impose logic beyond that of this method, for breaking ties, if a total * ordering consistent with equals is always required. *

* * @see org.scijava.Priority#compare(org.scijava.Prioritized, * org.scijava.Prioritized) */ public static int compare(final Class c1, final Class c2) { if (c1 == c2) return 0; final String name1 = c1 == null ? null : c1.getName(); final String name2 = c2 == null ? null : c2.getName(); return MiscUtils.compare(name1, name2); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy