[jbpm-commits] JBoss JBPM SVN: r6372 - in jbpm4/trunk/modules/pvm/src: test/java/org/jbpm/pvm/internal/wire and 1 other directory.

do-not-reply at jboss.org do-not-reply at jboss.org
Sat May 22 00:22:49 EDT 2010


Author: alex.guizar at jboss.com
Date: 2010-05-22 00:22:48 -0400 (Sat, 22 May 2010)
New Revision: 6372

Modified:
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/util/ReflectUtil.java
   jbpm4/trunk/modules/pvm/src/test/java/org/jbpm/pvm/internal/wire/ObjectWireTest.java
Log:
JBPM-2717: allow unboxing and widening primitive conversions in property injection

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/util/ReflectUtil.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/util/ReflectUtil.java	2010-05-21 11:46:12 UTC (rev 6371)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/util/ReflectUtil.java	2010-05-22 04:22:48 UTC (rev 6372)
@@ -5,7 +5,9 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.jbpm.api.JbpmException;
 import org.jbpm.internal.log.Log;
@@ -18,10 +20,32 @@
 import org.jbpm.pvm.internal.wire.WireContext;
 import org.jbpm.pvm.internal.wire.descriptor.ArgDescriptor;
 
-public abstract class ReflectUtil {
+public class ReflectUtil {
+  
+  private ReflectUtil() {
+    // hide default constructor to prevent instantiation
+  }
 
-  private static Log log = Log.getLog(ReflectUtil.class.getName());
-  
+  private static final Log log = Log.getLog(ReflectUtil.class.getName());
+
+  /**
+   * Maps wrapper <code>Class</code>es to their corresponding primitive types.
+   */
+  private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = createWrapperPrimitiveMap();
+
+  private static Map<Class<?>, Class<?>> createWrapperPrimitiveMap() {
+    Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
+    map.put(Boolean.class, boolean.class);
+    map.put(Byte.class, byte.class);
+    map.put(Character.class, char.class);
+    map.put(Short.class, short.class);
+    map.put(Integer.class, int.class);
+    map.put(Long.class, long.class);
+    map.put(Double.class, double.class);
+    map.put(Float.class, float.class);
+    return map;
+  }
+
   /** searches for the field in the given class and in its super classes */
   public static Field findField(Class<?> clazz, String fieldName) {
     return findField(clazz, fieldName, clazz);
@@ -200,38 +224,136 @@
   }
 
   public static boolean isArgumentMatch(Class<?>[] parameterTypes, List<ArgDescriptor> argDescriptors, Object[] args) {
-    int nbrOfArgs = 0;
-    if (args!=null) nbrOfArgs = args.length;
+    int nbrOfArgs = args!=null ? args.length : 0;
+    int nbrOfParameterTypes = parameterTypes!=null ? parameterTypes.length : 0;
     
-    int nbrOfParameterTypes = 0;
-    if (parameterTypes!=null) nbrOfParameterTypes = parameterTypes.length;
+    if (nbrOfArgs!=nbrOfParameterTypes) {
+      return false;
+    }
     
-    if ( (nbrOfArgs==0)
-         && (nbrOfParameterTypes==0)
-       ) {
+    if (nbrOfArgs==0) {
       return true;
     }
     
-    if (nbrOfArgs!=nbrOfParameterTypes) {
-      return false;
-    }
-
-    for (int i=0; (i<parameterTypes.length); i++) {
+    for (int i=0; i<parameterTypes.length; i++) {
       Class<?> parameterType = parameterTypes[i];
-      String argTypeName = (argDescriptors!=null ? argDescriptors.get(i).getTypeName() : null);
-      if (argTypeName!=null) {
-         if (! argTypeName.equals(parameterType.getName())) {
-           return false;
-         }
-      } else if ( (args[i]!=null)
-                  && (! parameterType.isAssignableFrom(args[i].getClass()))
-                ) {
+      String argTypeName;
+      if (argDescriptors == null || (argTypeName = argDescriptors.get(i).getTypeName()) == null) {
+        Object arg = args[i];
+        if (!isAssignable(parameterType, arg)) {
+          return false;
+        }
+      }
+      else if (!parameterType.getName().equals(argTypeName)) {
         return false;
       }
     }
     return true;
   }
 
+  /**
+   * <p>
+   * Checks if the given <code>value</code> can be assigned to a variable of the specified
+   * <code>type</code>.
+   * </p>
+   * <p>
+   * Unlike the {@link Class#isAssignableFrom(Class)} method, this method takes into
+   * account widenings of primitive types and <code>null</code>s.
+   * </p>
+   * <p>
+   * Primitive widenings allow an int to be assigned to a long, float or double. This method
+   * returns the correct result for these cases.
+   * </p>
+   * <p>
+   * <code>null</code> may be assigned to any reference type. This method will return
+   * <code>true</code> if <code>null</code> is passed in and the specified <code>type</code> is
+   * a reference type.
+   * </p>
+   * <p>
+   * Specifically, this method tests whether the class of the given <code>value</code> parameter
+   * can be converted to the type represented by the specified <code>Class</code> via an
+   * identity, widening primitive or widening reference conversion. See the
+   * <a href="http://java.sun.com/docs/books/jls/">Java Language Specification</a>,
+   * sections 5.1.1, 5.1.2 and 5.1.4 for details.
+   * </p>
+   * @param type the Class to try to assign into
+   * @param value the object to check, may be <code>null</code>
+   * @return <code>true</code> if assignment is possible
+   * @see <a
+   *      href="http://commons.apache.org/lang/api-release/org/apache/commons/lang/ClassUtils.html#isAssignable(java.lang.Class,%20java.lang.Class)"
+   *      >ClassUtils.isAssignable()</a>
+   */
+  private static boolean isAssignable(Class<?> type, Object value) {
+    // check for null value
+    if (value == null) {
+      // null is assignable to reference types
+      return !type.isPrimitive();
+    }
+
+    if (type.isPrimitive()) {
+      // unboxing
+      Class<?> valueType = wrapperToPrimitive(value.getClass());
+      if (null == valueType) {
+        return false;
+      }
+      if (type == valueType) {
+        return true;
+      }
+      // widening primitive conversion
+      if (int.class == valueType) {
+        return long.class == type || float.class == type || double.class == type;
+      }
+      if (long.class == valueType) {
+        return float.class == type || double.class == type;
+      }
+      if (boolean.class == valueType) {
+        return false;
+      }
+      if (double.class == valueType) {
+        return false;
+      }
+      if (float.class == valueType) {
+        return double.class == type;
+      }
+      if (char.class == valueType) {
+        return int.class == type || long.class == type || float.class == type
+          || double.class == type;
+      }
+      if (short.class == valueType) {
+        return int.class == type || long.class == type || float.class == type
+          || double.class == type;
+      }
+      if (byte.class == valueType) {
+        return short.class == type || int.class == type || long.class == type
+          || float.class == type || double.class == type;
+      }
+      // should never get here
+      return false;
+    }
+
+    return type.isInstance(value);
+  }
+
+  /**
+   * <p>
+   * Converts the specified wrapper class to its corresponding primitive class.
+   * </p>
+   * <p>
+   * If the passed in class is a wrapper class for a primitive type, this primitive type will be
+   * returned (e.g. <code>Integer.TYPE</code> for <code>Integer.class</code>). For other
+   * classes, or if the parameter is <code>null</code>, the return value is <code>null</code>.
+   * </p>
+   * @param cls the class to convert, may be <code>null</code>
+   * @return the corresponding primitive type if <code>cls</code> is a wrapper class,
+   *         <code>null</code> otherwise
+   * @see <a
+   *      href="http://commons.apache.org/lang/api-release/org/apache/commons/lang/ClassUtils.html#wrapperToPrimitive(java.lang.Class)"
+   *      >ClassUtils.wrapperToPrimitive</a>
+   */
+  private static Class<?> wrapperToPrimitive(Class<?> cls) {
+    return wrapperPrimitiveMap.get(cls);
+  }
+
   public static String getSignature(String methodName, List<ArgDescriptor> argDescriptors, Object[] args) {
     String signature = methodName+"(";
     if (args!=null) {

Modified: jbpm4/trunk/modules/pvm/src/test/java/org/jbpm/pvm/internal/wire/ObjectWireTest.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/test/java/org/jbpm/pvm/internal/wire/ObjectWireTest.java	2010-05-21 11:46:12 UTC (rev 6371)
+++ jbpm4/trunk/modules/pvm/src/test/java/org/jbpm/pvm/internal/wire/ObjectWireTest.java	2010-05-22 04:22:48 UTC (rev 6372)
@@ -534,7 +534,7 @@
   }
 
   public static class OverriddenFieldInjectionClass extends FieldInjectionClass {
-    String txtOne = null;
+    String txtOne;
   }
 
   public void testOverriddenFieldInjection() {
@@ -623,66 +623,143 @@
   }
 
   public static class PropertyInjectionClass {
-    String p = null;
-    String q = null;
-    String propertyP = null;
-    String propertyQ = null;
-    public void setP(String p) {
-      propertyP = p;
+    String s;
+    String propertyS;
+    boolean z;
+    boolean propertyZ;
+    char c;
+    char propertyC;
+    int i;
+    int propertyI;
+    long l;
+    long propertyL;
+    float f;
+    float propertyF;
+    double d;
+    double propertyD;
+    
+    public void setS(String s) {
+      propertyS = s;
     }
-    public void setQ(String q) {
-      propertyQ = q;
+    public void setZ(boolean z) {
+      propertyZ = z;
     }
+    public void setC(char c) {
+      propertyC = c;
+    }
+    public void setI(int i) {
+      propertyI = i;
+    }
+    public void setL(long l) {
+      propertyL = l;
+    }
+    public void setF(float f) {
+      propertyF = f;
+    }
+    public void setD(double d) {
+      propertyD = d;
+    }
   }
 
   public void testPropertyInjection() {
     WireContext wireContext = createWireContext(
       "<objects>" +
       "  <object name='o' class='"+PropertyInjectionClass.class.getName()+"'>" +
-      "    <property name='p'>" +
+      "    <property name='s'>" +
       "      <string value='hello' />" +
       "    </property>" +
-      "    <property name='q'>" +
-      "      <string value='world' />" +
+      "    <property name='z'>" +
+      "      <true/>" +
       "    </property>" +
+      "    <property name='c'>" +
+      "      <char value='x'/>" +
+      "    </property>" +
+      "    <property name='i'>" +
+      "      <int value='32768'/>" +
+      "    </property>" +
+      "    <property name='l'>" +
+      "      <long value='2147483648'/>" +
+      "    </property>" +
+      "    <property name='f'>" +
+      "      <float value='3e9'/>" +
+      "    </property>" +
+      "    <property name='d'>" +
+      "      <double value='1e39'/>" +
+      "    </property>" +
       "  </object>" +
       "</objects>"
     );
 
-    Object o = wireContext.get("o");
+    PropertyInjectionClass pic = (PropertyInjectionClass) wireContext.get("o");
 
-    assertNotNull(o);
-    assertEquals(PropertyInjectionClass.class, o.getClass());
-    assertNull(((PropertyInjectionClass)o).p);
-    assertNull(((PropertyInjectionClass)o).q);
-    assertEquals("hello", ((PropertyInjectionClass)o).propertyP);
-    assertEquals("world", ((PropertyInjectionClass)o).propertyQ);
+    assertNull(pic.s);
+    assertEquals("hello", pic.propertyS);
+    assertFalse(pic.z);
+    assertTrue(pic.propertyZ);
+    assertEquals('\0', pic.c);
+    assertEquals('x', pic.propertyC);
+    assertEquals(0, pic.i);
+    assertEquals(1 << 15, pic.propertyI);
+    assertEquals(0, pic.l);
+    assertEquals(1l << 31, pic.propertyL);
+    assertEquals(0, pic.f, 0);
+    assertEquals(3e9f, pic.propertyF, 0);
+    assertEquals(0, pic.d, 0);
+    assertEquals(1e39, pic.propertyD, 0);
   }
 
-  public void testPropertyInjectionWithSetter() {
+  public void testWideningPropertyInjection() {
     WireContext wireContext = createWireContext(
       "<objects>" +
       "  <object name='o' class='"+PropertyInjectionClass.class.getName()+"'>" +
-      "    <property setter='setP'>" +
+      "    <property name='s'>" +
       "      <string value='hello' />" +
       "    </property>" +
-      "    <property setter='setQ'>" +
-      "      <string value='world' />" +
+      "    <property name='i'>" +
+      "      <char value=' '/>" +
       "    </property>" +
+      "    <property name='l'>" +
+      "      <int value='2147483647'/>" +
+      "    </property>" +
+      "    <property name='f'>" +
+      "      <int value='16777216'/>" +
+      "    </property>" +
+      "    <property name='d'>" +
+      "      <long value='9007199254740992'/>" +
+      "    </property>" +
       "  </object>" +
       "</objects>"
     );
 
-    Object o = wireContext.get("o");
+    PropertyInjectionClass pic = (PropertyInjectionClass) wireContext.get("o");
 
-    assertNotNull(o);
-    assertEquals(PropertyInjectionClass.class, o.getClass());
-    assertNull(((PropertyInjectionClass)o).p);
-    assertNull(((PropertyInjectionClass)o).q);
-    assertEquals("hello", ((PropertyInjectionClass)o).propertyP);
-    assertEquals("world", ((PropertyInjectionClass)o).propertyQ);
+    assertEquals(0, pic.i);
+    assertEquals(' ', pic.propertyI);
+    assertEquals(0, pic.l);
+    assertEquals(Integer.MAX_VALUE, pic.propertyL);
+    assertEquals(0, pic.f, 0);
+    assertEquals(1 << 24, pic.propertyF, 0);
+    assertEquals(0, pic.d, 0);
+    assertEquals(1l << 53, pic.propertyD, 0);
   }
 
+  public void testPropertyInjectionWithSetter() {
+    WireContext wireContext = createWireContext(
+      "<objects>" +
+      "  <object name='o' class='"+PropertyInjectionClass.class.getName()+"'>" +
+      "    <property setter='setS'>" +
+      "      <string value='hello' />" +
+      "    </property>" +
+      "  </object>" +
+      "</objects>"
+    );
+
+    PropertyInjectionClass pic = (PropertyInjectionClass) wireContext.get("o");
+
+    assertNull(pic.s);
+    assertEquals("hello", pic.propertyS);
+  }
+
   public void testBadPropertyDescriptor() {
     List<Problem> problems = parseProblems(
       "<objects>" +
@@ -727,30 +804,24 @@
     WireContext wireContext = createWireContext(
       "<objects>" +
       "  <object name='o' class='"+InheritedPropertyInjectionClass.class.getName()+"'>" +
-      "    <property name='p'>" +
+      "    <property name='s'>" +
       "      <string value='hello' />" +
       "    </property>" +
-      "    <property name='q'>" +
-      "      <string value='world' />" +
-      "    </property>" +
       "  </object>" +
       "</objects>"
     );
 
-    Object o = wireContext.get("o");
+    InheritedPropertyInjectionClass ipic = (InheritedPropertyInjectionClass) wireContext.get("o");
 
-    assertNotNull(o);
-    assertEquals(InheritedPropertyInjectionClass.class, o.getClass());
-    assertNull(((InheritedPropertyInjectionClass)o).p);
-    assertNull(((InheritedPropertyInjectionClass)o).q);
-    assertEquals("hello", ((InheritedPropertyInjectionClass)o).propertyP);
-    assertEquals("world", ((InheritedPropertyInjectionClass)o).propertyQ);
+    assertNull(ipic.s);
+    assertEquals("hello", ipic.propertyS);
   }
 
   public static class OverwrittenPropertyInjectionClass extends PropertyInjectionClass {
-    String overwrittenPropertyQ = null;
-    public void setQ(String q) {
-      overwrittenPropertyQ = q;
+    String overwrittenPropertyS;
+    @Override
+    public void setS(String s) {
+      overwrittenPropertyS = s;
     }
   }
 
@@ -758,25 +829,18 @@
     WireContext wireContext = createWireContext(
       "<objects>" +
       "  <object name='o' class='"+OverwrittenPropertyInjectionClass.class.getName()+"'>" +
-      "    <property name='p'>" +
+      "    <property name='s'>" +
       "      <string value='hello' />" +
       "    </property>" +
-      "    <property name='q'>" +
-      "      <string value='world' />" +
-      "    </property>" +
       "  </object>" +
       "</objects>"
     );
 
-    Object o = wireContext.get("o");
+    OverwrittenPropertyInjectionClass opic = (OverwrittenPropertyInjectionClass) wireContext.get("o");
 
-    assertNotNull(o);
-    assertEquals(OverwrittenPropertyInjectionClass.class, o.getClass());
-    assertNull(((OverwrittenPropertyInjectionClass)o).p);
-    assertNull(((OverwrittenPropertyInjectionClass)o).q);
-    assertEquals("hello", ((OverwrittenPropertyInjectionClass)o).propertyP);
-    assertNull(((OverwrittenPropertyInjectionClass)o).propertyQ);
-    assertEquals("world", ((OverwrittenPropertyInjectionClass)o).overwrittenPropertyQ);
+    assertNull(opic.s);
+    assertNull(opic.propertyS);
+    assertEquals("hello", opic.overwrittenPropertyS);
   }
 
   public static class InvokeClass {



More information about the jbpm-commits mailing list