Issue 60: Simplify the way how the order of JavaBean properties is specified
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d498d2a..fd19336 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,11 @@
</properties>
<body>
<release version="1.8-SNAPSHOT" date="in Mercurial" description="Performance improvement">
+ <action dev="py4fun" type="update" issue="60">
+ Simplify the way how the order of JavaBean properties is specified. Introduce
+ PropertyUtils.createPropertySet() method to be overridden when a specific order
+ is expected (2010-11-23)
+ </action>
<action dev="maslovalex" type="fix" issue="95">
Fix: Loading of generic collections with Array parameter(s). (2010-11-16)
</action>
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
index 5cb7835..ed26d82 100644
--- a/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
+++ b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
@@ -24,6 +24,7 @@
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -37,83 +38,77 @@
private BeanAccess beanAccess = BeanAccess.DEFAULT;
private boolean allowReadOnlyProperties = false;
- private Map<String, Property> getPropertiesMap(Class<?> type,
- BeanAccess bAccess) throws IntrospectionException {
+ protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess)
+ throws IntrospectionException {
if (propertiesCache.containsKey(type)) {
return propertiesCache.get(type);
}
- Map<String, Property> properties = new HashMap<String, Property>();
+ Map<String, Property> properties = new LinkedHashMap<String, Property>();
switch (bAccess) {
- case FIELD:
- for (Class<?> c = type; c != null; c = c.getSuperclass()) {
- for (Field field : c.getDeclaredFields()) {
- int modifiers = field.getModifiers();
- if (!Modifier.isStatic(modifiers)
- && !Modifier.isTransient(modifiers)
- && !properties.containsKey(field.getName())) {
- properties.put(field.getName(), new FieldProperty(
- field));
- }
- }
- }
- break;
- default:
- // add JavaBean properties
- for (PropertyDescriptor property : Introspector.getBeanInfo(type)
- .getPropertyDescriptors()) {
- Method readMethod = property.getReadMethod();
- if (readMethod == null
- || !readMethod.getName().equals("getClass")) {
- properties.put(property.getName(), new MethodProperty(
- property));
- }
- }
-
- // add public fields
- for (Field field : type.getFields()) {
+ case FIELD:
+ for (Class<?> c = type; c != null; c = c.getSuperclass()) {
+ for (Field field : c.getDeclaredFields()) {
int modifiers = field.getModifiers();
- if (!Modifier.isStatic(modifiers)
- && !Modifier.isTransient(modifiers)) {
- properties.put(field.getName(),
- new FieldProperty(field));
+ if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
+ && !properties.containsKey(field.getName())) {
+ properties.put(field.getName(), new FieldProperty(field));
}
}
- break;
+ }
+ break;
+ default:
+ // add JavaBean properties
+ for (PropertyDescriptor property : Introspector.getBeanInfo(type)
+ .getPropertyDescriptors()) {
+ Method readMethod = property.getReadMethod();
+ if (readMethod == null || !readMethod.getName().equals("getClass")) {
+ properties.put(property.getName(), new MethodProperty(property));
+ }
+ }
+
+ // add public fields
+ for (Field field : type.getFields()) {
+ int modifiers = field.getModifiers();
+ if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
+ properties.put(field.getName(), new FieldProperty(field));
+ }
+ }
+ break;
}
if (properties.isEmpty()) {
- throw new YAMLException("No JavaBean properties found in "
- + type.getName());
+ throw new YAMLException("No JavaBean properties found in " + type.getName());
}
propertiesCache.put(type, properties);
return properties;
}
- public Set<Property> getProperties(Class<? extends Object> type)
- throws IntrospectionException {
+ public Set<Property> getProperties(Class<? extends Object> type) throws IntrospectionException {
return getProperties(type, beanAccess);
}
- public Set<Property> getProperties(Class<? extends Object> type,
- BeanAccess bAccess) throws IntrospectionException {
+ public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess)
+ throws IntrospectionException {
if (readableProperties.containsKey(type)) {
return readableProperties.get(type);
}
+ Set<Property> properties = createPropertySet(type, bAccess);
+ if (properties.isEmpty()) {
+ throw new YAMLException("No JavaBean properties found in " + type.getName());
+ }
+ readableProperties.put(type, properties);
+ return properties;
+ }
+
+ protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+ throws IntrospectionException {
Set<Property> properties = new TreeSet<Property>();
Collection<Property> props = getPropertiesMap(type, bAccess).values();
for (Property property : props) {
- if (property.isReadable()
- && (allowReadOnlyProperties || property.isWritable())) {
+ if (property.isReadable() && (allowReadOnlyProperties || property.isWritable())) {
properties.add(property);
}
}
-
- if (properties.isEmpty()) {
- throw new YAMLException("No JavaBean properties found in "
- + type.getName());
- }
-
- readableProperties.put(type, properties);
return properties;
}
@@ -122,13 +117,13 @@
return getProperty(type, name, beanAccess);
}
- public Property getProperty(Class<? extends Object> type, String name,
- BeanAccess bAccess) throws IntrospectionException {
+ public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess)
+ throws IntrospectionException {
Map<String, Property> properties = getPropertiesMap(type, bAccess);
Property property = properties.get(name);
if (property == null || !property.isWritable()) {
- throw new YAMLException("Unable to find property '" + name
- + "' on class: " + type.getName());
+ throw new YAMLException("Unable to find property '" + name + "' on class: "
+ + type.getName());
}
return property;
}
@@ -147,17 +142,4 @@
readableProperties.clear();
}
}
-
-// private volatile static PropertyUtils singleton;
-//
-// public static PropertyUtils getSingleton() {
-// if (singleton == null) {
-// synchronized (PropertyUtils.class) {
-// if (singleton == null) {
-// singleton = new PropertyUtils();
-// }
-// }
-// }
-// return singleton;
-// }
}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java
index ad441c0..9abe748 100644
--- a/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java
@@ -19,6 +19,7 @@
import java.beans.IntrospectionException;
import java.util.Arrays;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
@@ -26,24 +27,48 @@
import org.yaml.snakeyaml.Util;
import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.representer.Representer;
public class CustomOrderTest extends TestCase {
public void testReversedOrder() {
- Yaml yaml = new Yaml(new ReversedRepresenter());
+ Representer repr = new Representer();
+ repr.setPropertyUtils(new ReversedPropertyUtils());
+ Yaml yaml = new Yaml(repr);
String output = yaml.dump(getBean());
// System.out.println(output);
assertEquals(Util.getLocalResource("issues/issue59-1.yaml"), output);
}
- private class ReversedRepresenter extends Representer {
+ private class ReversedPropertyUtils extends PropertyUtils {
@Override
- protected Set<Property> getProperties(Class<? extends Object> type)
+ protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
throws IntrospectionException {
Set<Property> result = new TreeSet<Property>(Collections.reverseOrder());
- result.addAll(super.getProperties(type));
+ result.addAll(super.createPropertySet(type, bAccess));
+ return result;
+ }
+ }
+
+ public void testUnsorted() {
+ Representer repr = new Representer();
+ repr.setPropertyUtils(new UnsortedPropertyUtils());
+ Yaml yaml = new Yaml(repr);
+ String output = yaml.dump(getBean());
+ // System.out.println(output);
+ assertEquals(Util.getLocalResource("issues/issue60-3.yaml"), output);
+ }
+
+ private class UnsortedPropertyUtils extends PropertyUtils {
+ @Override
+ protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+ throws IntrospectionException {
+ Set<Property> result = new LinkedHashSet<Property>(getPropertiesMap(type, bAccess)
+ .values());
+ result.remove(result.iterator().next());// drop 'empty' property
return result;
}
}
diff --git a/src/test/resources/issues/issue60-3.yaml b/src/test/resources/issues/issue60-3.yaml
new file mode 100644
index 0000000..ba0806f
--- /dev/null
+++ b/src/test/resources/issues/issue60-3.yaml
@@ -0,0 +1,7 @@
+!!org.yaml.snakeyaml.issues.issue60.SkipBean
+listDate: null
+listInt: [null, 1, 2, 3]
+listStr: [bar, null, foo, null]
+map: {}
+number: null
+text: foo