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

com.cedarsoftware.util.ClassUtilities Maven / Gradle / Ivy

The newest version!
package com.cedarsoftware.util;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Useful utilities for Class work. For example, call computeInheritanceDistance(source, destination)
 * to get the inheritance distance (number of super class steps to make it from source to destination.
 * It will return the distance as an integer.  If there is no inheritance relationship between the two,
 * then -1 is returned.  The primitives and primitive wrappers return 0 distance as if they are the
 * same class.
 * 

* @author John DeRegnaucourt ([email protected]) *
* Copyright (c) Cedar Software LLC *

* 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 *

* License *

* 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. */ public class ClassUtilities { private static final Set> prims = new HashSet<>(); private static final Map, Class> primitiveToWrapper = new HashMap<>(20, .8f); private static final Map> nameToClass = new HashMap<>(); private static final Map, Class> wrapperMap = new HashMap<>(); // Cache for OSGi ClassLoader to avoid repeated reflection calls private static final Map, ClassLoader> osgiClassLoaders = new ConcurrentHashMap<>(); private static final Set> osgiChecked = new ConcurrentSet<>(); static { prims.add(Byte.class); prims.add(Short.class); prims.add(Integer.class); prims.add(Long.class); prims.add(Float.class); prims.add(Double.class); prims.add(Character.class); prims.add(Boolean.class); nameToClass.put("boolean", Boolean.TYPE); nameToClass.put("char", Character.TYPE); nameToClass.put("byte", Byte.TYPE); nameToClass.put("short", Short.TYPE); nameToClass.put("int", Integer.TYPE); nameToClass.put("long", Long.TYPE); nameToClass.put("float", Float.TYPE); nameToClass.put("double", Double.TYPE); nameToClass.put("string", String.class); nameToClass.put("date", Date.class); nameToClass.put("class", Class.class); primitiveToWrapper.put(int.class, Integer.class); primitiveToWrapper.put(long.class, Long.class); primitiveToWrapper.put(double.class, Double.class); primitiveToWrapper.put(float.class, Float.class); primitiveToWrapper.put(boolean.class, Boolean.class); primitiveToWrapper.put(char.class, Character.class); primitiveToWrapper.put(byte.class, Byte.class); primitiveToWrapper.put(short.class, Short.class); primitiveToWrapper.put(void.class, Void.class); wrapperMap.put(int.class, Integer.class); wrapperMap.put(Integer.class, int.class); wrapperMap.put(char.class, Character.class); wrapperMap.put(Character.class, char.class); wrapperMap.put(byte.class, Byte.class); wrapperMap.put(Byte.class, byte.class); wrapperMap.put(short.class, Short.class); wrapperMap.put(Short.class, short.class); wrapperMap.put(long.class, Long.class); wrapperMap.put(Long.class, long.class); wrapperMap.put(float.class, Float.class); wrapperMap.put(Float.class, float.class); wrapperMap.put(double.class, Double.class); wrapperMap.put(Double.class, double.class); wrapperMap.put(boolean.class, Boolean.class); wrapperMap.put(Boolean.class, boolean.class); } /** * Add alias names for classes to allow .forName() to bring the class (.class) back with the alias name. * Because the alias to class name mappings are static, it is expected that these are set up during initialization * and not changed later. * * @param clazz Class to add an alias for * @param alias String alias name */ public static void addPermanentClassAlias(Class clazz, String alias) { nameToClass.put(alias, clazz); } /** * Remove alias name for classes to prevent .forName() from fetching the class with the alias name. * Because the alias to class name mappings are static, it is expected that these are set up during initialization * and not changed later. * * @param alias String alias name */ public static void removePermanentClassAlias(String alias) { nameToClass.remove(alias); } /** * Computes the inheritance distance between two classes/interfaces/primitive types. * * @param source The source class, interface, or primitive type. * @param destination The destination class, interface, or primitive type. * @return The number of steps from the source to the destination, or -1 if no path exists. */ public static int computeInheritanceDistance(Class source, Class destination) { if (source == null || destination == null) { return -1; } if (source.equals(destination)) { return 0; } // Check for primitive types if (source.isPrimitive()) { if (destination.isPrimitive()) { // Not equal because source.equals(destination) already chceked. return -1; } if (!isPrimitive(destination)) { return -1; } return comparePrimitiveToWrapper(destination, source); } if (destination.isPrimitive()) { if (!isPrimitive(source)) { return -1; } return comparePrimitiveToWrapper(source, destination); } Queue> queue = new LinkedList<>(); Map, String> visited = new IdentityHashMap<>(); queue.add(source); visited.put(source, null); int distance = 0; while (!queue.isEmpty()) { int levelSize = queue.size(); distance++; for (int i = 0; i < levelSize; i++) { Class current = queue.poll(); // Check superclass if (current.getSuperclass() != null) { if (current.getSuperclass().equals(destination)) { return distance; } if (!visited.containsKey(current.getSuperclass())) { queue.add(current.getSuperclass()); visited.put(current.getSuperclass(), null); } } // Check interfaces for (Class interfaceClass : current.getInterfaces()) { if (interfaceClass.equals(destination)) { return distance; } if (!visited.containsKey(interfaceClass)) { queue.add(interfaceClass); visited.put(interfaceClass, null); } } } } return -1; // No path found } /** * @param c Class to test * @return boolean true if the passed in class is a Java primitive, false otherwise. The Wrapper classes * Integer, Long, Boolean, etc. are considered primitives by this method. */ public static boolean isPrimitive(Class c) { return c.isPrimitive() || prims.contains(c); } /** * Compare two primitives. * * @return 0 if they are the same, -1 if not. Primitive wrapper classes are consider the same as primitive classes. */ private static int comparePrimitiveToWrapper(Class source, Class destination) { try { return source.getField("TYPE").get(null).equals(destination) ? 0 : -1; } catch (Exception e) { return -1; } } /** * Given the passed in String class name, return the named JVM class. * * @param name String name of a JVM class. * @param classLoader ClassLoader to use when searching for JVM classes. * @return Class instance of the named JVM class or null if not found. */ public static Class forName(String name, ClassLoader classLoader) { if (name == null || name.isEmpty()) { return null; } try { return internalClassForName(name, classLoader); } catch (SecurityException e) { throw new IllegalArgumentException("Security exception, classForName() call on: " + name, e); } catch (Exception e) { return null; } } /** * Used internally to load a class by name, and takes care of caching name mappings for speed. * * @param name String name of a JVM class. * @param classLoader ClassLoader to use when searching for JVM classes. * @return Class instance of the named JVM class */ private static Class internalClassForName(String name, ClassLoader classLoader) throws ClassNotFoundException { Class c = nameToClass.get(name); if (c != null) { return c; } c = loadClass(name, classLoader); if (ClassLoader.class.isAssignableFrom(c) || ProcessBuilder.class.isAssignableFrom(c) || Process.class.isAssignableFrom(c) || Constructor.class.isAssignableFrom(c) || Method.class.isAssignableFrom(c) || Field.class.isAssignableFrom(c)) { throw new SecurityException("For security reasons, cannot instantiate: " + c.getName() + " when loading JSON."); } nameToClass.put(name, c); return c; } /** * loadClass() provided by: Thomas Margreiter */ private static Class loadClass(String name, ClassLoader classLoader) throws ClassNotFoundException { String className = name; boolean arrayType = false; Class primitiveArray = null; while (className.startsWith("[")) { arrayType = true; if (className.endsWith(";")) { className = className.substring(0, className.length() - 1); } if (className.equals("[B")) { primitiveArray = byte[].class; } else if (className.equals("[S")) { primitiveArray = short[].class; } else if (className.equals("[I")) { primitiveArray = int[].class; } else if (className.equals("[J")) { primitiveArray = long[].class; } else if (className.equals("[F")) { primitiveArray = float[].class; } else if (className.equals("[D")) { primitiveArray = double[].class; } else if (className.equals("[Z")) { primitiveArray = boolean[].class; } else if (className.equals("[C")) { primitiveArray = char[].class; } int startpos = className.startsWith("[L") ? 2 : 1; className = className.substring(startpos); } Class currentClass = null; if (null == primitiveArray) { try { currentClass = classLoader.loadClass(className); } catch (ClassNotFoundException e) { currentClass = Thread.currentThread().getContextClassLoader().loadClass(className); } } if (arrayType) { currentClass = (null != primitiveArray) ? primitiveArray : Array.newInstance(currentClass, 0).getClass(); while (name.startsWith("[[")) { currentClass = Array.newInstance(currentClass, 0).getClass(); name = name.substring(1); } } return currentClass; } public static boolean isClassFinal(Class c) { return (c.getModifiers() & Modifier.FINAL) != 0; } public static boolean areAllConstructorsPrivate(Class c) { Constructor[] constructors = c.getDeclaredConstructors(); for (Constructor constructor : constructors) { if ((constructor.getModifiers() & Modifier.PRIVATE) == 0) { return false; } } return true; } public static Class toPrimitiveWrapperClass(Class primitiveClass) { if (!primitiveClass.isPrimitive()) { return primitiveClass; } Class c = primitiveToWrapper.get(primitiveClass); if (c == null) { throw new IllegalArgumentException("Passed in class: " + primitiveClass + " is not a primitive class"); } return c; } public static boolean doesOneWrapTheOther(Class x, Class y) { return wrapperMap.get(x) == y; } /** * Obtains the appropriate ClassLoader depending on whether the environment is OSGi, JPMS, or neither. * * @return the appropriate ClassLoader */ public static ClassLoader getClassLoader() { return getClassLoader(ClassUtilities.class); } /** * Obtains the appropriate ClassLoader depending on whether the environment is OSGi, JPMS, or neither. * * @return the appropriate ClassLoader */ public static ClassLoader getClassLoader(final Class anchorClass) { // Attempt to detect and handle OSGi environment ClassLoader cl = getOSGiClassLoader(anchorClass); if (cl != null) { return cl; } // Use the thread's context ClassLoader if available cl = Thread.currentThread().getContextClassLoader(); if (cl != null) { return cl; } // Fallback to the ClassLoader that loaded this utility class cl = anchorClass.getClassLoader(); if (cl != null) { return cl; } // As a last resort, use the system ClassLoader return ClassLoader.getSystemClassLoader(); } /** * Attempts to retrieve the OSGi Bundle's ClassLoader using FrameworkUtil. * * @return the OSGi Bundle's ClassLoader if in an OSGi environment; otherwise, null */ private static ClassLoader getOSGiClassLoader(final Class classFromBundle) { if (osgiChecked.contains(classFromBundle)) { return osgiClassLoaders.get(classFromBundle); } synchronized (ClassUtilities.class) { if (osgiChecked.contains(classFromBundle)) { return osgiClassLoaders.get(classFromBundle); } osgiClassLoaders.computeIfAbsent(classFromBundle, k -> getOSGiClassLoader0(k)); osgiChecked.add(classFromBundle); } return osgiClassLoaders.get(classFromBundle); } /** * Attempts to retrieve the OSGi Bundle's ClassLoader using FrameworkUtil. * * @return the OSGi Bundle's ClassLoader if in an OSGi environment; otherwise, null */ private static ClassLoader getOSGiClassLoader0(final Class classFromBundle) { try { // Load the FrameworkUtil class from OSGi Class frameworkUtilClass = Class.forName("org.osgi.framework.FrameworkUtil"); // Get the getBundle(Class) method Method getBundleMethod = frameworkUtilClass.getMethod("getBundle", Class.class); // Invoke FrameworkUtil.getBundle(thisClass) to get the Bundle instance Object bundle = getBundleMethod.invoke(null, classFromBundle); if (bundle != null) { // Get BundleWiring class Class bundleWiringClass = Class.forName("org.osgi.framework.wiring.BundleWiring"); // Get the adapt(Class) method Method adaptMethod = bundle.getClass().getMethod("adapt", Class.class); // method is inside not a public class, so we need to make it accessible adaptMethod.setAccessible(true); // Invoke bundle.adapt(BundleWiring.class) to get the BundleWiring instance Object bundleWiring = adaptMethod.invoke(bundle, bundleWiringClass); if (bundleWiring != null) { // Get the getClassLoader() method from BundleWiring Method getClassLoaderMethod = bundleWiringClass.getMethod("getClassLoader"); // Invoke getClassLoader() to obtain the ClassLoader Object classLoader = getClassLoaderMethod.invoke(bundleWiring); if (classLoader instanceof ClassLoader) { return (ClassLoader) classLoader; } } } } catch (Exception e) { // OSGi FrameworkUtil is not present; not in an OSGi environment } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy