/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2005 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution37.javolution.lang;

import javolution37.javolution.JavolutionError;
import javolution37.javolution.util.StandardLog;

/**
 * 

This utility class greatly facilitates the use of reflection to invoke * constructors or methods which may or may not exist at runtime.

* *

The constructors/methods are identified through their signatures * represented as a {@link String}. When the constructor/method does * not exist (e.g. class not found) or when the platform does not support * reflection, the constructor/method is null * (no exception raised). Here is an example of timer taking advantage * of the new (JRE1.5+) high resolution time when available:[code] * public static long microTime() { * if (NANO_TIME_METHOD != null) { // JRE 1.5+ * Long time = (Long) NANO_TIME_METHOD.invoke(null); // Static method. * return time.longValue() / 1000; * } else { // Use the less accurate time in milliseconds. * return System.currentTimeMillis() * 1000; * } * } * private static final Reflection.Method NANO_TIME_METHOD * = Reflection.getMethod("java.lang.System.nanoTime()");[/code]

* *

Arrays and primitive types are supported. For example:[code] * Reflection.Constructor sbc = Reflection.getConstructor("java.lang.StringBuilder(int)"); * if (sbc != null) { // JDK 1.5+ * Object sb = sbc.newInstance(new Integer(32)); * Reflection.Method append = Reflection.getMethod("java.lang.StringBuilder.append(char[], int, int)"); * append.invoke(sb, new char[] { 'h', 'i' }, new Integer(0), new Integer(2)); * System.out.println(sb); * } * * > hi[/code]

* * @author Jean-Marie Dautelle * @version 3.6, November 3, 2005 */ public final class Reflection { /** * Default constructor (private to forbid instantiation). */ private Reflection() { } /** * Returns and initializes the class having the specified name * (returns null if the class is not found). * This method searches the Reflection.class class loader * first (which might be the bootstrap class loader), then the context * class loader and finally the system class loader. * * @param name the name of the class to search for. * @return the corresponding class or null if the class * is not found or if an error occurs during initialization * (in which case a warning or error is * {@link javolution37.javolution.realtime.LogContext logged}). */ public static Class getClass(String name) { try { return Class.forName(name); // Try Reflection.class class loader. } catch (ClassNotFoundException e0) { // Try context class loader. /**/ try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return Class.forName(name, true, cl); } catch (ClassNotFoundException e1) { // Try system class loader. try { ClassLoader cl = ClassLoader.getSystemClassLoader(); return Class.forName(name, true, cl); } catch (ClassNotFoundException e2) { } } /**/ } catch (Throwable error) { StandardLog.error(error); return null; } StandardLog.warning(name + " not found"); return null; } /** * Returns the constructor having the specified signature. * * @param signature the textual representation of the constructor signature. * @return the corresponding constructor or null if none * found. */ public static Constructor getConstructor(String signature) { int argStart = signature.indexOf('(') + 1; if (argStart < 0) { throw new IllegalArgumentException("Parenthesis '(' not found"); } int argEnd = signature.indexOf(')'); if (argEnd < 0) { throw new IllegalArgumentException("Parenthesis ')' not found"); } String className = signature.substring(0, argStart - 1); Class theClass = Reflection.getClass(className); if (theClass == null) return null; String args = signature.substring(argStart, argEnd); if (args.length() == 0) return new DefaultConstructor(theClass); /**/ Class[] argsTypes = classesFor(args); if (argsTypes != null) { try { return new ReflectConstructor(theClass.getConstructor(argsTypes), signature); } catch (NoSuchMethodException e) { return null; } } /**/ return null; } private static class DefaultConstructor extends Constructor { final Class _class; DefaultConstructor(Class cl) { _class = cl; } public Object allocate(Object[] args) { try { return _class.newInstance(); } catch (InstantiationException e) { throw new JavolutionError("Instantiation error for " + _class.getName() + " default constructor", e); } catch (IllegalAccessException e) { throw new JavolutionError("Illegal access error for " + _class.getName() + " constructor", e); } } public String toString() { return _class + " default constructor"; } } /**/ private static final class ReflectConstructor extends Constructor { private final java.lang.reflect.Constructor _value; private final String _signature; public ReflectConstructor(java.lang.reflect.Constructor value, String signature) { _value = value; _signature = signature; } public Object allocate(Object[] args) { try { return _value.newInstance(args); } catch (IllegalArgumentException e) { throw new JavolutionError("Illegal argument for " + _signature + " constructor", e); } catch (InstantiationException e) { throw new JavolutionError("Instantiation error for " + _signature + " constructor", e); } catch (IllegalAccessException e) { throw new JavolutionError("Illegal access error for " + _signature + " constructor", e); } catch (java.lang.reflect.InvocationTargetException e) { throw new JavolutionError("Invocation exception for " + _signature + " constructor", (java.lang.reflect.InvocationTargetException) e.getTargetException()); } } public String toString() { return _signature + " constructor"; } } /**/ /** * Returns the method having the specified signature. * * @param signature the textual representation of the method signature. * @return the corresponding constructor or null if none * found. */ public static Method getMethod(String signature) { /**/ int argStart = signature.indexOf('(') + 1; if (argStart < 0) { throw new IllegalArgumentException("Parenthesis '(' not found"); } int argEnd = signature.indexOf(')'); if (argEnd < 0) { throw new IllegalArgumentException("Parenthesis ')' not found"); } int nameStart = signature.substring(0, argStart).lastIndexOf('.') + 1; try { String className = signature.substring(0, nameStart - 1); Class theClass = Reflection.getClass(className); if (theClass == null) return null; String methodName = signature.substring(nameStart, argStart - 1); String args = signature.substring(argStart, argEnd); Class[] argsTypes = classesFor(args); if (argsTypes == null) return null; return new ReflectMethod(theClass.getMethod(methodName, argsTypes), signature); } catch (Throwable t) { } /**/ return null; } /**/ private static final class ReflectMethod extends Method { private final java.lang.reflect.Method _value; private final String _signature; public ReflectMethod(java.lang.reflect.Method value, String signature) { _value = value; _signature = signature; } public Object execute(Object that, Object[] args) { try { return _value.invoke(that, args); } catch (IllegalArgumentException e) { throw new JavolutionError("Illegal argument for " + _signature + " method", e); } catch (IllegalAccessException e) { throw new JavolutionError("Illegal access error for " + _signature + " method", e); } catch (java.lang.reflect.InvocationTargetException e) { throw new JavolutionError("Invocation exception for " + _signature + " method", (java.lang.reflect.InvocationTargetException) e .getTargetException()); } } public String toString() { return _signature + " method"; } } /**/ /** * Returns the classes for the specified argument. * * @param args the comma separated arguments. * @return the classes or null if one of the class is not found. */ private static Class[] classesFor(String args) { args = args.trim(); if (args.length() == 0) { return new Class[0]; } // Count semicolons occurences. int semiColons = 0; for (int i = 0; i < args.length(); i++) { if (args.charAt(i) == ',') { semiColons++; } } Class[] classes = new Class[semiColons + 1]; int index = 0; for (int i = 0; i < semiColons; i++) { int sep = args.indexOf(',', index); classes[i] = classFor(args.substring(index, sep).trim()); if (classes[i] == null) return null; index = sep + 1; } classes[semiColons] = classFor(args.substring(index).trim()); if (classes[semiColons] == null) return null; return classes; } private static Class classFor(String className) { int arrayIndex = className.indexOf("[]"); if (arrayIndex >= 0) { if (className.indexOf("[][]") >= 0) { if (className.indexOf("[][][]") >= 0) { if (className.indexOf("[][][][]") >= 0) { throw new UnsupportedOperationException( "The maximum array dimension is 3"); } else { // Dimension three. return Reflection.getClass("[[[" + descriptorFor(className.substring(0, arrayIndex))); } } else { // Dimension two. return Reflection.getClass("[[" + descriptorFor(className.substring(0, arrayIndex))); } } else { // Dimension one. return Reflection.getClass("[" + descriptorFor(className.substring(0, arrayIndex))); } } if (className.equals("boolean")) { return boolean.class; } else if (className.equals("byte")) { return byte.class; } else if (className.equals("char")) { return char.class; } else if (className.equals("short")) { return short.class; } else if (className.equals("int")) { return int.class; } else if (className.equals("long")) { return long.class; } else if (className.equals("float")) { return float.class; } else if (className.equals("double")) { return double.class; } else { return Reflection.getClass(className); } } private static String descriptorFor(String className) { if (className.equals("boolean")) { return "Z"; } else if (className.equals("byte")) { return "B"; } else if (className.equals("char")) { return "C"; } else if (className.equals("short")) { return "S"; } else if (className.equals("int")) { return "I"; } else if (className.equals("long")) { return "J"; } else if (className.equals("float")) { return "F"; } else if (className.equals("double")) { return "D"; } else { return "L" + className.replace('.', '/') + ";"; } } /**/ /** * This class represents a run-time constructor obtained through reflection. * * Here are few examples of utilization:[code] * // Default constructor (fastList = new FastList()) * Reflection.Constructor fastListConstructor * = Reflection.getConstructor("javolution37.javolution.util.FastList()"); * Object fastList = fastListConstructor.newInstance(); * * // Constructor with arguments (fastMap = new FastMap(64)) * Reflection.Constructor fastMapConstructor * = Reflection.getConstructor("javolution37.javolution.util.FastMap(int)"); * Object fastMap = fastMapConstructor.newInstance(new Integer(64)); * [/code] */ public static abstract class Constructor { /** * Default constructor. */ public Constructor() { } /** * Allocates a new object using this constructor with the specified * arguments. * * @param args the constructor arguments. * @return the object being instantiated. */ protected abstract Object allocate(Object[] args); /** * Invokes this constructor with no argument (convenience method). * * @return the object being instantiated. */ public final Object newInstance() { return allocate(NO_ARG); } private static final Object[] NO_ARG = new Object[0]; /** * Invokes this constructor with the specified single argument. * * @param arg0 the first argument. * @return the object being instantiated. */ public final Object newInstance(Object arg0) { synchronized (this) { array1[0] = arg0; return allocate(array1); } } private final Object[] array1 = new Object[1]; /** * Invokes this constructor with the specified two arguments. * * @param arg0 the first argument. * @param arg1 the second argument. * @return the object being instantiated. */ public final Object newInstance(Object arg0, Object arg1) { synchronized (this) { array2[0] = arg0; array2[1] = arg1; return allocate(array2); } } private final Object[] array2 = new Object[2]; /** * Invokes this constructor with the specified three arguments. * * @param arg0 the first argument. * @param arg1 the second argument. * @param arg2 the third argument. * @return the object being instantiated. */ public final Object newInstance(Object arg0, Object arg1, Object arg2) { synchronized (this) { array3[0] = arg0; array3[1] = arg1; array3[2] = arg2; return allocate(array3); } } private final Object[] array3 = new Object[3]; /** * Invokes this constructor with the specified four arguments. * * @param arg0 the first argument. * @param arg1 the second argument. * @param arg2 the third argument. * @param arg3 the fourth argument. * @return the object being instantiated. */ public final Object newInstance(Object arg0, Object arg1, Object arg2, Object arg3) { synchronized (this) { array4[0] = arg0; array4[1] = arg1; array4[2] = arg2; array4[3] = arg3; return allocate(array4); } } private final Object[] array4 = new Object[4]; } /** * This class represents a run-time method obtained through reflection. * * Here are few examples of utilization:[code] * // Non-static method: fastMap.put(myKey, myValue) * Reflection.Method putKeyValue * = Reflection.getMethod( * "javolution37.javolution.util.FastMap.put(java.lang.Object, java.lang.Object)"); * Object previous = putKeyValue.invoke(fastMap, myKey, myValue); * * // Static method: System.nanoTime() (JRE1.5+) * Reflection.Method nanoTime * = Reflection.getMethod("java.lang.System.nanoTime()"); * long time = ((Long)nanoTime.invoke(null)).longValue();[/code] */ public static abstract class Method { /** * Default constructor. */ public Method() { } /** * Executes this method with the specified arguments. * * @param thisObject the object upon which this method is invoked * or null for static methods. * @param args the method arguments. * @return the result of the execution. */ protected abstract Object execute(Object thisObject, Object[] args); /** * Invokes this method on the specified object which might be * null if the method is static (convenience method). * * @param thisObject the object upon which this method is invoked * or null for static methods. * @return the result of the invocation. */ public final Object invoke(Object thisObject) { return execute(thisObject, NO_ARG); } private static final Object[] NO_ARG = new Object[0]; /** * Invokes this method with the specified single argument * on the specified object which might be null * if the method is static (convenience method). * * @param thisObject the object upon which this method is invoked * or null for static methods. * @param arg0 the single argument. * @return the result of the invocation. */ public final Object invoke(Object thisObject, Object arg0) { synchronized (this) { array1[0] = arg0; return execute(thisObject, array1); } } private final Object[] array1 = new Object[1]; /** * Invokes this method with the specified two arguments * on the specified object which might be null * if the method is static (convenience method). * * @param thisObject the object upon which this method is invoked * or null for static methods. * @param arg0 the first argument. * @param arg1 the second argument. * @return the result of the invocation. * @throws RuntimeException wrapping any exception raised during * invocation (see Throwable.getCause()). */ public final Object invoke(Object thisObject, Object arg0, Object arg1) { synchronized (this) { array2[0] = arg0; array2[1] = arg1; return execute(thisObject, array2); } } private final Object[] array2 = new Object[2]; /** * Invokes this method with the specified three arguments * on the specified object which might be null * if the method is static. * * @param thisObject the object upon which this method is invoked * or null for static methods. * @param arg0 the first argument (convenience method). * @param arg1 the second argument. * @param arg2 the third argument. * @return the result of the invocation. */ public final Object invoke(Object thisObject, Object arg0, Object arg1, Object arg2) { synchronized (this) { array3[0] = arg0; array3[1] = arg1; array3[2] = arg2; return execute(thisObject, array3); } } private final Object[] array3 = new Object[3]; /** * Invokes this method with the specified four arguments * on the specified object which might be null * if the method is static (convenience method). * * @param thisObject the object upon which this method is invoked * or null for static methods. * @param arg0 the first argument. * @param arg1 the second argument. * @param arg2 the third argument. * @param arg3 the fourth argument. * @return the result of the invocation. */ public final Object invoke(Object thisObject, Object arg0, Object arg1, Object arg2, Object arg3) { synchronized (this) { array4[0] = arg0; array4[1] = arg1; array4[2] = arg2; array4[3] = arg3; return execute(thisObject, array4); } } private final Object[] array4 = new Object[4]; } }

Ads help maintain this website.