From 7c279747beb43c7e88633a6228a155a30e6834f7 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 27 May 2024 11:38:33 -0400 Subject: Initial import --- .../israfil/foundation/dynamic/DynamicUtil.java | 297 +++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java (limited to 'israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java') diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java new file mode 100644 index 0000000..9c165bc --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java @@ -0,0 +1,297 @@ +/* + * 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, Class>(); + static Map, Class> _boxedTypeEquivalents = new HashMap, Class>(); + 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; + } + } + +} -- cgit v1.2.3