package org.basex.util; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; /** * This class assembles some reflection methods. If exceptions occur, a * {@code null} reference is returned or a runtime exception is thrown. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Reflect { /** Cached constructors. */ private static final HashMap<String, Constructor<?>> CONS = new HashMap<String, Constructor<?>>(); /** Cached classes. */ private static final HashMap<String, Class<?>> CLASSES = new HashMap<String, Class<?>>(); /** Cached fields. */ private static final HashMap<String, Field> FIELDS = new HashMap<String, Field>(); /** Hidden constructor. */ private Reflect() { } /** * Checks if the class specified by the pattern is available. * @param pattern class pattern * @param ext optional extension * @return result of check */ public static boolean available(final String pattern, final Object... ext) { try { Class.forName(Util.info(pattern, ext)); return true; } catch(final ClassNotFoundException ex) { return false; } } /** * Returns a reference to the specified class, or {@code null}. * @param name class name * @return reference, or {@code null} if the class is not found */ public static Class<?> find(final String name) { final Class<?> c = CLASSES.get(name); if(c != null) return c; try { return cache(name, Class.forName(name)); } catch(final Throwable ex) { return null; } } /** * Returns a reference to a class located in the specified jar loader, * or {@code null}. * @param name class name * @param jar jar loader * @return reference, or {@code null} if the class is not found */ public static Class<?> find(final String name, final JarLoader jar) { try { return cache(name, Class.forName(name, true, jar)); } catch(final Throwable ex) { return null; } } /** * Caches the specified class. * @param name class name * @param c class * @return reference, or {@code null} if the class is not found */ private static Class<?> cache(final String name, final Class<?> c) { try { if(!accessible(c)) return null; CLASSES.put(name, c); } catch(final Throwable ex) { } return c; } /** * Returns a reference to the specified field, or {@code null}. * @param clazz class to search for the constructor * @param name class name * @return reference, or {@code null} if the field is not found */ public static Field field(final Class<?> clazz, final String name) { final String key = clazz.getName() + name; Field f = FIELDS.get(key); if(f == null) { try { f = clazz.getField(name); FIELDS.put(key, f); } catch(final Throwable ex) { } } return f; } /** * Returns a reference to the class specified by the pattern, or {@code null}. * @param pattern class pattern * @param ext optional extension * @return reference, or {@code null} if the class is not found */ public static Class<?> find(final String pattern, final Object... ext) { return find(Util.info(pattern, ext)); } /** * Returns a class reference to one of the specified classes, or {@code null}. * @param names class names * @return reference, or {@code null} if the class is not found */ public static Class<?> find(final String[] names) { for(final String n : names) { final Class<?> c = find(n); if(c != null) return c; } return null; } /** * Finds a constructor by parameter types. * @param clazz class to search for the constructor * @param types constructor parameters * @return {@code null} if the class is not found */ public static Constructor<?> find(final Class<?> clazz, final Class<?>... types) { if(clazz == null) return null; final StringBuilder sb = new StringBuilder(clazz.getName()); for(final Class<?> c : types) sb.append(c.getName()); final String key = sb.toString(); Constructor<?> m = CONS.get(key); if(m == null) { try { try { m = clazz.getConstructor(types); } catch(final Throwable ex) { m = clazz.getDeclaredConstructor(types); m.setAccessible(true); } CONS.put(key, m); } catch(final Throwable ex) { Util.debug(ex); } } return m; } /** * Finds a public, protected or private method by name and parameter types. * @param clazz class to search for the method * @param name method name * @param types method parameters * @return reference, or {@code null} if the method is not found */ public static Method method(final Class<?> clazz, final String name, final Class<?>... types) { if(clazz == null) return null; Method m = null; try { try { m = clazz.getMethod(name, types); } catch(final Throwable ex) { m = clazz.getDeclaredMethod(name, types); m.setAccessible(true); } } catch(final Throwable ex) { Util.debug(ex); } return m; } /** * Returns a class instance, or throws a runtime exception. * @param clazz class * @return instance */ public static Object get(final Class<?> clazz) { try { return clazz != null ? clazz.newInstance() : null; } catch(final Throwable ex) { Util.debug(ex); return null; } } /** * Returns a class instance, or {@code null}. * @param clazz class * @param args arguments * @return instance */ public static Object get(final Constructor<?> clazz, final Object... args) { try { return clazz != null ? clazz.newInstance(args) : null; } catch(final Throwable ex) { Util.debug(ex); return null; } } /** * Invokes the specified method. * @param method method to run * @param object object ({@code null} for static methods) * @param args arguments * @return result of method call */ public static Object invoke(final Method method, final Object object, final Object... args) { try { return method != null ? method.invoke(object, args) : null; } catch(final Throwable ex) { Util.debug(ex); return null; } } /** * Check if a class is accessible. * @param cls class * @return {@code true} if a class is accessible */ private static boolean accessible(final Class<?> cls) { // non public classes cannot be instantiated return Modifier.isPublic(cls.getModifiers()); } }