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

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

Go to download

SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.

There is a newer version: 2.99.0
Show newest version
/*
 * #%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 - 2024 Weber Informatics LLC | Privacy Policy