[Practicalxml-commits] SF.net SVN: practicalxml:[164] branches/dev-1.1/src
Brought to you by:
kdgregory
|
From: Auto-Generated S. C. M. <pra...@li...> - 2009-10-13 18:35:34
|
Revision: 164
http://practicalxml.svn.sourceforge.net/practicalxml/?rev=164&view=rev
Author: kdgregory
Date: 2009-10-13 18:35:12 +0000 (Tue, 13 Oct 2009)
Log Message:
-----------
replace java.beans.Introspector with homegrown alternative
Modified Paths:
--------------
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/package.html
Added Paths:
-----------
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Introspection.java
branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/IntrospectionCache.java
branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospection.java
branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospectionCache.java
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/BeanConverter.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -33,12 +33,6 @@
* {@link net.sf.practicalxml.converter.bean.Bean2XmlConverter} and
* {@link net.sf.practicalxml.converter.bean.Xml2BeanConverter}. If static
* methods and throwaway objects offend you then use those classes directly.
- * <p>
- * <strong>Warning</strong>:
- * Bean-to-XML conversion uses <code>java.beans.Introspector</code>, which
- * holds a cache of introspected objects. If you use this conversion in an
- * app-server you should call <code>Introspector.flushCaches()</code> during
- * deploy.
*/
public class BeanConverter
{
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlConverter.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -14,12 +14,7 @@
package net.sf.practicalxml.converter.bean;
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
-import java.lang.reflect.Method;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
@@ -42,14 +37,17 @@
*/
public class Bean2XmlConverter
{
+ private EnumSet<Bean2XmlOptions> _options;
private PrimitiveConversionHelper _helper;
- private EnumSet<Bean2XmlOptions> _options = EnumSet.noneOf(Bean2XmlOptions.class);
+ private IntrospectionCache _introspections;
public Bean2XmlConverter(Bean2XmlOptions... options)
{
+ _options = EnumSet.noneOf(Bean2XmlOptions.class);
for (Bean2XmlOptions option : options)
_options.add(option);
_helper = new PrimitiveConversionHelper(shouldUseXsdFormatting());
+ _introspections = new IntrospectionCache(_options.contains(Bean2XmlOptions.INTROSPECTION_CACHE));
}
@@ -225,33 +223,20 @@
{
Element parent = appender.appendContainer(name, DomUtilToo.getXsiTypeForJavaObject(bean));
Appender childAppender = new BasicAppender(parent, _options);
- try
- {
- BeanInfo info = Introspector.getBeanInfo(bean.getClass(), Object.class);
- PropertyDescriptor[] props = info.getPropertyDescriptors();
- for (int ii = 0 ; ii < props.length ; ii++)
- convertBeanProperty(bean, props[ii], childAppender);
- }
- catch (IntrospectionException ee)
- {
- throw new ConversionException("introspection failure", ee);
- }
+ Introspection ispec = _introspections.lookup(bean.getClass());
+ for (String propName : ispec.propertyNames())
+ convertBeanProperty(bean, ispec, propName, childAppender);
return true;
}
private void convertBeanProperty(
- Object bean, PropertyDescriptor propDesc, Appender appender)
+ Object bean, Introspection ispec, String propName, Appender appender)
{
- String name = propDesc.getName();
- Class<?> type = propDesc.getPropertyType();
Object value;
try
{
- Method getter = propDesc.getReadMethod();
- if (getter == null)
- return;
- value = getter.invoke(bean);
+ value = ispec.getter(propName).invoke(bean);
}
catch (Exception ee)
{
@@ -259,9 +244,9 @@
}
if (value == null)
- tryToConvertAsPrimitiveOrNull(null, type, name, appender);
+ tryToConvertAsPrimitiveOrNull(null, ispec.type(propName), propName, appender);
else
- convert(value, name, appender);
+ convert(value, propName, appender);
}
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Bean2XmlOptions.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -88,5 +88,14 @@
* <p>
* <em>This option implies {@link #XSD_FORMAT} for simple types</em>.
*/
- XSI_TYPE
+ XSI_TYPE,
+
+ /**
+ * Will use a shared static introspection cache for all conversions.
+ * <p>
+ * <strong>Warning</strong>: if you use this option, do not store this
+ * library in a shared app-server classpath. If you do, the cache will
+ * prevent class unloading, and you will run out of permgen space.
+ */
+ INTROSPECTION_CACHE
}
Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Introspection.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Introspection.java (rev 0)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Introspection.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -0,0 +1,251 @@
+// Copyright 2008-2009 severally by the contributors
+//
+// 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 net.sf.practicalxml.converter.bean;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.practicalxml.converter.ConversionException;
+
+
+/**
+ * A replacement for <code>java.beans.Introspector</code> that is tailored to
+ * the operation of <code>BeanConverter</code>:
+ * <dl>
+ * <dt>Identifies multiple accessor methods
+ * <dd><code>javax.beans.Introspector</code> looks at parameter type, and
+ * attempts to match getters and setters. This method looks only at the
+ * method name, and will return any method whose name starts with "get"
+ * or "is".
+ * <dt>Ignores case in name comparison
+ * <dd><code>javax.beans.Introspector</code> has camel-casing rules that do
+ * not always correspond to programmer intent -- in particular, two
+ * initial capitals are not camelcased.
+ * <dt>Resolves of multiple setter methods for same property
+ * <dd>In the case where there are multiple getters/setters with the same
+ * name, the following ranking is applied:
+ * <ol>
+ * <li> Methods defined by subclass, over those defined by superclass.
+ * The subclass is assumed to be more specific.
+ * <li> Methods that get/take primitive values.
+ * <li> Methods that get/take primitive wrappers.
+ * <li> Methods that get/take <code>String</code>. Driven by the use of
+ * this introspector to translate to/from a text format.
+ * <li> Methods that get/take arbitrary objects.
+ * </ol>
+ * <dt>Resolves multiple getter methods for same property
+ * <dd>For class hierarchies that define covariant return types, will use
+ * the most specific type.
+ * <dt>Ignores properties defined by <code>Object</code>
+ * <dd><code>javax.beans.Introspector</code> allows specific control over the
+ * parts of the class hierarchy to be introspected; by default, it will
+ * include methods defined by <code>Object</code>. We don't care about
+ * those, but assume any other class in the hierarchy to be important.
+ * </dl>
+ * <p>
+ * Also unlike <code>javax.beans.Introspector</code>, instances of this
+ * class are constructed around a target class (thus the different name).
+ * Use {@link IntrospectionCache} for caching and lookup services.
+ * <p>
+ * Instances of this class are read-only (and threadsafe) once constructed.
+ */
+public class Introspection
+{
+ private Set<String> _propNames;
+ private Set<String> _propNamesPublic;
+ private Map<String,Method> _getters;
+ private Map<String,Method> _setters;
+
+
+ /**
+ * Introspects the specified class, per the rules above.
+ *
+ * @throws ConversionException on any error.
+ */
+ public Introspection(Class<?> klass)
+ {
+ _propNames = new HashSet<String>();
+ _propNamesPublic = Collections.unmodifiableSet(_propNames);
+ _getters = new HashMap<String,Method>();
+ _setters = new HashMap<String,Method>();
+
+ introspect(klass);
+ }
+
+//----------------------------------------------------------------------------
+// Public Methods
+//----------------------------------------------------------------------------
+
+ /**
+ * Returns the property names for the specified class. These names are
+ * generated from getter methods -- any method beginning with "get"
+ * or "is". The returned set is unmodifiable, and will be empty if
+ * there are no properties with bean-style getter methods.
+ * <p>
+ * Names are processed by <code>Introspector.decapitalize()</code>, so
+ * will be consistent with the bean specification.
+ */
+ public Set<String> propertyNames()
+ {
+ return _propNamesPublic;
+ }
+
+
+ /**
+ * Returns the getter method for the named property, <code>null</code>
+ * no method is known (all properties returned by {@link #propertyNames}
+ * must have getters, but may not have setters).
+ */
+ public Method getter(String propName)
+ {
+ return _getters.get(propName.toLowerCase());
+ }
+
+
+ /**
+ * Returns the setter method for the named property, <code>null</code>
+ * if unable to find a method.
+ */
+ public Method setter(String propName)
+ {
+ return _setters.get(propName.toLowerCase());
+ }
+
+
+ /**
+ * Returns the type of the named property, taken from the return type
+ * of the property's getter. Will be <code>null</code> if the property
+ * isn't known.
+ */
+ public Class<?> type(String propName)
+ {
+ Method getter = getter(propName);
+ return (getter == null)
+ ? null
+ : getter.getReturnType();
+ }
+
+
+//----------------------------------------------------------------------------
+// Internals
+//----------------------------------------------------------------------------
+
+ private void introspect(Class<?> klass)
+ {
+ try
+ {
+ for (Method method : klass.getMethods())
+ {
+ if (method.getDeclaringClass() == Object.class)
+ continue;
+
+ String methodName = method.getName();
+ if (methodName.startsWith("get"))
+ {
+ String propName = extractAndSavePropName(methodName, 3);
+ saveGetter(propName, method);
+ }
+ else if (methodName.startsWith("is"))
+ {
+ String propName = extractAndSavePropName(methodName, 2);
+ saveGetter(propName, method);
+ }
+ else if (methodName.startsWith("set"))
+ {
+ String propName = extractAndSavePropName(methodName, 3);
+ saveSetter(propName, method);
+ }
+ }
+ }
+ catch (Exception ee)
+ {
+ throw new ConversionException("unable to introspect", ee);
+ }
+ }
+
+
+ private String extractAndSavePropName(String methodName, int pos)
+ {
+ String propName = methodName.substring(pos);
+ _propNames.add(Introspector.decapitalize(propName));
+ return propName.toLowerCase();
+ }
+
+
+ private void saveGetter(String propName, Method method)
+ {
+ Method existing = _getters.get(propName);
+ if (existing == null)
+ {
+ _getters.put(propName, method);
+ return;
+ }
+
+ Class<?> methodClass = method.getReturnType();
+ Class<?> existingClass = existing.getReturnType();
+ if (existingClass.isAssignableFrom(methodClass))
+ {
+ _getters.put(propName, method);
+ return;
+ }
+ }
+
+
+ private void saveSetter(String propName, Method method)
+ {
+ Method existing = _setters.get(propName);
+ if (existing == null)
+ {
+ _setters.put(propName, method);
+ return;
+ }
+
+ Class<?> methodClass = method.getDeclaringClass();
+ Class<?> existingClass = existing.getDeclaringClass();
+ if (!existingClass.isAssignableFrom(methodClass))
+ return; // existing is subclass, keep it
+
+ if (methodClass != existingClass)
+ {
+ // existing is superclass, take subclass
+ _setters.put(propName, method);
+ return;
+ }
+
+ if (setterRank(method) < setterRank(existing))
+ {
+ _setters.put(propName, method);
+ return;
+ }
+ }
+
+
+ private static int setterRank(Method method)
+ {
+ Class<?> parmClass = method.getParameterTypes()[0];
+ if (parmClass.isPrimitive())
+ return 1;
+ if (Number.class.isAssignableFrom(parmClass))
+ return 2;
+ if (String.class.isAssignableFrom(parmClass))
+ return 3;
+ return 4;
+ }
+}
Added: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/IntrospectionCache.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/IntrospectionCache.java (rev 0)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/IntrospectionCache.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -0,0 +1,65 @@
+// Copyright 2008-2009 severally by the contributors
+//
+// 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 net.sf.practicalxml.converter.bean;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A thread-safe cache of {@link Introspection} objects. May be constructed
+ * using either a local or shared (static) cache.
+ */
+public class IntrospectionCache
+{
+ private static Map<Class<?>,Introspection> _staticCache = new HashMap<Class<?>,Introspection>();
+ private Map<Class<?>,Introspection> _cache;
+
+
+ /**
+ * Creates an instance that uses a local cache.
+ */
+ public IntrospectionCache()
+ {
+ this(false);
+ }
+
+
+ /**
+ * Creates an instance that will either use a local or shared (static) cache.
+ */
+ public IntrospectionCache(boolean shared)
+ {
+ _cache = shared ? _staticCache
+ : new HashMap<Class<?>,Introspection>();
+ }
+
+
+ /**
+ * Returns an {@link Introspection} of the passed class.
+ *
+ * @throws ConversionError if unable to introspect the class.
+ */
+ public synchronized Introspection lookup(Class<?> klass)
+ {
+ Introspection result = _cache.get(klass);
+ if (result == null)
+ {
+ result = new Introspection(klass);
+ _cache.put(klass, result);
+ }
+ return result;
+ }
+}
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanConverter.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -14,10 +14,6 @@
package net.sf.practicalxml.converter.bean;
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -53,7 +49,7 @@
{
private EnumSet<Xml2BeanOptions> _options;
private PrimitiveConversionHelper _helper;
- private Map<Class<?>,Map<String,Method>> _introspectedClasses;
+ private IntrospectionCache _introspections;
public Xml2BeanConverter(Xml2BeanOptions... options)
@@ -63,7 +59,7 @@
_options.add(option);
_helper = new PrimitiveConversionHelper(_options.contains(Xml2BeanOptions.EXPECT_XSD_FORMAT));
- _introspectedClasses = new HashMap<Class<?>,Map<String,Method>>();
+ _introspections = new IntrospectionCache(_options.contains(Xml2BeanOptions.INTROSPECTION_CACHE));
}
@@ -298,11 +294,8 @@
private Method getSetterMethod(Class<?> beanKlass, Element child)
{
- Map<String,Method> methodMap = _introspectedClasses.get(beanKlass);
- if (methodMap == null)
- methodMap = introspect(beanKlass);
-
- Method setter = methodMap.get(DomUtil.getLocalName(child));
+ Method setter = _introspections.lookup(beanKlass)
+ .setter(DomUtil.getLocalName(child));
if ((setter == null) && !_options.contains(Xml2BeanOptions.IGNORE_MISSING_PROPERTIES))
{
throw new ConversionException("can't find property setter", child);
@@ -312,29 +305,6 @@
}
- private Map<String,Method> introspect(Class<?> klass)
- {
- Map<String,Method> methodMap = new HashMap<String,Method>();
- try
- {
- BeanInfo info = Introspector.getBeanInfo(klass, Object.class);
- for (PropertyDescriptor propDesc : info.getPropertyDescriptors())
- {
- Method setter = propDesc.getWriteMethod();
- if (setter != null)
- methodMap.put(propDesc.getName(), setter);
- }
- }
- catch (IntrospectionException ee)
- {
- throw new ConversionException("unable to introspect", ee);
- }
-
- _introspectedClasses.put(klass, methodMap);
- return methodMap;
- }
-
-
/**
* Attempts to create a <code>Collection</code> instance appropriate for
* the passed class, returns <code>null</code> if unable.
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/Xml2BeanOptions.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -54,5 +54,14 @@
* uses the <code>xsi:type</code> value to choose between different setter
* methods, but otherwise ignores it.
*/
- REQUIRE_XSI_TYPE
+ REQUIRE_XSI_TYPE,
+
+ /**
+ * Will use a shared static introspection cache for all conversions.
+ * <p>
+ * <strong>Warning</strong>: if you use this option, do not store this
+ * library in a shared app-server classpath. If you do, the cache will
+ * prevent class unloading, and you will run out of permgen space.
+ */
+ INTROSPECTION_CACHE
}
Modified: branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/package.html
===================================================================
--- branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/package.html 2009-10-10 18:19:28 UTC (rev 163)
+++ branches/dev-1.1/src/main/java/net/sf/practicalxml/converter/bean/package.html 2009-10-13 18:35:12 UTC (rev 164)
@@ -120,15 +120,8 @@
</ul>
<tr><td>Bean-structured Objects
<td>The object is introspected, and properties are written in the order
- provided by the <code>Introspector</code>. Note that this means you
- can't validate beans against a schema, as the order of elements may
- change.
- <p>
- <strong>Warning</strong>:
- Bean-to-XML conversion uses <code>java.beans.Introspector</code>, which
- holds a cache of introspected objects. If you use this conversion in an
- app-server you should call <code>Introspector.flushCaches()</code> during
- deploy.
+ provided by the introspector. Note that this means you can't validate
+ beans against a schema, as the order of elements may change.
<td>The bean class must provide a no-argument constructor (otherwise it
doesn't follow the bean spec, and we can't use it).
<p>
Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospection.java
===================================================================
--- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospection.java (rev 0)
+++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospection.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -0,0 +1,324 @@
+// Copyright 2008-2009 severally by the contributors
+//
+// 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 net.sf.practicalxml.converter.bean;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+
+public class TestIntrospection
+extends TestCase
+{
+ public TestIntrospection(String testName)
+ {
+ super(testName);
+ }
+
+
+//----------------------------------------------------------------------------
+// Classes to introspect -- all must be public and static
+//----------------------------------------------------------------------------
+
+ /**
+ * A simple bean class, with both primitive and object properties, and
+ * getters that use both "get" and "is".
+ */
+ public static class SimpleBean
+ {
+ private String _sVal;
+ private int _iVal;
+ private boolean _bVal;
+
+ public String getSVal() { return _sVal; }
+ public void setSVal(String val) { _sVal = val; }
+
+ public int getIVal() { return _iVal; }
+ public void setIVal(int val) { _iVal = val; }
+
+ public boolean isBVal() { return _bVal; }
+ public void setBVal(boolean val) { _bVal = val; }
+ }
+
+
+ /**
+ * A class that violates the bean spec by providing getters and setters
+ * with different types.
+ */
+ public static class MixedPrimitiveAndWrapperBean
+ {
+ private Integer _iVal;
+
+ public Integer getIVal() { return _iVal; }
+ public void setIVal(int val) { _iVal = Integer.valueOf(val); }
+ }
+
+
+ /**
+ * A class that provides a variety of setters for its values.
+ */
+ public static class MultipleSetterBean
+ {
+ private String _propS1;
+ private String _propS2;
+ private Integer _propI1;
+ private Integer _propI2;
+ private Integer _propI3;
+ private Integer _propI4;
+
+ public String getPropS1() { return _propS1; }
+ public void setPropS1(String val) { _propS1 = val; }
+ public void setPropS1(Object val) { _propS1 = String.valueOf(val); }
+
+ public String getPropS2() { return _propS2; }
+ public void setPropS2(Object val) { _propS2 = String.valueOf(val); }
+
+ public Integer getPropI1() { return _propI1; }
+ public void setPropI1(Integer val) { _propI1 = val; }
+ public void setPropI1(int val) { _propI1 = Integer.valueOf(val); }
+
+ public Integer getPropI2() { return _propI2; }
+ public void setPropI2(String val) { _propI2 = Integer.valueOf(val); }
+ public void setPropI2(Integer val) { _propI2 = val; }
+
+ public Integer getPropI3() { return _propI3; }
+ public void setPropI3(String val) { _propI3 = Integer.valueOf(val); }
+ public void setPropI3(Object val) { _propI3 = Integer.valueOf(String.valueOf(val)); }
+
+ public Integer getPropI4() { return _propI4; }
+ public void setPropI4(Object val) { _propI4 = Integer.valueOf(String.valueOf(val)); }
+ }
+
+
+ /**
+ * Base class for override tests.
+ */
+ public static class OverrideBaseBean
+ {
+ protected Integer _iVal;
+
+ public Number getIVal() { return _iVal; }
+ public void setIVal(Integer val) { _iVal = val; }
+ }
+
+
+ /**
+ * Subclass for override tests.
+ */
+ public static class OverrideChildBean
+ extends OverrideBaseBean
+ {
+ @Override
+ public Integer getIVal() { return _iVal; }
+ public void setIVal(String val) { _iVal = Integer.valueOf(val); }
+ }
+
+
+ /**
+ * This class has setters for all fields, but is missing a getter. The
+ * introspector should ignore that field.
+ */
+ public static class MissingGetterBean
+ {
+ private String _propS1;
+ private String _propS2;
+
+ public String getPropS1() { return _propS1; }
+ public void setPropS1(String val) { _propS1 = val; }
+
+ public void setPropS2(String val) { _propS2 = val; }
+ }
+
+
+//----------------------------------------------------------------------------
+// Test Cases
+//----------------------------------------------------------------------------
+
+ public void testSimpleBean() throws Exception
+ {
+ Introspection ispec = new Introspection(SimpleBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(3, props.size());
+ assertTrue(props.contains("SVal"));
+ assertTrue(props.contains("IVal"));
+ assertTrue(props.contains("BVal"));
+
+ assertEquals(String.class, ispec.type("sval"));
+ assertEquals(Integer.TYPE, ispec.type("ival"));
+ assertEquals(Boolean.TYPE, ispec.type("bval"));
+
+ Method getSVal = ispec.getter("sval");
+ assertEquals("getSVal", getSVal.getName());
+ assertEquals(String.class, getSVal.getReturnType());
+
+ Method setSVal = ispec.setter("sval");
+ assertEquals("setSVal", setSVal.getName());
+ assertEquals(String.class, setSVal.getParameterTypes()[0]);
+
+ Method getIVal = ispec.getter("ival");
+ assertEquals("getIVal", getIVal.getName());
+ assertEquals(Integer.TYPE, getIVal.getReturnType());
+
+ Method setIVal = ispec.setter("ival");
+ assertEquals("setIVal", setIVal.getName());
+ assertEquals(Integer.TYPE, setIVal.getParameterTypes()[0]);
+
+ Method getBVal = ispec.getter("bval");
+ assertEquals("isBVal", getBVal.getName());
+ assertEquals(Boolean.TYPE, getBVal.getReturnType());
+
+ Method setBVal = ispec.setter("bval");
+ assertEquals("setBVal", setBVal.getName());
+ assertEquals(Boolean.TYPE, setBVal.getParameterTypes()[0]);
+ }
+
+
+ public void testPropertyNamesIgnoreCase() throws Exception
+ {
+ Introspection ispec = new Introspection(SimpleBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertTrue(props.contains("SVal"));
+ assertFalse(props.contains("sval"));
+ assertFalse(props.contains("SVAL"));
+
+ assertEquals(String.class, ispec.type("SVal"));
+ assertEquals(String.class, ispec.type("sval"));
+ assertEquals(String.class, ispec.type("SVAL"));
+
+ Method g1 = ispec.getter("SVal");
+ Method g2 = ispec.getter("sval");
+ Method g3 = ispec.getter("SVAL");
+ assertNotNull(g1);
+ assertSame(g1, g2);
+ assertSame(g1, g3);
+
+ Method s1 = ispec.setter("SVal");
+ Method s2 = ispec.setter("sval");
+ Method s3 = ispec.setter("SVAL");
+ assertNotNull(s1);
+ assertSame(s1, s2);
+ assertSame(s1, s3);
+ }
+
+
+
+ public void testMixingPrimitiveAndWrappers() throws Exception
+ {
+ Introspection ispec = new Introspection(MixedPrimitiveAndWrapperBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(1, props.size());
+ assertTrue(props.contains("IVal"));
+
+ assertEquals(Integer.class, ispec.type("ival"));
+
+ Method getIVal = ispec.getter("ival");
+ assertEquals("getIVal", getIVal.getName());
+ assertEquals(Integer.class, getIVal.getReturnType());
+
+ Method setIVal = ispec.setter("ival");
+ assertEquals("setIVal", setIVal.getName());
+ assertEquals(Integer.TYPE, setIVal.getParameterTypes()[0]);
+ }
+
+
+ public void testSetterParameterRanking() throws Exception
+ {
+ Introspection ispec = new Introspection(MultipleSetterBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(6, props.size());
+ assertTrue(props.contains("propS1"));
+ assertTrue(props.contains("propS2"));
+ assertTrue(props.contains("propI1"));
+ assertTrue(props.contains("propI2"));
+ assertTrue(props.contains("propI3"));
+ assertTrue(props.contains("propI4"));
+
+ Method setPropS1 = ispec.setter("propS1");
+ assertEquals("setPropS1", setPropS1.getName());
+ assertEquals(String.class, setPropS1.getParameterTypes()[0]);
+
+ Method setPropS2 = ispec.setter("propS2");
+ assertEquals("setPropS2", setPropS2.getName());
+ assertEquals(Object.class, setPropS2.getParameterTypes()[0]);
+
+ Method setPropI1 = ispec.setter("propI1");
+ assertEquals("setPropI1", setPropI1.getName());
+ assertEquals(Integer.TYPE, setPropI1.getParameterTypes()[0]);
+
+ Method setPropI2 = ispec.setter("propI2");
+ assertEquals("setPropI2", setPropI2.getName());
+ assertEquals(Integer.class, setPropI2.getParameterTypes()[0]);
+
+ Method setPropI3 = ispec.setter("propI3");
+ assertEquals("setPropI3", setPropI3.getName());
+ assertEquals(String.class, setPropI3.getParameterTypes()[0]);
+
+ Method setPropI4 = ispec.setter("propI4");
+ assertEquals("setPropI4", setPropI4.getName());
+ assertEquals(Object.class, setPropI4.getParameterTypes()[0]);
+ }
+
+
+ public void testSubclassSetterOverride() throws Exception
+ {
+ Introspection ispec = new Introspection(OverrideChildBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(1, props.size());
+ assertTrue(props.contains("IVal"));
+
+ Method setIVal = ispec.setter("ival");
+ assertEquals("setIVal", setIVal.getName());
+ assertEquals(String.class, setIVal.getParameterTypes()[0]);
+ }
+
+
+ public void testSubclassGetterOverride() throws Exception
+ {
+ Introspection ispec = new Introspection(OverrideChildBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(1, props.size());
+ assertTrue(props.contains("IVal"));
+
+ Method getIVal = ispec.getter("ival");
+ assertEquals("getIVal", getIVal.getName());
+ assertEquals(Integer.class, getIVal.getReturnType());
+ }
+
+
+ public void testMissingGetter() throws Exception
+ {
+ Introspection ispec = new Introspection(MissingGetterBean.class);
+
+ Set<String> props = ispec.propertyNames();
+ assertEquals(2, props.size());
+ assertTrue(props.contains("propS1"));
+ assertTrue(props.contains("propS2"));
+
+ assertNotNull(ispec.getter("propS1"));
+ assertNotNull(ispec.setter("propS1"));
+ assertEquals(String.class, ispec.type("propS1"));
+
+ assertNull(ispec.getter("propS2"));
+ assertNotNull(ispec.setter("propS2"));
+ assertNull(ispec.type("propS2"));
+ }
+}
Added: branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospectionCache.java
===================================================================
--- branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospectionCache.java (rev 0)
+++ branches/dev-1.1/src/test/java/net/sf/practicalxml/converter/bean/TestIntrospectionCache.java 2009-10-13 18:35:12 UTC (rev 164)
@@ -0,0 +1,90 @@
+// Copyright 2008-2009 severally by the contributors
+//
+// 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 net.sf.practicalxml.converter.bean;
+
+import junit.framework.TestCase;
+
+public class TestIntrospectionCache
+extends TestCase
+{
+ public TestIntrospectionCache(String testName)
+ {
+ super(testName);
+ }
+
+
+//----------------------------------------------------------------------------
+// Test Objects
+//----------------------------------------------------------------------------
+
+ public static class Bean1
+ {
+ private String _sVal;
+
+ public String getSVal() { return _sVal; }
+ public void setSVal(String val) { _sVal = val; }
+ }
+
+
+ public static class Bean2
+ {
+ private String _sVal;
+
+ public String getSVal() { return _sVal; }
+ public void setSVal(String val) { _sVal = val; }
+ }
+
+//----------------------------------------------------------------------------
+// Test Cases
+//----------------------------------------------------------------------------
+
+ public void testBasicOperation() throws Exception
+ {
+ IntrospectionCache cache = new IntrospectionCache();
+
+ Introspection ispec1 = cache.lookup(Bean1.class);
+ assertNotNull(ispec1);
+
+ Introspection ispec2 = cache.lookup(Bean2.class);
+ assertNotNull(ispec2);
+ assertNotSame(ispec1, ispec2);
+
+ Introspection ispec3 = cache.lookup(Bean1.class);
+ assertNotNull(ispec3);
+ assertSame(ispec1, ispec3);
+ }
+
+
+ // note: this is the only method allowed to test a static cache
+ public void testStaticCache() throws Exception
+ {
+ IntrospectionCache cache1 = new IntrospectionCache(true);
+ IntrospectionCache cache2 = new IntrospectionCache(false);
+ IntrospectionCache cache3 = new IntrospectionCache(true);
+ IntrospectionCache cache4 = new IntrospectionCache(false);
+
+ Introspection ispec1 = cache1.lookup(Bean1.class);
+
+ Introspection ispec2 = cache2.lookup(Bean1.class);
+ assertNotSame(ispec1, ispec2);
+
+ Introspection ispec3 = cache3.lookup(Bean1.class);
+ assertSame(ispec1, ispec3);
+
+ Introspection ispec4 = cache4.lookup(Bean1.class);
+ assertNotSame(ispec1, ispec4);
+ assertNotSame(ispec2, ispec4);
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|