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;
+ }
+ }
+ }
+
+
+}
+
+
+