From 9e14409334caa7f365be4b19ce2d4ae50754dd97 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 26 Jul 2013 10:32:10 +0100 Subject: [PATCH] Improve test coverage in RelaxedDataBinder --- .../bootstrap/bind/RelaxedDataBinder.java | 303 +++++++++--------- .../bind/BindingPreparationTests.java | 46 +++ 2 files changed, 200 insertions(+), 149 deletions(-) diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/RelaxedDataBinder.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/RelaxedDataBinder.java index a4f590e5fa9..bd3d4fdd06c 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/RelaxedDataBinder.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/RelaxedDataBinder.java @@ -139,160 +139,35 @@ public class RelaxedDataBinder extends DataBinder { } } + /** + * Normalize a bean property path to a format understood by a BeanWrapper. This is + * used so that + * + * + * @param wrapper a bean wrapper for the object to bind + * @param path the bean path to bind + * @return a transformed path with correct bean wrapper syntax + */ protected String normalizePath(BeanWrapper wrapper, String path) { return initializePath(wrapper, new BeanPath(path), 0); } - private static class BeanPath { - - private List nodes; - - public BeanPath(String path) { - this.nodes = splitPath(path); - } - - public void mapIndex(int index) { - PathNode node = this.nodes.get(index); - if (node instanceof PropertyNode) { - node = ((PropertyNode) node).mapIndex(); - } - this.nodes.set(index, node); - } - - public String prefix(int index) { - return range(0, index); - } - - public void rename(int index, String name) { - this.nodes.get(index).name = name; - } - - public String name(int index) { - if (index < this.nodes.size()) { - return this.nodes.get(index).name; - } - return null; - } - - public int length() { - return this.nodes.size(); - } - - private String range(int start, int end) { - StringBuilder builder = new StringBuilder(); - for (int i = start; i < end; i++) { - PathNode node = this.nodes.get(i); - builder.append(node); - } - if (builder.toString().startsWith(("."))) { - builder.replace(0, 1, ""); - } - return builder.toString(); - } - - public boolean isArrayIndex(int index) { - return this.nodes.get(index) instanceof ArrayIndexNode; - } - - public boolean isProperty(int index) { - return this.nodes.get(index) instanceof PropertyNode; - } - - @Override - public String toString() { - return prefix(this.nodes.size()); - } - - private static class PathNode { - - protected String name; - - public PathNode(String name) { - this.name = name; - } - - } - - private static class ArrayIndexNode extends PathNode { - - public ArrayIndexNode(String name) { - super(name); - } - - @Override - public String toString() { - return "[" + this.name + "]"; - } - - } - - private static class MapIndexNode extends PathNode { - - public MapIndexNode(String name) { - super(name); - } - - @Override - public String toString() { - return "[" + this.name + "]"; - } - } - - private static class PropertyNode extends PathNode { - - public PropertyNode(String name) { - super(name); - } - - public MapIndexNode mapIndex() { - return new MapIndexNode(this.name); - } - - @Override - public String toString() { - return "." + this.name; - } - } - - private List splitPath(String path) { - List nodes = new ArrayList(); - for (String name : StringUtils.delimitedListToStringArray(path, ".")) { - for (String sub : StringUtils.delimitedListToStringArray(name, "[")) { - if (StringUtils.hasText(sub)) { - if (sub.endsWith("]")) { - sub = sub.substring(0, sub.length() - 1); - if (sub.matches("[0-9]+")) { - nodes.add(new ArrayIndexNode(sub)); - } - else { - nodes.add(new MapIndexNode(sub)); - } - } - else { - nodes.add(new PropertyNode(sub)); - } - } - } - } - return nodes; - } - - } - private String initializePath(BeanWrapper wrapper, BeanPath path, int index) { + String prefix = path.prefix(index); String key = path.name(index); - if (key == null) { - return path.toString(); - } if (path.isProperty(index)) { key = getActualPropertyName(wrapper, prefix, key); path.rename(index, key); } - if (index >= path.length() - 1) { + if (path.name(++index) == null) { return path.toString(); } - String name = path.prefix(++index); + + String name = path.prefix(index); TypeDescriptor descriptor = wrapper.getPropertyTypeDescriptor(name); if (descriptor == null || descriptor.isMap()) { if (descriptor != null) { @@ -302,20 +177,18 @@ public class RelaxedDataBinder extends DataBinder { extendMapIfNecessary(wrapper, path, index); } else if (descriptor.isCollection()) { - // TODO: test collection extension extendCollectionIfNecessary(wrapper, path, index); } else if (descriptor.getType().equals(Object.class)) { path.mapIndex(index); - name = path.prefix(index + 1); - if (wrapper.getPropertyValue(name) == null) { - wrapper.setPropertyValue(name, new LinkedHashMap()); + String next = path.prefix(index + 1); + if (wrapper.getPropertyValue(next) == null) { + wrapper.setPropertyValue(next, new LinkedHashMap()); } } - if (index < path.length()) { - return initializePath(wrapper, path, index); - } - return path.toString(); + + return initializePath(wrapper, path, index); + } private void extendCollectionIfNecessary(BeanWrapper wrapper, BeanPath path, int index) { @@ -439,4 +312,136 @@ public class RelaxedDataBinder extends DataBinder { } } + private static class BeanPath { + + private List nodes; + + public BeanPath(String path) { + this.nodes = splitPath(path); + } + + public void mapIndex(int index) { + PathNode node = this.nodes.get(index); + if (node instanceof PropertyNode) { + node = ((PropertyNode) node).mapIndex(); + } + this.nodes.set(index, node); + } + + public String prefix(int index) { + return range(0, index); + } + + public void rename(int index, String name) { + this.nodes.get(index).name = name; + } + + public String name(int index) { + if (index < this.nodes.size()) { + return this.nodes.get(index).name; + } + return null; + } + + private String range(int start, int end) { + StringBuilder builder = new StringBuilder(); + for (int i = start; i < end; i++) { + PathNode node = this.nodes.get(i); + builder.append(node); + } + if (builder.toString().startsWith(("."))) { + builder.replace(0, 1, ""); + } + return builder.toString(); + } + + public boolean isArrayIndex(int index) { + return this.nodes.get(index) instanceof ArrayIndexNode; + } + + public boolean isProperty(int index) { + return this.nodes.get(index) instanceof PropertyNode; + } + + @Override + public String toString() { + return prefix(this.nodes.size()); + } + + private static class PathNode { + + protected String name; + + public PathNode(String name) { + this.name = name; + } + + } + + private static class ArrayIndexNode extends PathNode { + + public ArrayIndexNode(String name) { + super(name); + } + + @Override + public String toString() { + return "[" + this.name + "]"; + } + + } + + private static class MapIndexNode extends PathNode { + + public MapIndexNode(String name) { + super(name); + } + + @Override + public String toString() { + return "[" + this.name + "]"; + } + } + + private static class PropertyNode extends PathNode { + + public PropertyNode(String name) { + super(name); + } + + public MapIndexNode mapIndex() { + return new MapIndexNode(this.name); + } + + @Override + public String toString() { + return "." + this.name; + } + } + + private List splitPath(String path) { + List nodes = new ArrayList(); + for (String name : StringUtils.delimitedListToStringArray(path, ".")) { + for (String sub : StringUtils.delimitedListToStringArray(name, "[")) { + if (StringUtils.hasText(sub)) { + if (sub.endsWith("]")) { + sub = sub.substring(0, sub.length() - 1); + if (sub.matches("[0-9]+")) { + nodes.add(new ArrayIndexNode(sub)); + } + else { + nodes.add(new MapIndexNode(sub)); + } + } + else { + nodes.add(new PropertyNode(sub)); + } + } + } + } + return nodes; + } + + } + } diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/bind/BindingPreparationTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/bind/BindingPreparationTests.java index 612c9e03343..3bed4c66c08 100644 --- a/spring-bootstrap/src/test/java/org/springframework/bootstrap/bind/BindingPreparationTests.java +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/bind/BindingPreparationTests.java @@ -132,6 +132,28 @@ public class BindingPreparationTests { assertNotNull(wrapper.getPropertyValue("nested[foo]")); } + @Test + public void testAutoGrowListOfMaps() throws Exception { + TargetWithNestedListOfMaps target = new TargetWithNestedListOfMaps(); + BeanWrapperImpl wrapper = new BeanWrapperImpl(target); + wrapper.setAutoGrowNestedPaths(true); + RelaxedDataBinder binder = new RelaxedDataBinder(target); + binder.normalizePath(wrapper, "nested[0][foo]"); + assertNotNull(wrapper.getPropertyValue("nested")); + assertNotNull(wrapper.getPropertyValue("nested[0]")); + } + + @Test + public void testAutoGrowListOfLists() throws Exception { + TargetWithNestedListOfLists target = new TargetWithNestedListOfLists(); + BeanWrapperImpl wrapper = new BeanWrapperImpl(target); + wrapper.setAutoGrowNestedPaths(true); + RelaxedDataBinder binder = new RelaxedDataBinder(target); + binder.normalizePath(wrapper, "nested[0][1]"); + assertNotNull(wrapper.getPropertyValue("nested")); + assertNotNull(wrapper.getPropertyValue("nested[0][1]")); + } + @Test public void testBeanWrapperCreatesNewNestedMaps() throws Exception { TargetWithNestedMap target = new TargetWithNestedMap(); @@ -213,6 +235,30 @@ public class BindingPreparationTests { } } + public static class TargetWithNestedListOfMaps { + private List> nested; + + public List> getNested() { + return this.nested; + } + + public void setNested(List> nested) { + this.nested = nested; + } + } + + public static class TargetWithNestedListOfLists { + private List> nested; + + public List> getNested() { + return this.nested; + } + + public void setNested(List> nested) { + this.nested = nested; + } + } + public static class TargetWithNestedMapOfBean { private Map nested;