-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Got rid of Introspector, bean properties changed slightly.
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
1 parent
0917b00
commit 5a1db5b
Showing
3 changed files
with
89 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */ | ||
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<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)) { | ||
|
@@ -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; | ||
} | ||
|
@@ -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); | ||
} | ||
} | ||
|
||
|
@@ -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; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */ | ||
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; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <a href="mailto:[email protected]">Nathan Sweet</a> */ | ||
public class YamlWriter { | ||
|
@@ -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 { | ||
|
@@ -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; | ||
|