001: /*
002: * Copyright (C) 2005 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 12. April 2005 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.converters.javabean;
013:
014: import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
015: import com.thoughtworks.xstream.core.util.OrderRetainingMap;
016:
017: import java.beans.BeanInfo;
018: import java.beans.IntrospectionException;
019: import java.beans.Introspector;
020: import java.beans.PropertyDescriptor;
021: import java.lang.reflect.Constructor;
022: import java.lang.reflect.InvocationTargetException;
023: import java.lang.reflect.Method;
024: import java.lang.reflect.Modifier;
025: import java.util.ArrayList;
026: import java.util.Comparator;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031: import java.util.TreeSet;
032: import java.util.WeakHashMap;
033:
034: public class BeanProvider {
035:
036: protected static final Object[] NO_PARAMS = new Object[0];
037: private final Comparator propertyNameComparator;
038: private final transient Map propertyNameCache = new WeakHashMap();
039:
040: public BeanProvider() {
041: this (null);
042: }
043:
044: public BeanProvider(final Comparator propertyNameComparator) {
045: this .propertyNameComparator = propertyNameComparator;
046: }
047:
048: public Object newInstance(Class type) {
049: try {
050: return getDefaultConstrutor(type).newInstance(NO_PARAMS);
051: } catch (InstantiationException e) {
052: throw new ObjectAccessException("Cannot construct "
053: + type.getName(), e);
054: } catch (IllegalAccessException e) {
055: throw new ObjectAccessException("Cannot construct "
056: + type.getName(), e);
057: } catch (InvocationTargetException e) {
058: if (e.getTargetException() instanceof RuntimeException) {
059: throw (RuntimeException) e.getTargetException();
060: } else if (e.getTargetException() instanceof Error) {
061: throw (Error) e.getTargetException();
062: } else {
063: throw new ObjectAccessException("Constructor for "
064: + type.getName() + " threw an exception", e);
065: }
066: }
067: }
068:
069: public void visitSerializableProperties(Object object,
070: Visitor visitor) {
071: PropertyDescriptor[] propertyDescriptors = getSerializableProperties(object);
072: for (int i = 0; i < propertyDescriptors.length; i++) {
073: PropertyDescriptor property = propertyDescriptors[i];
074: try {
075: Method readMethod = property.getReadMethod();
076: Object value = readMethod.invoke(object, new Object[0]);
077: visitor.visit(property.getName(), property
078: .getPropertyType(), readMethod
079: .getDeclaringClass(), value);
080: } catch (IllegalArgumentException e) {
081: throw new ObjectAccessException(
082: "Could not get property " + object.getClass()
083: + "." + property.getName(), e);
084: } catch (IllegalAccessException e) {
085: throw new ObjectAccessException(
086: "Could not get property " + object.getClass()
087: + "." + property.getName(), e);
088: } catch (InvocationTargetException e) {
089: throw new ObjectAccessException(
090: "Could not get property " + object.getClass()
091: + "." + property.getName(), e);
092: }
093: }
094: }
095:
096: public void writeProperty(Object object, String propertyName,
097: Object value) {
098: PropertyDescriptor property = getProperty(propertyName, object
099: .getClass());
100: try {
101: property.getWriteMethod().invoke(object,
102: new Object[] { value });
103: } catch (IllegalArgumentException e) {
104: throw new ObjectAccessException("Could not set property "
105: + object.getClass() + "." + property.getName(), e);
106: } catch (IllegalAccessException e) {
107: throw new ObjectAccessException("Could not set property "
108: + object.getClass() + "." + property.getName(), e);
109: } catch (InvocationTargetException e) {
110: throw new ObjectAccessException("Could not set property "
111: + object.getClass() + "." + property.getName(), e);
112: }
113: }
114:
115: public Class getPropertyType(Object object, String name) {
116: return getProperty(name, object.getClass()).getPropertyType();
117: }
118:
119: public boolean propertyDefinedInClass(String name, Class type) {
120: return getProperty(name, type) != null;
121: }
122:
123: /**
124: * Returns true if the Bean provider can instantiate the specified class
125: */
126: public boolean canInstantiate(Class type) {
127: return getDefaultConstrutor(type) != null;
128: }
129:
130: /**
131: * Returns the default constructor, or null if none is found
132: *
133: * @param type
134: */
135: protected Constructor getDefaultConstrutor(Class type) {
136: Constructor[] constructors = type.getConstructors();
137: for (int i = 0; i < constructors.length; i++) {
138: Constructor c = constructors[i];
139: if (c.getParameterTypes().length == 0
140: && Modifier.isPublic(c.getModifiers()))
141: return c;
142: }
143: return null;
144: }
145:
146: private PropertyDescriptor[] getSerializableProperties(Object object) {
147: Map nameMap = getNameMap(object.getClass());
148: List result = new ArrayList(nameMap.size());
149: Set names = nameMap.keySet();
150: if (propertyNameComparator != null) {
151: Set sortedSet = new TreeSet(propertyNameComparator);
152: sortedSet.addAll(names);
153: names = sortedSet;
154: }
155: for (final Iterator iter = names.iterator(); iter.hasNext();) {
156: final PropertyDescriptor descriptor = (PropertyDescriptor) nameMap
157: .get(iter.next());
158: if (canStreamProperty(descriptor)) {
159: result.add(descriptor);
160: }
161: }
162: return (PropertyDescriptor[]) result
163: .toArray(new PropertyDescriptor[result.size()]);
164: }
165:
166: protected boolean canStreamProperty(PropertyDescriptor descriptor) {
167: return descriptor.getReadMethod() != null
168: && descriptor.getWriteMethod() != null;
169: }
170:
171: public boolean propertyWriteable(String name, Class type) {
172: PropertyDescriptor property = getProperty(name, type);
173: return property.getWriteMethod() != null;
174: }
175:
176: private PropertyDescriptor getProperty(String name, Class type) {
177: return (PropertyDescriptor) getNameMap(type).get(name);
178: }
179:
180: private Map getNameMap(Class type) {
181: Map nameMap = (Map) propertyNameCache.get(type);
182: if (nameMap == null) {
183: BeanInfo beanInfo;
184: try {
185: beanInfo = Introspector.getBeanInfo(type, Object.class);
186: } catch (IntrospectionException e) {
187: throw new ObjectAccessException("", e);
188: }
189: nameMap = new OrderRetainingMap();
190: propertyNameCache.put(type, nameMap);
191: PropertyDescriptor[] propertyDescriptors = beanInfo
192: .getPropertyDescriptors();
193: for (int i = 0; i < propertyDescriptors.length; i++) {
194: PropertyDescriptor descriptor = propertyDescriptors[i];
195: nameMap.put(descriptor.getName(), descriptor);
196: }
197: }
198: return nameMap;
199: }
200:
201: interface Visitor {
202: void visit(String name, Class type, Class definedIn,
203: Object value);
204: }
205: }
|