/* * Copyright (c) 2003-2009 Israfil Consulting Services Corporation * Copyright (c) 2003-2009 Christian Edward Gruber * All Rights Reserved * * This software is licensed under the Berkeley Standard Distribution license, * (BSD license), as defined below: * * 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. * 3. Neither the name of Israfil Consulting Services nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * 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 OWNER 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. * * $Id: DynamicUtil.java 91 2006-08-03 21:38:24Z cgruber $ */ package net.israfil.foundation.dynamic; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import net.israfil.foundation.collections.ArrayUtils; /** * Utilities used for the implementation of Dynamic and * related interfaces. * * @author Christian Edward Gruber */ public final class DynamicUtil { private static Logger logger = Logger.getLogger(DynamicUtil.class.getName()); private DynamicUtil() {} static Map> _primitiveTypes = new HashMap<>(); static Map, Class> _primitiveTypeEquivalents = new HashMap<>(); static Map, Class> _boxedTypeEquivalents = new HashMap<>(); static { _primitiveTypes.put("boolean",boolean.class); _primitiveTypeEquivalents.put(Boolean.class,boolean.class); _boxedTypeEquivalents.put(boolean.class,Boolean.class); _primitiveTypes.put("int",int.class); _primitiveTypeEquivalents.put(Integer.class,int.class); _boxedTypeEquivalents.put(int.class,Integer.class); _primitiveTypes.put("long",long.class); _primitiveTypeEquivalents.put(Long.class,long.class); _boxedTypeEquivalents.put(long.class,Long.class); _primitiveTypes.put("short",short.class); _primitiveTypeEquivalents.put(Short.class,short.class); _boxedTypeEquivalents.put(short.class,Short.class); _primitiveTypes.put("byte",byte.class); _primitiveTypeEquivalents.put(Byte.class,byte.class); _boxedTypeEquivalents.put(byte.class,Byte.class); _primitiveTypes.put("char",char.class); _primitiveTypeEquivalents.put(Character.class,char.class); _boxedTypeEquivalents.put(char.class,Character.class); _primitiveTypes.put("double",double.class); _primitiveTypeEquivalents.put(Double.class,double.class); _boxedTypeEquivalents.put(double.class,Double.class); _primitiveTypes.put("float",float.class); _primitiveTypeEquivalents.put(Float.class,float.class); _boxedTypeEquivalents.put(float.class,Float.class); } public static boolean hasPrimitiveTypeEquivalent(Class c) { return _primitiveTypeEquivalents.containsKey(c); } public static Class getPrimitiveTypeEquivalent(Class c) { return _primitiveTypeEquivalents.get(c); } public static boolean hasBoxedTypeEquivalent(Class c) { return _boxedTypeEquivalents.containsKey(c); } public static Class getBoxedTypeEquivalent(Class c) { return _boxedTypeEquivalents.get(c); } protected static final Map, Set>> classes = new HashMap, Set>>(); public static Set> getAllParentTypes(Class c) { if (!classes.containsKey(c)) { Set> parents = new HashSet>(); for (Class cType = c; cType != Object.class && cType != null; cType = cType .getSuperclass()) { parents.add(cType); addSuperInterfaces(parents,cType); } parents.add(Object.class); parents.remove(c); classes.put(c,parents); } return classes.get(c); } public static void addSuperInterfaces(Set> set, Class c) { if (c == null || c.getInterfaces() == null) return; for (Class i : c.getInterfaces()) { set.add(i); addSuperInterfaces(set,i); } } /** * Conveniently get a field from an object, automatically trapping * exceptions and returning the Field or null if no such field exists. */ public static Field getField(Object receiver, String attributeName) { Field f = null; try { f = receiver.getClass().getField(attributeName); } catch (NoSuchFieldException e) { return null; } return f; } /** * @see org.israfil.maveric.Dynamic#respondsTo(java.lang.String) */ public static boolean respondsTo(Object receiver, String selector) { return (getMethodForSelector(receiver,selector) != null); } public static Method getMethodForSelector(Object receiver, String selector) { if (receiver instanceof Class) return getMethodForSelector((Class) receiver, selector); return getMethodForSelector(receiver.getClass(),selector); } public static Method getMethodForSelector(Class receiverClass, String selector) { try { return _getMethodForSelector(receiverClass,selector); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { return null; } } protected static Method _getMethodForSelector(Class receiverClass, String selector) throws ClassNotFoundException, NoSuchMethodException { StringTokenizer st = new StringTokenizer(selector,":"); Class[] paramTypes = new Class[st.countTokens() - 1]; String methodName = st.nextToken(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (int i = 0; st.hasMoreTokens(); i++) { String className = st.nextToken(); if (DynamicUtil._primitiveTypes.containsKey(className)) paramTypes[i] = (Class) DynamicUtil._primitiveTypes .get(className); else paramTypes[i] = loader.loadClass(className); } return receiverClass.getMethod(methodName,paramTypes); } /** * @see org.israfil.maveric.Dynamic#perform(java.lang.String, java.lang.Object[]) */ public static Object performOn(Object receiver, String selector, Object ... parameters) { if (parameters == null) parameters = new Object[]{}; Method m = getMethodForSelector(receiver,selector); try { if (m != null) return m.invoke(receiver,parameters); //else return null; else throw new NoSuchMethodError("No public method defined with selector \""+selector+"\""); } catch (InvocationTargetException ite) { logger.log(Level.FINE,ite.getClass().getName()+" thrown attempting to invoke selector: " + selector + " on "+receiver,ite); if (ite.getCause() != null) throw new RuntimeException(ite.getCause()); else throw new RuntimeException(ite); } catch (IllegalAccessException iae) { logger.log(Level.FINE,iae.getClass().getName()+" thrown attempting to invoke selector: " + selector + " on "+receiver,iae); throw new RuntimeException(iae); } } /** * Construct an object of the given named class with the provided * constructor parameters. If there is no such class or no * constructor is found that is appropriate to these parameters, * the method will return null; * * @param className The fully qualified name of a Class object to be instantiated. * @param parameters Optional parameters for the discovered constructor * @return an instance of the named class, or null if no such class or constructor is found. */ public static Object construct(String className, Object ... parameters) { try { Class clazz = Thread.currentThread().getContextClassLoader() .loadClass(className); return construct(clazz,parameters); } catch (ClassNotFoundException e) { logger.log(Level.FINE,e.getClass().getName()+" thrown attempting to construct a " + className + ".", e); return null; } } /** * Construct an object of the given class with the provided * constructor parameters. If there is no such class or no * constructor is found that is appropriate to these parameters, * the method will return null; * * @param c A Class object to be instantiated. * @param parameters Optional parameters for the discovered constructor * @return an instance of the named class, or null if no such class or constructor is found. */ public static T construct(Class c, Object ... parameters) { Class [] parmTypes = new Class[parameters.length]; for (int i = 0; i < parmTypes.length; i++) { parmTypes[i] = parameters[i].getClass(); } return construct(c,parmTypes,parameters); } /** * Construct an object of the given named class with the provided * constructor parameter types and values. If there is no such class * or no constructor is found that is appropriate to these parameters, * the method will return null; * * @param className The fully qualified name of a Class object to be instantiated. * @param parameterTypes The types that form the desired constructor's method signature * @param parameters Optional parameters for the discovered constructor * @return an instance of the named class, or null if no such class or constructor is found. */ public static Object construct(String className, Class[] parameterTypes, Object... parameters) { try { Class clazz = Thread.currentThread().getContextClassLoader() .loadClass(className); return construct(clazz, parameterTypes,parameters); } catch (ClassNotFoundException e) { logger.log(Level.FINE,e.getClass().getName()+" thrown attempting to construct a " + className + ".", e); return null; } } /** * Construct an object of the given class with the provided * constructor parameter types and values. If there is no such class * or no constructor is found that is appropriate to these parameters, * the method will return null; * * @param c A Class object to be instantiated. * @param parameterTypes The types that form the desired constructor's method signature * @param parameters Optional parameters for the discovered constructor * @return an instance of the named class, or null if no such class or constructor is found. */ @SuppressWarnings("unchecked") public static T construct(Class c, Class[] parameterTypes, Object... parameters) { try { Constructor[] constructors = (Constructor[]) c.getDeclaredConstructors(); for (Constructor constructor : constructors) { if (ArrayUtils.equivalent(parameterTypes,constructor.getParameterTypes())) { return (T)constructor.newInstance(parameters); } } return null; } catch (Exception e) { logger.log(Level.FINE,e.getClass().getName()+" thrown attempting to construct a " + c.getName() + ".", e); return null; } } }