diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 7aa2a59..454be78 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -16,15 +16,11 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; - -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; @@ -35,6 +31,8 @@ import java.util.Set; import java.util.TreeSet; +import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; + /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ class Beans { @@ -62,16 +60,6 @@ static public DeferredConstruction getDeferredConstruction (Class type, YamlConf return null; } - static private boolean canInitializeProperty (Class type, PropertyDescriptor property, YamlConfig config) { - if (property.getWriteMethod() != null) return true; - - // Check if the property can be initialized through the constructor. - DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) return true; - - return false; - } - static public Object createObject (Class type, boolean privateConstructors) throws InvocationTargetException { // Use no-arg constructor. Constructor constructor = null; @@ -117,16 +105,34 @@ static public Object createObject (Class type, boolean privateConstructors) thro } } - static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { + static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); + Class[] noArgs = new Class[0], oneArg = new Class[1]; Set properties = new TreeSet(); - if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) - properties.add(new MethodProperty(type, property)); - } for (Field field : getAllFields(type)) { + String name = field.getName(); + + if (beanProperties) { + DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); + boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); + + String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null, setMethod = null; + try { + oneArg[0] = field.getType(); + setMethod = type.getMethod("set" + nameUpper, oneArg); + } catch (Exception ignored) { + } + try { + getMethod = type.getMethod("get" + nameUpper, noArgs); + } catch (Exception ignored) { + } + if (getMethod != null && (setMethod != null || constructorProperty)) { + properties.add(new MethodProperty(name, setMethod, getMethod)); + continue; + } + } + int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { @@ -138,27 +144,39 @@ static public Set getProperties (Class type, boolean beanProperties, b return properties; } - static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { + static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); - if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { - if (property.getName().equals(name)) { - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) - return new MethodProperty(type, property); - break; + Class[] noArgs = new Class[0], oneArg = new Class[1]; + for (Field field : getAllFields(type)) { + if (!field.getName().equals(name)) continue; + + if (beanProperties) { + DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); + boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); + + String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null, setMethod = null; + try { + oneArg[0] = field.getType(); + setMethod = type.getMethod("set" + nameUpper, oneArg); + } catch (Exception ignored) { + } + try { + getMethod = type.getMethod("get" + nameUpper, noArgs); + } catch (Exception ignored) { } + if (getMethod != null && (setMethod != null || constructorProperty)) + return new MethodProperty(name, setMethod, getMethod); } - } - for (Field field : getAllFields(type)) { + int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { if (!privateFields) continue; field.setAccessible(true); } - if (field.getName().equals(name)) return new FieldProperty(field); + return new FieldProperty(field); } return null; } @@ -174,11 +192,12 @@ static private ArrayList getAllFields (Class type) { } static public class MethodProperty extends Property { - private final PropertyDescriptor property; + private final Method setMethod, getMethod; - public MethodProperty (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { - super(declaringClass, property); - this.property = property; + public MethodProperty (String name, Method setMethod, Method getMethod) { + super(getMethod.getDeclaringClass(), name, getMethod.getReturnType()); + this.setMethod = setMethod; + this.getMethod = getMethod; } public void set (Object object, Object value) throws Exception { @@ -186,11 +205,11 @@ public void set (Object object, Object value) throws Exception { ((DeferredConstruction)object).storeProperty(this, value); return; } - property.getWriteMethod().invoke(object, value); + setMethod.invoke(object, value); } public Object get (Object object) throws Exception { - return property.getReadMethod().invoke(object); + return getMethod.invoke(object); } } @@ -226,21 +245,6 @@ static public abstract class Property implements Comparable { this.type = type; } - Property (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { - this.declaringClass = declaringClass; - this.name = property.getName(); - try { - // The PropertyDescriptor returns the wrong type if the getter is an implementation of a interface method with a - // generic return value. - type = property.getReadMethod().getDeclaringClass() - .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]).getReturnType(); - } catch (Exception ex) { - IntrospectionException introspectionEx = new IntrospectionException("Error getting "); - introspectionEx.initCause(ex); - throw introspectionEx; - } - } - public int hashCode () { final int prime = 31; int result = 1; diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 3a12ef0..1dbffcb 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -16,12 +16,6 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.Beans.Property; -import com.esotericsoftware.yamlbeans.emitter.EmitterConfig; -import com.esotericsoftware.yamlbeans.scalar.DateSerializer; -import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; - -import java.beans.IntrospectionException; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; @@ -31,6 +25,11 @@ import java.util.IdentityHashMap; import java.util.Map; +import com.esotericsoftware.yamlbeans.Beans.Property; +import com.esotericsoftware.yamlbeans.emitter.EmitterConfig; +import com.esotericsoftware.yamlbeans.scalar.DateSerializer; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + /** Stores configuration for reading and writing YAML. * @author Nathan Sweet */ public class YamlConfig { @@ -80,20 +79,13 @@ public void setPropertyElementType (Class type, String propertyName, Class eleme if (type == null) throw new IllegalArgumentException("type cannot be null."); if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null."); if (elementType == null) throw new IllegalArgumentException("propertyType cannot be null."); - Property property = null; - Exception cause = null; - try { - property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); - } catch (IntrospectionException ex) { - cause = ex; - } - if (property == null) { - throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, - cause); - } - if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) + Property property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); + if (property == null) + throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName); + if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) { throw new IllegalArgumentException("The '" + propertyName + "' property on the " + type.getName() + " class must be a Collection or Map: " + property.getType()); + } propertyToElementType.put(property, elementType); } @@ -103,21 +95,14 @@ public void setPropertyDefaultType (Class type, String propertyName, Class defau if (type == null) throw new IllegalArgumentException("type cannot be null."); if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null."); if (defaultType == null) throw new IllegalArgumentException("defaultType cannot be null."); - Property property = null; - Exception cause = null; - try { - property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); - } catch (IntrospectionException ex) { - cause = ex; - } - if (property == null) { - throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, - cause); - } + Property property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); + if (property == null) + throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName); propertyToDefaultType.put(property, defaultType); } - /** If true, bean properties with both a getter and setter will be used. Default is true. */ + /** If true, bean properties with both a getter and setter will be used. Note the getter and setter methods must be named the + * same as the field they get or set. Default is true. */ public void setBeanProperties (boolean beanProperties) { this.beanProperties = beanProperties; } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 6e62de8..665c5a2 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -16,20 +16,6 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.Beans.Property; -import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; -import com.esotericsoftware.yamlbeans.emitter.Emitter; -import com.esotericsoftware.yamlbeans.emitter.EmitterException; -import com.esotericsoftware.yamlbeans.parser.AliasEvent; -import com.esotericsoftware.yamlbeans.parser.DocumentEndEvent; -import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; -import com.esotericsoftware.yamlbeans.parser.Event; -import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; -import com.esotericsoftware.yamlbeans.parser.ScalarEvent; -import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; -import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; - -import java.beans.IntrospectionException; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Array; @@ -44,6 +30,19 @@ import java.util.Map.Entry; import java.util.Set; +import com.esotericsoftware.yamlbeans.Beans.Property; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.AliasEvent; +import com.esotericsoftware.yamlbeans.parser.DocumentEndEvent; +import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; +import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + /** Serializes Java objects as YAML. * @author Nathan Sweet */ public class YamlWriter { @@ -243,12 +242,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } } - Set properties; - try { - properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config); - } catch (IntrospectionException ex) { - throw new YamlException("Error inspecting class: " + valueClass.getName(), ex); - } + Set properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config); emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); for (Property property : properties) { try { @@ -299,14 +293,9 @@ private void countObjectReferences (Object object) throws YamlException { return; } - // Value must be a bean. + // Value must be an object. - Set properties; - try { - properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config); - } catch (IntrospectionException ex) { - throw new YamlException("Error inspecting class: " + object.getClass().getName(), ex); - } + Set properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config); for (Property property : properties) { if (Beans.isScalar(property.getType())) continue; Object propertyValue;