[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. |