Author: cbegin
Date: Wed Jul 23 17:29:36 2008
New Revision: 679245

URL: http://svn.apache.org/viewvc?rev=679245&view=rev
Log: (empty)

Added:
    
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanIntrospector.java
    
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/ClassIntrospector.java
Modified:
    
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanTest.java

Added: 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanIntrospector.java
URL: 
http://svn.apache.org/viewvc/ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanIntrospector.java?rev=679245&view=auto
==============================================================================
--- 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanIntrospector.java
 (added)
+++ 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanIntrospector.java
 Wed Jul 23 17:29:36 2008
@@ -0,0 +1,478 @@
+/*
+ *  Copyright 2004 Clinton Begin
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.ibatis.jpetstore.domain;
+
+import java.util.*;
+import java.lang.reflect.Method;
+
+/**
+ * Abstract class used to help development of Probe implementations
+ */
+public class BeanIntrospector {
+
+  protected Object getIndexedProperty(Object object, String indexedName) {
+
+    Object value = null;
+
+    try {
+      String name = indexedName.substring(0, indexedName.indexOf('['));
+      int i = Integer.parseInt(indexedName.substring(indexedName.indexOf('[') 
+ 1, indexedName.indexOf(']')));
+      Object list = null;
+      if("".equals(name)) {
+        list = object;        
+      } else {
+        list = getProperty(object, name);
+      }
+
+      if (list instanceof List) {
+        value = ((List) list).get(i);
+      } else if (list instanceof Object[]) {
+        value = ((Object[]) list)[i];
+      } else if (list instanceof char[]) {
+        value = new Character(((char[]) list)[i]);
+      } else if (list instanceof boolean[]) {
+        value = new Boolean(((boolean[]) list)[i]);
+      } else if (list instanceof byte[]) {
+        value = new Byte(((byte[]) list)[i]);
+      } else if (list instanceof double[]) {
+        value = new Double(((double[]) list)[i]);
+      } else if (list instanceof float[]) {
+        value = new Float(((float[]) list)[i]);
+      } else if (list instanceof int[]) {
+        value = new Integer(((int[]) list)[i]);
+      } else if (list instanceof long[]) {
+        value = new Long(((long[]) list)[i]);
+      } else if (list instanceof short[]) {
+        value = new Short(((short[]) list)[i]);
+      } else {
+        throw new RuntimeException("The '" + name + "' property of the " + 
object.getClass().getName() + " class is not a List or Array.");
+      }
+
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new RuntimeException("Error getting ordinal list from JavaBean. 
Cause " + e, e);
+    }
+
+    return value;
+  }
+
+  protected Class getIndexedType(Object object, String indexedName) {
+
+    Class value = null;
+
+    try {
+      String name = indexedName.substring(0, indexedName.indexOf('['));
+      int i = Integer.parseInt(indexedName.substring(indexedName.indexOf('[') 
+ 1, indexedName.indexOf(']')));
+      Object list = null;
+      if(!"".equals(name)) {
+        list = getProperty(object, name);
+      } else {
+        list = object;
+      }
+
+      if (list instanceof List) {
+        value = ((List) list).get(i).getClass();
+      } else if (list instanceof Object[]) {
+        value = ((Object[]) list)[i].getClass();
+      } else if (list instanceof char[]) {
+        value = Character.class;
+      } else if (list instanceof boolean[]) {
+        value = Boolean.class;
+      } else if (list instanceof byte[]) {
+        value = Byte.class;
+      } else if (list instanceof double[]) {
+        value = Double.class;
+      } else if (list instanceof float[]) {
+        value = Float.class;
+      } else if (list instanceof int[]) {
+        value = Integer.class;
+      } else if (list instanceof long[]) {
+        value = Long.class;
+      } else if (list instanceof short[]) {
+        value = Short.class;
+      } else {
+        throw new RuntimeException("The '" + name + "' property of the " + 
object.getClass().getName() + " class is not a List or Array.");
+      }
+
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new RuntimeException("Error getting ordinal list from JavaBean. 
Cause " + e, e);
+    }
+
+    return value;
+  }
+
+  protected void setIndexedProperty(Object object, String indexedName, Object 
value) {
+
+    try {
+      String name = indexedName.substring(0, indexedName.indexOf('['));
+      int i = Integer.parseInt(indexedName.substring(indexedName.indexOf('[') 
+ 1, indexedName.indexOf(']')));
+      Object list = getProperty(object, name);
+      if (list instanceof List) {
+        ((List) list).set(i, value);
+      } else if (list instanceof Object[]) {
+        ((Object[]) list)[i] = value;
+      } else if (list instanceof char[]) {
+        ((char[]) list)[i] = ((Character) value).charValue();
+      } else if (list instanceof boolean[]) {
+        ((boolean[]) list)[i] = ((Boolean) value).booleanValue();
+      } else if (list instanceof byte[]) {
+        ((byte[]) list)[i] = ((Byte) value).byteValue();
+      } else if (list instanceof double[]) {
+        ((double[]) list)[i] = ((Double) value).doubleValue();
+      } else if (list instanceof float[]) {
+        ((float[]) list)[i] = ((Float) value).floatValue();
+      } else if (list instanceof int[]) {
+        ((int[]) list)[i] = ((Integer) value).intValue();
+      } else if (list instanceof long[]) {
+        ((long[]) list)[i] = ((Long) value).longValue();
+      } else if (list instanceof short[]) {
+        ((short[]) list)[i] = ((Short) value).shortValue();
+      } else {
+        throw new RuntimeException("The '" + name + "' property of the " + 
object.getClass().getName() + " class is not a List or Array.");
+      }
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new RuntimeException("Error getting ordinal value from JavaBean. 
Cause " + e, e);
+    }
+  }
+
+  private static final Object[] NO_ARGUMENTS = new Object[0];
+
+  /**
+   * Returns an array of the readable properties exposed by a bean
+   *
+   * @param object The bean
+   * @return The properties
+   */
+  public String[] getReadablePropertyNames(Object object) {
+    return 
ClassIntrospector.getInstance(object.getClass()).getReadablePropertyNames();
+  }
+
+  /**
+   * Returns an array of the writeable properties exposed by a bean
+   *
+   * @param object The bean
+   * @return The properties
+   */
+  public String[] getWriteablePropertyNames(Object object) {
+    return 
ClassIntrospector.getInstance(object.getClass()).getWriteablePropertyNames();
+  }
+
+  /**
+   * Returns the class that the setter expects to receive as a parameter when
+   * setting a property value.
+   *
+   * @param object The bean to check
+   * @param name   The name of the property
+   * @return The type of the property
+   */
+  public Class getPropertyTypeForSetter(Object object, String name) {
+    Class type = object.getClass();
+
+    if (object instanceof Class) {
+      type = getClassPropertyTypeForSetter((Class) object, name);
+    } else if (object instanceof Map) {
+      Map map = (Map) object;
+      Object value = map.get(name);
+      if (value == null) {
+        type = Object.class;
+      } else {
+        type = value.getClass();
+      }
+    } else {
+      if (name.indexOf('.') > -1) {
+        StringTokenizer parser = new StringTokenizer(name, ".");
+        while (parser.hasMoreTokens()) {
+          name = parser.nextToken();
+          type = ClassIntrospector.getInstance(type).getSetterType(name);
+        }
+      } else {
+        type = ClassIntrospector.getInstance(type).getSetterType(name);
+      }
+    }
+
+    return type;
+  }
+
+  /**
+   * Returns the class that the getter will return when reading a property 
value.
+   *
+   * @param object The bean to check
+   * @param name   The name of the property
+   * @return The type of the property
+   */
+  public Class getPropertyTypeForGetter(Object object, String name) {
+    Class type = object.getClass();
+
+    if (object instanceof Class) {
+      type = getClassPropertyTypeForGetter((Class) object, name);
+    } else if (object instanceof Map) {
+      Map map = (Map) object;
+      Object value = map.get(name);
+      if (value == null) {
+        type = Object.class;
+      } else {
+        type = value.getClass();
+      }
+    } else {
+      if (name.indexOf('.') > -1) {
+        StringTokenizer parser = new StringTokenizer(name, ".");
+        while (parser.hasMoreTokens()) {
+          name = parser.nextToken();
+          type = ClassIntrospector.getInstance(type).getGetterType(name);
+        }
+      } else {
+        type = ClassIntrospector.getInstance(type).getGetterType(name);
+      }
+    }
+
+    return type;
+  }
+
+  /**
+   * Returns the class that the getter will return when reading a property 
value.
+   *
+   * @param type The class to check
+   * @param name The name of the property
+   * @return The type of the property
+   */
+  private Class getClassPropertyTypeForGetter(Class type, String name) {
+
+    if (name.indexOf('.') > -1) {
+      StringTokenizer parser = new StringTokenizer(name, ".");
+      while (parser.hasMoreTokens()) {
+        name = parser.nextToken();
+        type = ClassIntrospector.getInstance(type).getGetterType(name);
+      }
+    } else {
+      type = ClassIntrospector.getInstance(type).getGetterType(name);
+    }
+
+    return type;
+  }
+
+  /**
+   * Returns the class that the setter expects to receive as a parameter when
+   * setting a property value.
+   *
+   * @param type The class to check
+   * @param name The name of the property
+   * @return The type of the property
+   */
+  private Class getClassPropertyTypeForSetter(Class type, String name) {
+
+    if (name.indexOf('.') > -1) {
+      StringTokenizer parser = new StringTokenizer(name, ".");
+      while (parser.hasMoreTokens()) {
+        name = parser.nextToken();
+        type = ClassIntrospector.getInstance(type).getSetterType(name);
+      }
+    } else {
+      type = ClassIntrospector.getInstance(type).getSetterType(name);
+    }
+
+    return type;
+  }
+
+  /**
+   * Gets an Object property from a bean
+   *
+   * @param object The bean
+   * @param name   The property name
+   * @return The property value (as an Object)
+   */
+  public Object getObject(Object object, String name) {
+    if (name.indexOf('.') > -1) {
+      StringTokenizer parser = new StringTokenizer(name, ".");
+      Object value = object;
+      while (parser.hasMoreTokens()) {
+        value = getProperty(value, parser.nextToken());
+
+        if (value == null) {
+          break;
+        }
+
+      }
+      return value;
+    } else {
+      return getProperty(object, name);
+    }
+  }
+
+  /**
+   * Sets the value of a bean property to an Object
+   *
+   * @param object The bean to change
+   * @param name   The name of the property to set
+   * @param value  The new value to set
+   */
+  public void setObject(Object object, String name, Object value) {
+    if (name.indexOf('.') > -1) {
+      StringTokenizer parser = new StringTokenizer(name, ".");
+      String property = parser.nextToken();
+      Object child = object;
+      while (parser.hasMoreTokens()) {
+        Class type = getPropertyTypeForSetter(child, property);
+        Object parent = child;
+        child = getProperty(parent, property);
+        if (child == null) {
+          if (value == null) {
+            return; // don't instantiate child path if value is null
+          } else {
+            try {
+              child = type.newInstance();
+              setObject(parent, property, child);
+            } catch (Exception e) {
+              throw new RuntimeException("Cannot set value of property '" + 
name + "' because '" + property + "' is null and cannot be instantiated on 
instance of " + type.getName() + ". Cause:" + e.toString(), e);
+            }
+          }
+        }
+        property = parser.nextToken();
+      }
+      setProperty(child, property, value);
+    } else {
+      setProperty(object, name, value);
+    }
+  }
+
+
+  /**
+   * Checks to see if a bean has a writable property be a given name
+   *
+   * @param object       The bean to check
+   * @param propertyName The property to check for
+   * @return True if the property exists and is writable
+   */
+  public boolean hasWritableProperty(Object object, String propertyName) {
+    boolean hasProperty = false;
+    if (object instanceof Map) {
+      hasProperty = true;//((Map) object).containsKey(propertyName);
+    } else {
+      if (propertyName.indexOf('.') > -1) {
+        StringTokenizer parser = new StringTokenizer(propertyName, ".");
+        Class type = object.getClass();
+        while (parser.hasMoreTokens()) {
+          propertyName = parser.nextToken();
+          type = 
ClassIntrospector.getInstance(type).getGetterType(propertyName);
+          hasProperty = 
ClassIntrospector.getInstance(type).hasWritableProperty(propertyName);
+        }
+      } else {
+        hasProperty = 
ClassIntrospector.getInstance(object.getClass()).hasWritableProperty(propertyName);
+      }
+    }
+    return hasProperty;
+  }
+
+  /**
+   * Checks to see if a bean has a readable property be a given name
+   *
+   * @param object       The bean to check
+   * @param propertyName The property to check for
+   * @return True if the property exists and is readable
+   */
+  public boolean hasReadableProperty(Object object, String propertyName) {
+    boolean hasProperty = false;
+    if (object instanceof Map) {
+      hasProperty = true;//((Map) object).containsKey(propertyName);
+    } else {
+      if (propertyName.indexOf('.') > -1) {
+        StringTokenizer parser = new StringTokenizer(propertyName, ".");
+        Class type = object.getClass();
+        while (parser.hasMoreTokens()) {
+          propertyName = parser.nextToken();
+          type = 
ClassIntrospector.getInstance(type).getGetterType(propertyName);
+          hasProperty = 
ClassIntrospector.getInstance(type).hasReadableProperty(propertyName);
+        }
+      } else {
+        hasProperty = 
ClassIntrospector.getInstance(object.getClass()).hasReadableProperty(propertyName);
+      }
+    }
+    return hasProperty;
+  }
+
+  protected Object getProperty(Object object, String name) {
+    ClassIntrospector classCache = 
ClassIntrospector.getInstance(object.getClass());
+    try {
+      Object value = null;
+      if (name.indexOf('[') > -1) {
+        value = getIndexedProperty(object, name);
+      } else {
+        if (object instanceof Map) {
+          value = ((Map) object).get(name);
+        } else {
+          Method method = classCache.getGetter(name);
+          if (method == null) {
+            throw new NoSuchMethodException("No GET method for property " + 
name + " on instance of " + object.getClass().getName());
+          }
+          try {
+            value = method.invoke(object, NO_ARGUMENTS);
+          } catch (Throwable t) {
+            throw ClassIntrospector.unwrapThrowable(t);
+          }
+        }
+      }
+      return value;
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Throwable t) {
+      if (object == null) {
+        throw new RuntimeException("Could not get property '" + name + "' from 
null reference.  Cause: " + t.toString(), t);
+      } else {
+        throw new RuntimeException("Could not get property '" + name + "' from 
" + object.getClass().getName() + ".  Cause: " + t.toString(), t);
+      }
+    }
+  }
+
+  protected void setProperty(Object object, String name, Object value) {
+    ClassIntrospector classCache = 
ClassIntrospector.getInstance(object.getClass());
+    try {
+      if (name.indexOf('[') > -1) {
+        setIndexedProperty(object, name, value);
+      } else {
+        if (object instanceof Map) {
+          ((Map) object).put(name, value);
+        } else {
+          Method method = classCache.getSetter(name);
+          if (method == null) {
+            throw new NoSuchMethodException("No SET method for property " + 
name + " on instance of " + object.getClass().getName());
+          }
+          Object[] params = new Object[1];
+          params[0] = value;
+          try {
+            method.invoke(object, params);
+          } catch (Throwable t) {
+            throw ClassIntrospector.unwrapThrowable(t);
+          }
+        }
+      }
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Throwable t) {
+      if (object == null) {
+        throw new RuntimeException("Could not set property '" + name + "' for 
null reference.  Cause: " + t.toString(), t);
+      } else {
+        throw new RuntimeException("Could not set property '" + name + "' for 
" + object.getClass().getName() + ".  Cause: " + t.toString(), t);
+      }
+    }
+  }
+
+
+
+}

Modified: 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanTest.java
URL: 
http://svn.apache.org/viewvc/ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanTest.java?rev=679245&r1=679244&r2=679245&view=diff
==============================================================================
--- 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanTest.java
 (original)
+++ 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/BeanTest.java
 Wed Jul 23 17:29:36 2008
@@ -1,8 +1,5 @@
 package com.ibatis.jpetstore.domain;
 
-import com.ibatis.common.beans.ClassInfo;
-import com.ibatis.common.beans.Probe;
-import com.ibatis.common.beans.ProbeFactory;
 import com.ibatis.jpetstore.presentation.AccountBean;
 import com.ibatis.jpetstore.presentation.CartBean;
 import com.ibatis.jpetstore.presentation.CatalogBean;
@@ -35,15 +32,15 @@
     try {
       for (int i=0; i < classes.length; i++) {
         Object object = classes[i].newInstance();
-        ClassInfo info = ClassInfo.getInstance(classes[i]);
-        List writeables = Arrays.asList(info.getWriteablePropertyNames());
-        List readables = Arrays.asList(info.getReadablePropertyNames());
+        ClassIntrospector introspector = 
ClassIntrospector.getInstance(classes[i]);
+        List writeables = 
Arrays.asList(introspector.getWriteablePropertyNames());
+        List readables = 
Arrays.asList(introspector.getReadablePropertyNames());
         for (int j=0; j < writeables.size(); j++) {
           String writeable = (String)writeables.get(j);
           if (readables.contains(writeable)) {
-            Class type = info.getGetterType(writeable);
+            Class type = introspector.getGetterType(writeable);
             Object sample = getSampleFor(type);
-            Probe probe = ProbeFactory.getProbe(object);
+            BeanIntrospector probe = new BeanIntrospector();
             probe.setObject(object, writeable, sample);
             assertEquals(sample,probe.getObject(object, writeable));
           }

Added: 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/ClassIntrospector.java
URL: 
http://svn.apache.org/viewvc/ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/ClassIntrospector.java?rev=679245&view=auto
==============================================================================
--- 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/ClassIntrospector.java
 (added)
+++ 
ibatis/trunk/java/jpetstore/jpetstore5/test/com/ibatis/jpetstore/domain/ClassIntrospector.java
 Wed Jul 23 17:29:36 2008
@@ -0,0 +1,396 @@
+/*
+ *  Copyright 2004 Clinton Begin
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.ibatis.jpetstore.domain;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.lang.reflect.ReflectPermission;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * This class represents a cached set of class definition information that
+ * allows for easy mapping between property names and getter/setter methods.
+ */
+public class ClassIntrospector {
+  
+  private static boolean cacheEnabled = true;
+  private static final String[] EMPTY_STRING_ARRAY = new String[0];
+  private static final Set SIMPLE_TYPE_SET = new HashSet();
+  private static final Map CLASS_INFO_MAP = Collections.synchronizedMap(new 
HashMap());
+
+  private String className;
+  private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
+  private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
+  private HashMap setMethods = new HashMap();
+  private HashMap getMethods = new HashMap();
+  private HashMap setTypes = new HashMap();
+  private HashMap getTypes = new HashMap();
+
+  static {
+    SIMPLE_TYPE_SET.add(String.class);
+    SIMPLE_TYPE_SET.add(Byte.class);
+    SIMPLE_TYPE_SET.add(Short.class);
+    SIMPLE_TYPE_SET.add(Character.class);
+    SIMPLE_TYPE_SET.add(Integer.class);
+    SIMPLE_TYPE_SET.add(Long.class);
+    SIMPLE_TYPE_SET.add(Float.class);
+    SIMPLE_TYPE_SET.add(Double.class);
+    SIMPLE_TYPE_SET.add(Boolean.class);
+    SIMPLE_TYPE_SET.add(Date.class);
+    SIMPLE_TYPE_SET.add(Class.class);
+    SIMPLE_TYPE_SET.add(BigInteger.class);
+    SIMPLE_TYPE_SET.add(BigDecimal.class);
+
+    SIMPLE_TYPE_SET.add(Collection.class);
+    SIMPLE_TYPE_SET.add(Set.class);
+    SIMPLE_TYPE_SET.add(Map.class);
+    SIMPLE_TYPE_SET.add(List.class);
+    SIMPLE_TYPE_SET.add(HashMap.class);
+    SIMPLE_TYPE_SET.add(TreeMap.class);
+    SIMPLE_TYPE_SET.add(ArrayList.class);
+    SIMPLE_TYPE_SET.add(LinkedList.class);
+    SIMPLE_TYPE_SET.add(HashSet.class);
+    SIMPLE_TYPE_SET.add(TreeSet.class);
+    SIMPLE_TYPE_SET.add(Vector.class);
+    SIMPLE_TYPE_SET.add(Hashtable.class);
+    SIMPLE_TYPE_SET.add(Enumeration.class);
+  }
+
+  private ClassIntrospector(Class clazz) {
+    className = clazz.getName();
+    addMethods(clazz);
+    readablePropertyNames = (String[]) getMethods.keySet().toArray(new 
String[getMethods.keySet().size()]);
+    writeablePropertyNames = (String[]) setMethods.keySet().toArray(new 
String[setMethods.keySet().size()]);
+  }
+
+  private void addMethods(Class cls) {
+    Method[] methods = getAllMethodsForClass(cls);
+    for (int i = 0; i < methods.length; i++) {
+      String name = methods[i].getName();
+      if (name.startsWith("set") && name.length() > 3) {
+        if (methods[i].getParameterTypes().length == 1) {
+          name = dropCase(name);
+          if (setMethods.containsKey(name)) {
+            throw new RuntimeException ("Illegal overloaded setter method for 
property " + name + " in class " + cls.getName() +
+                ".  This breaks the JavaBeans specification and can cause 
unpredicatble results.");
+          }
+          setMethods.put(name, methods[i]);
+          setTypes.put(name, methods[i].getParameterTypes()[0]);
+        }
+      } else if (name.startsWith("get") && name.length() > 3) {
+        if (methods[i].getParameterTypes().length == 0) {
+          name = dropCase(name);
+          getMethods.put(name, methods[i]);
+          getTypes.put(name, methods[i].getReturnType());
+        }
+      } else if (name.startsWith("is") && name.length() > 2) {
+        if (methods[i].getParameterTypes().length == 0) {
+          name = dropCase(name);
+          getMethods.put(name, methods[i]);
+          getTypes.put(name, methods[i].getReturnType());
+        }
+      }
+      name = null;
+    }
+  }
+
+  private Method[] getAllMethodsForClass(Class cls) {
+    if (cls.isInterface()) {
+      // interfaces only have public methods - so the
+      // simple call is all we need (this will also get superinterface methods)
+      return cls.getMethods();
+    } else {
+      // need to get all the declared methods in this class
+      // and any super-class - then need to set access appropriatly
+      // for private methods
+      return getClassMethods(cls);
+    }
+  }
+  
+  /**
+   * This method returns an array containing all methods
+   * declared in this class and any superclass.
+   * We use this method, instead of the simpler Class.getMethods(),
+   * because we want to look for private methods as well. 
+   * 
+   * @param cls
+   * @return
+   */
+  private Method[] getClassMethods(Class cls) {
+    HashMap uniqueMethods = new HashMap();
+    Class currentClass = cls;
+    while (currentClass != null) {
+      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
+      
+      // we also need to look for interface methods - 
+      // because the class may be abstract
+      Class[] interfaces = currentClass.getInterfaces();
+      for (int i = 0; i < interfaces.length; i++) {
+        addUniqueMethods(uniqueMethods, interfaces[i].getMethods());
+      }
+      
+      currentClass = currentClass.getSuperclass();
+    }
+    
+    Collection methods = uniqueMethods.values();
+    
+    return (Method[]) methods.toArray(new Method[methods.size()]);
+  }
+
+  private void addUniqueMethods(HashMap uniqueMethods, Method[] methods) {
+    for (int i = 0; i < methods.length; i++) {
+      Method currentMethod = methods[i];
+      String signature = getSignature(currentMethod);
+      // check to see if the method is already known
+      // if it is known, then an extended class must have
+      // overridden a method
+      if (!uniqueMethods.containsKey(signature)) {
+        if (canAccessPrivateMethods()) {
+          try {
+            currentMethod.setAccessible(true);
+          } catch (Exception e) {
+            // Ignored. This is only a final precaution, nothing we can do.
+          }
+        }
+        
+        uniqueMethods.put(signature, currentMethod);
+      }
+    }
+  }
+  
+  private String getSignature(Method method) {
+    StringBuffer sb = new StringBuffer();
+    sb.append(method.getName());
+    Class[] parameters = method.getParameterTypes();
+    
+    for (int i = 0; i < parameters.length; i++) {
+      if (i == 0) {
+        sb.append(':');
+      } else {
+        sb.append(',');
+      }
+      sb.append(parameters[i].getName());
+    }
+    
+    return sb.toString();
+  }
+
+  private boolean canAccessPrivateMethods() {
+    try {
+      System.getSecurityManager().checkPermission(new 
ReflectPermission("suppressAccessChecks"));
+      return true;
+    } catch (SecurityException e) {
+      return false;
+    } catch (NullPointerException e) {
+      return true;
+    }
+  }
+
+  private static String dropCase(String name) {
+    if (name.startsWith("is")) {
+      name = name.substring(2);
+    } else if (name.startsWith("get") || name.startsWith("set")) {
+      name = name.substring(3);
+    } else {
+      throw new RuntimeException("Error parsing property name '" + name + "'.  
Didn't start with 'is', 'get' or 'set'.");
+    }
+
+    if (name.length() == 1 || (name.length() > 1 && 
!Character.isUpperCase(name.charAt(1)))) {
+      name = name.substring(0, 1).toLowerCase(Locale.US) + name.substring(1);
+    }
+
+    return name;
+  }
+
+  /**
+   * Gets the name of the class the instance provides information for
+   *
+   * @return The class name
+   */
+  public String getClassName() {
+    return className;
+  }
+
+  /**
+   * Gets the setter for a property as a Method object
+   *
+   * @param propertyName - the property
+   * @return The Method
+   */
+  public Method getSetter(String propertyName) {
+    Method method = (Method) setMethods.get(propertyName);
+    if (method == null) {
+      throw new RuntimeException("There is no WRITEABLE property named '" + 
propertyName + "' in class '" + className + "'");
+    }
+    return method;
+  }
+
+  /**
+   * Gets the getter for a property as a Method object
+   *
+   * @param propertyName - the property
+   * @return The Method
+   */
+  public Method getGetter(String propertyName) {
+    Method method = (Method) getMethods.get(propertyName);
+    if (method == null) {
+      throw new RuntimeException("There is no READABLE property named '" + 
propertyName + "' in class '" + className + "'");
+    }
+    return method;
+  }
+
+  /**
+   * Gets the type for a property setter
+   *
+   * @param propertyName - the name of the property
+   * @return The Class of the propery setter
+   */
+  public Class getSetterType(String propertyName) {
+    Class clazz = (Class) setTypes.get(propertyName);
+    if (clazz == null) {
+      throw new RuntimeException("There is no WRITEABLE property named '" + 
propertyName + "' in class '" + className + "'");
+    }
+    return clazz;
+  }
+
+  /**
+   * Gets the type for a property getter
+   *
+   * @param propertyName - the name of the property
+   * @return The Class of the propery getter
+   */
+  public Class getGetterType(String propertyName) {
+    Class clazz = (Class) getTypes.get(propertyName);
+    if (clazz == null) {
+      throw new RuntimeException("There is no READABLE property named '" + 
propertyName + "' in class '" + className + "'");
+    }
+    return clazz;
+  }
+
+  /**
+   * Gets an array of the readable properties for an object
+   *
+   * @return The array
+   */
+  public String[] getReadablePropertyNames() {
+    return readablePropertyNames;
+  }
+
+  /**
+   * Gets an array of the writeable properties for an object
+   *
+   * @return The array
+   */
+  public String[] getWriteablePropertyNames() {
+    return writeablePropertyNames;
+  }
+
+  /**
+   * Check to see if a class has a writeable property by name
+   *
+   * @param propertyName - the name of the property to check
+   * @return True if the object has a writeable property by the name
+   */
+  public boolean hasWritableProperty(String propertyName) {
+    return setMethods.keySet().contains(propertyName);
+  }
+
+  /**
+   * Check to see if a class has a readable property by name
+   *
+   * @param propertyName - the name of the property to check
+   * @return True if the object has a readable property by the name
+   */
+  public boolean hasReadableProperty(String propertyName) {
+    return getMethods.keySet().contains(propertyName);
+  }
+
+  /**
+   * Tells us if the class passed in is a knwon common type
+   *
+   * @param clazz The class to check
+   * @return True if the class is known
+   */
+  public static boolean isKnownType(Class clazz) {
+    if (SIMPLE_TYPE_SET.contains(clazz)) {
+      return true;
+    } else if (Collection.class.isAssignableFrom(clazz)) {
+      return true;
+    } else if (Map.class.isAssignableFrom(clazz)) {
+      return true;
+    } else if (List.class.isAssignableFrom(clazz)) {
+      return true;
+    } else if (Set.class.isAssignableFrom(clazz)) {
+      return true;
+    } else if (Iterator.class.isAssignableFrom(clazz)) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * Gets an instance of ClassInfo for the specified class.
+   *
+   * @param clazz The class for which to lookup the method cache.
+   * @return The method cache for the class
+   */
+  public static ClassIntrospector getInstance(Class clazz) {
+    if (cacheEnabled) {
+      synchronized (clazz) {
+        ClassIntrospector cache = (ClassIntrospector) 
CLASS_INFO_MAP.get(clazz);
+        if (cache == null) {
+          cache = new ClassIntrospector(clazz);
+          CLASS_INFO_MAP.put(clazz, cache);
+        }
+        return cache;
+      }
+    } else {
+      return new ClassIntrospector(clazz);
+    }
+  }
+
+  public static void setCacheEnabled(boolean cacheEnabled) {
+    ClassIntrospector.cacheEnabled = cacheEnabled;
+  }
+
+  /**
+   * Examines a Throwable object and gets it's root cause
+   *
+   * @param t - the exception to examine
+   * @return The root cause
+   */
+  public static Throwable unwrapThrowable(Throwable t) {
+    Throwable t2 = t;
+    while (true) {
+      if (t2 instanceof InvocationTargetException) {
+        t2 = ((InvocationTargetException) t).getTargetException();
+      } else if (t instanceof UndeclaredThrowableException) {
+        t2 = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
+      } else {
+        return t2;
+      }
+    }
+  }
+
+
+}
+
+
+


Reply via email to