From 7c279747beb43c7e88633a6228a155a30e6834f7 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 27 May 2024 11:38:33 -0400 Subject: Initial import --- .../net/israfil/foundation/dynamic/Dynamic.java | 62 +++++ .../israfil/foundation/dynamic/DynamicObject.java | 69 +++++ .../israfil/foundation/dynamic/DynamicUtil.java | 297 +++++++++++++++++++++ .../foundation/dynamic/DynamicallyAccessible.java | 50 ++++ .../dynamic/DynamicallyAccessibleObject.java | 144 ++++++++++ .../foundation/dynamic/DynamicallyMutable.java | 54 ++++ .../dynamic/DynamicallyMutableObject.java | 197 ++++++++++++++ 7 files changed, 873 insertions(+) create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/Dynamic.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicObject.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicUtil.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessible.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessibleObject.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutable.java create mode 100644 israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutableObject.java (limited to 'israfil-foundation-dynamic/src/main/java/net') diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/Dynamic.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/Dynamic.java new file mode 100644 index 0000000..e1bdb8b --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/Dynamic.java @@ -0,0 +1,62 @@ +/* + * Copyright © 2003-2009 Israfil Consulting Services Corporation + * Copyright © 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: Dynamic.java 14 2006-01-27 23:50:37Z cgruber $ + */ +package net.israfil.foundation.dynamic; + + +/** + * A simple interface that defines the ability to perform and detect + * arbitrary methods, by use of a "selector" string. The selector + * is a string constructed of a method name and an arbitrary number of + * classes representing parameter types. These are then concatenated + * with a ":" separator. The resulting string represents a full + * method signature, without reference to the implementing class. This + * allows arbitrary classes to be interrogated as to the validity of a + * given method call (i.e. do they or their parents implement it). It + * also then allows the method with the given signature to be invoked. + * + * For example: + * + * if (a.respondsTo("doThing:java.lang.String")) + * a.perform("doThing:java.lang.String",aString); + * + * + * @author Christian Edward Gruber + */ +public interface Dynamic { + + public Object perform(String selector, Object ... parameters); + + public boolean respondsTo(String selector); + +} diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicObject.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicObject.java new file mode 100644 index 0000000..3beda20 --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicObject.java @@ -0,0 +1,69 @@ +/* + * Copyright © 2003-2009 Israfil Consulting Services Corporation + * Copyright © 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: DynamicObject.java 92 2006-08-04 12:39:25Z cgruber $ + */ +package net.israfil.foundation.dynamic; + +import java.lang.reflect.Method; + + +/** + * An abstract superclass that implements the Dynamic interface, with + * all the necessary lookup logic. It also implements static equivalents of + * the Dynamic methods. + * + * @author Christian Edward Gruber + */ +public abstract class DynamicObject implements Dynamic { + //private static Logger logger = Logger.getLogger(DynamicObject.class.getName()); + + + /** + * @see org.israfil.maveric.Dynamic#perform(java.lang.String, java.lang.Object[]) + */ + public Object perform(String selector, Object ... parameters) { + return DynamicUtil.performOn(this,selector,parameters); + } + + /** + * @see org.israfil.maveric.Dynamic#respondsTo(java.lang.String) + */ + public boolean respondsTo(String selector) { + return DynamicUtil.respondsTo(this,selector); + } + + protected Method getMethodForSelector(String selector) { + return DynamicUtil.getMethodForSelector(this,selector); + } + + +} 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; + } + } + +} diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessible.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessible.java new file mode 100644 index 0000000..202bd11 --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessible.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2003-2009 Israfil Consulting Services Corporation + * Copyright © 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: DynamicallyAccessible.java 23 2006-02-01 17:17:31Z cgruber $ + */ +package net.israfil.foundation.dynamic; + + +/** + * A generic accessor interface. By this means any attribute should be + * retrievable by using the provided methods, as well as the existence of + * an accessor method can be detected. + * + * @author Christian Edward Gruber + */ +public interface DynamicallyAccessible extends Dynamic{ + + public boolean hasAttribute(String attributeName); + public Object get(String attributeName); + public abstract boolean hasAccessor(String attributeName); + +} \ No newline at end of file diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessibleObject.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessibleObject.java new file mode 100644 index 0000000..fd376a7 --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyAccessibleObject.java @@ -0,0 +1,144 @@ +/* + * Copyright © 2003-2009 Israfil Consulting Services Corporation + * Copyright © 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: DynamicallyAccessibleObject.java 76 2006-08-03 15:37:05Z cgruber $ + */ +package net.israfil.foundation.dynamic; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.israfil.foundation.core.Strings; + +/** + * An implementation of the DynamicallyAccessible interface with the + * appropriate infrastructure for retrieving field data, via accessors if + * such are implemented, or directly from the field where not. + * + * @author Christian Edward Gruber + * @author Latest: $Author: cgruber $ + * @version $Revision: 76 $ + */ +public abstract class DynamicallyAccessibleObject extends DynamicObject implements DynamicallyAccessible { + private static Logger logger = Logger.getLogger(DynamicallyAccessibleObject.class.getName()); + + public Object get(String attributeName) { + return get(this,attributeName); + } + + public static Object get(Object receiver, String attributeName) { + try { + return _get(receiver, attributeName); + } catch (NoSuchFieldException e) { + logger.log(Level.FINE,"Object attempted to dynamically access a non-existant or unreadable field.",e); + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + logger.log(Level.FINE,"Object attempted to dynamically access a inaccessible field.",e); + throw new RuntimeException(e); + } + } + + /** @deprecated This method is deprecated in favour of the static _get(receiver, attributeName) */ + Object _get(String attributeName) throws NoSuchFieldException, IllegalAccessException { + return _get(this,attributeName); + } + + /** + * Internal _get method that throws all the exceptions, to be wrapped by public methods + * so that the various exceptions can be given meaning in different contexts (such + * as fetching data, vs. testing for the existance of something. + * + * @param attributeName + * @return + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + static Object _get(Object receiver, String attributeName) throws NoSuchFieldException, IllegalAccessException { + if (hasAccessor(receiver,attributeName)) return DynamicUtil.performOn(receiver,_getAccessorSelector(receiver,attributeName), new Object[]{}); + return receiver.getClass().getField(attributeName).get(receiver); + } + + /** + * Determines whether an attribute of the given name exists on this object's + * definition, or if not, if there is a javabeans-style accessor named + * appropriately for that attribute. Put another way, determines whether + * a get() call would work against this attribute name. + * + * The existing implementation is inefficient, but simple to maintain. + */ + public boolean hasAttribute(String attributeName) { + return hasAttribute(this,attributeName); + } + + public static boolean hasAttribute(Object receiver, String attributeName) { + try { + _get(receiver,attributeName); + return true; + } catch (NoSuchFieldException e) { + logger.log(Level.FINEST,"Object attempted to dynamically access a non-existant or unreadable field.",e); + return false; + } catch (IllegalAccessException e) { + logger.log(Level.FINEST,"Object attempted to dynamically access a inaccessible field.",e); + return false; + } + } + + protected static final String[] accessPrefixes = new String[] { "get","is" }; + + /** @deprecated This method is deprecated in favour of the static _getAccessorSelector(receiver,attribute) */ + protected String getAccessorSelector(String attributeName) { return _getAccessorSelector(this,attributeName); } + + protected static String _getAccessorSelector(Object receiver, String attributeName) { + if (attributeName == null || attributeName.equals("")) return null; + String camelAttr = camel(attributeName); + for (String prefix : accessPrefixes) { + String selector = prefix+camelAttr; + if (DynamicUtil.respondsTo(receiver,selector)) return selector; + } + // No accessor, try a NeXTSTEP-style accessor, named the same as the property. + if (DynamicUtil.respondsTo(receiver,attributeName)) return attributeName; + return null; + } + + public boolean hasAccessor(String attributeName) { + return hasAccessor(this,attributeName); + } + + public static boolean hasAccessor(Object receiver, String attributeName) { + return _getAccessorSelector(receiver,attributeName) != null; + } + + /** @deprecated This method has been moved to net.israfil.foundation.core.Strings.camel(String) */ + public static String camel(String string) { + return Strings.camel(string); + } + +} diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutable.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutable.java new file mode 100644 index 0000000..49b2a5d --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutable.java @@ -0,0 +1,54 @@ +/* + * 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: DynamicallyMutable.java 39 2006-02-23 00:01:17Z cgruber $ + */ +package net.israfil.foundation.dynamic; + + +/** + * A generic mutator interface. By this means any attribute should be + * mutable by using the provided methods. Additionally, the existence of + * a mutator method can be detected. + * + * @author Christian Edward Gruber + */ +public interface DynamicallyMutable extends DynamicallyAccessible{ + + public void set(String attributeName, Object attributeValue); + + public void set(String attributeName, Object attributeValue, Class type); + + public void setNull(String attributeName, Class type); + + public abstract boolean hasMutator(String attributeName, Class type); + +} \ No newline at end of file diff --git a/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutableObject.java b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutableObject.java new file mode 100644 index 0000000..63b301d --- /dev/null +++ b/israfil-foundation-dynamic/src/main/java/net/israfil/foundation/dynamic/DynamicallyMutableObject.java @@ -0,0 +1,197 @@ +/* + * Copyright © 2003-2009 Israfil Consulting Services Corporation + * Copyright © 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: DynamicallyMutableObject.java 127 2006-11-09 18:04:43Z cgruber $ + */ +package net.israfil.foundation.dynamic; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.israfil.foundation.core.Strings; +import net.israfil.foundation.core.Types; + +/** + * An abstract class which implements the DynamicallyMutableObject + * infrastructure. It sets a field's value, by mutator method if such + * exists, or directly if none such exists. + * + * @author Christian Edward Gruber + * + */ +public abstract class DynamicallyMutableObject extends DynamicallyAccessibleObject implements DynamicallyMutable { + private static Logger logger = Logger.getLogger(DynamicallyMutableObject.class.getName()); + + + public void setNull(String attributeName, Class valueType) { + setNull(this,attributeName,valueType); + } + public void setNull(Object target, String attributeName, Class valueType) { + if (valueType == null) + throw new IllegalArgumentException("Cannot give a valueType of null for setNull(String,Class) method."); + set(target,attributeName, null, valueType); + } + + public void set(String attributeName,Object value) { + set(this,attributeName,value); + } + public static void set(Object target, String attributeName,Object value) { + if (value == null) set(target,attributeName, value, null); + else set(target,attributeName, value, value.getClass()); + } + + public void set(String attributeName, Object value, Class valueType) { + set(this,attributeName,value,valueType); + } + public static void set(Object target, String attributeName, Object value, + Class valueType) { + if (value == null) { + if (valueType != null && valueType.isPrimitive()) throw new IllegalArgumentException("Attempted to set null on a variable or accessor for attribute '"+attributeName+"' of primitive type: " + valueType); + String selector = getMutatorSelector(target,attributeName,valueType); + if (selector != null) { + Method m = DynamicUtil.getMethodForSelector(target,selector); + Class[] parmTypes = m.getParameterTypes(); + if (parmTypes[0].isPrimitive()) + throw new IllegalArgumentException("Attempted to set null using an accessor for attribute '"+attributeName+"' of primitive type: " + parmTypes[0]); + DynamicUtil.performOn(target,selector,new Object[]{value}); + } else { + Field f = DynamicUtil.getField(target,attributeName); + if (f == null || f.getType().isPrimitive()) + throw new IllegalArgumentException("Attempted to set null but could not find a set"+Strings.camel(attributeName)+" method with a single non-primitive type."); + else + _setField(target,attributeName,valueType); + } + } else /* value != null */ { + if (valueType == null) valueType = value.getClass(); + if (hasMutator(target,attributeName,valueType)) { + DynamicUtil.performOn(target,getMutatorSelector(target,attributeName,valueType),new Object[]{value}); + return; + } else if(hasMutator(target,attributeName,DynamicUtil.getPrimitiveTypeEquivalent(valueType))) { + DynamicUtil.performOn(target,getMutatorSelector(target,attributeName,DynamicUtil.getPrimitiveTypeEquivalent(valueType)),new Object[]{value}); + return; + } else { + _setField(target,attributeName,value); + } + } + } + + protected void _setField(String attributeName,Object value) { + _setField(this,attributeName,value); + } + + protected static void _setField(Object target, String attributeName,Object value) { + Field f = DynamicUtil.getField(target,attributeName); + try { + if (f == null) { + RuntimeException rte = new RuntimeException("Could not find field named " + attributeName, + new NoSuchFieldException(target + " has no property " + attributeName)); + logger.log(Level.FINEST,"Failed to set field.",rte); + throw rte; + } + f.set(target,Types.convert(value,f.getType())); + } catch (IllegalAccessException e) { + logger.log(Level.FINEST,"Object attempted to dynamically access a inaccessible field.",e); + throw new RuntimeException(e); + } + } + + protected static final String mutatePrefix = "set"; + protected String getMutatorSelector(String attributeName, Class type) { + return getMutatorSelector(this,attributeName,type); + } + protected static String getMutatorSelector(Object target, + String attributeName, Class type) { + if (attributeName == null || attributeName.equals("")) return null; + String camelAttr = Strings.camel(attributeName); + String selector = null; + if (type == null) { + Method method = _tryToFindMutator(target,attributeName); + if (method == null) { + return null; + } else /* method != null */ { + if (method.getParameterTypes().length != 1) return null; + else return "set"+Strings.camel(attributeName)+":"+method.getParameterTypes()[0].getName(); + } + } else /* type != null */ { + + String tmp = mutatePrefix+camelAttr+":"+type.getName(); + if (DynamicUtil.respondsTo(target,tmp)) selector = tmp; + for (Class c : DynamicUtil.getAllParentTypes(type)) { + tmp = mutatePrefix + camelAttr + ":" + c.getName(); + if (DynamicUtil.respondsTo(target, tmp)) + selector = tmp; + } + } + return selector; + } + + protected static Method _tryToFindMutator(Object target, String attributeName) { + Method method = null; + Method[] methods = target.getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals("set"+Strings.camel(attributeName))) { + if (method == null) method = methods[i]; + else throw new IllegalArgumentException("Attempted to find "+mutatePrefix+Strings.camel(attributeName) + " without a given type, but more than one such method exists. Should use set(String,Object,Class)"); + } + } + return method; + } + + protected static Method _tryToFindMutator(Object target, + String attributeName, Class type) { + if (type == null) throw new IllegalArgumentException("Must set a type, or use _tryToFindMutator(Object,String)."); + String methodName = mutatePrefix+Strings.camel(attributeName); + Method m = DynamicUtil.getMethodForSelector(target,methodName+":"+type.getName()); + for (Class c : DynamicUtil.getAllParentTypes(type)) { + m = DynamicUtil.getMethodForSelector(target,methodName+":"+c.getName()); + } + return m; + } + + public static Method getMutator(Object target, String attributeName, + Class type) { + if (type == null) { + return _tryToFindMutator(target,attributeName); + } else { + return _tryToFindMutator(target,attributeName,type); + } + } + + public boolean hasMutator(String attributeName, Class type) { + return hasMutator(this,attributeName,type); + } + public static boolean hasMutator(Object target, String attributeName, + Class type) { + return getMutatorSelector(target,attributeName,type) != null; + } +} -- cgit v1.2.3