Skip to content

Commit

Permalink
Got rid of Introspector, bean properties changed slightly.
Browse files Browse the repository at this point in the history
Bean property getter and setter methods now need to be named the same as the field they get/set. This is usually the case and makes it so we can enumerate the fields and look for a getter and setter method that matches the field name.

closes #25
  • Loading branch information
NathanSweet committed Jan 27, 2015
1 parent 0917b00 commit 5a1db5b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 111 deletions.
112 changes: 58 additions & 54 deletions src/com/esotericsoftware/yamlbeans/Beans.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */
class Beans {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -117,16 +105,34 @@ static public Object createObject (Class type, boolean privateConstructors) thro
}
}

static public Set<Property> getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config)
throws IntrospectionException {
static public Set<Property> 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<Property> 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)) {
Expand All @@ -138,27 +144,39 @@ static public Set<Property> 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;
}
Expand All @@ -174,23 +192,24 @@ static private ArrayList<Field> 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 {
if (object instanceof DeferredConstruction) {
((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);
}
}

Expand Down Expand Up @@ -226,21 +245,6 @@ static public abstract class Property implements Comparable<Property> {
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;
Expand Down
45 changes: 15 additions & 30 deletions src/com/esotericsoftware/yamlbeans/YamlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */
public class YamlConfig {
Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
}
Expand Down
43 changes: 16 additions & 27 deletions src/com/esotericsoftware/yamlbeans/YamlWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */
public class YamlWriter {
Expand Down Expand Up @@ -243,12 +242,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla
}
}

Set<Property> properties;
try {
properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config);
} catch (IntrospectionException ex) {
throw new YamlException("Error inspecting class: " + valueClass.getName(), ex);
}
Set<Property> properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config);
emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false));
for (Property property : properties) {
try {
Expand Down Expand Up @@ -299,14 +293,9 @@ private void countObjectReferences (Object object) throws YamlException {
return;
}

// Value must be a bean.
// Value must be an object.

Set<Property> 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<Property> properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config);
for (Property property : properties) {
if (Beans.isScalar(property.getType())) continue;
Object propertyValue;
Expand Down

0 comments on commit 5a1db5b

Please sign in to comment.