Skip to content

Commit 9e14409

Browse files
committed
Improve test coverage in RelaxedDataBinder
1 parent b1f4320 commit 9e14409

File tree

2 files changed

+200
-149
lines changed

2 files changed

+200
-149
lines changed

spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/RelaxedDataBinder.java

+154-149
Original file line numberDiff line numberDiff line change
@@ -139,160 +139,35 @@ private void modifyProperty(MutablePropertyValues propertyValues, BeanWrapper ta
139139
}
140140
}
141141

142+
/**
143+
* Normalize a bean property path to a format understood by a BeanWrapper. This is
144+
* used so that
145+
* <ul>
146+
* <li>Fuzzy matching can be employed for bean property names</li>
147+
* <li>Period separators can be used instead of indexing ([...]) for map keys</li>
148+
* </ul>
149+
*
150+
* @param wrapper a bean wrapper for the object to bind
151+
* @param path the bean path to bind
152+
* @return a transformed path with correct bean wrapper syntax
153+
*/
142154
protected String normalizePath(BeanWrapper wrapper, String path) {
143155
return initializePath(wrapper, new BeanPath(path), 0);
144156
}
145157

146-
private static class BeanPath {
147-
148-
private List<PathNode> nodes;
149-
150-
public BeanPath(String path) {
151-
this.nodes = splitPath(path);
152-
}
153-
154-
public void mapIndex(int index) {
155-
PathNode node = this.nodes.get(index);
156-
if (node instanceof PropertyNode) {
157-
node = ((PropertyNode) node).mapIndex();
158-
}
159-
this.nodes.set(index, node);
160-
}
161-
162-
public String prefix(int index) {
163-
return range(0, index);
164-
}
165-
166-
public void rename(int index, String name) {
167-
this.nodes.get(index).name = name;
168-
}
169-
170-
public String name(int index) {
171-
if (index < this.nodes.size()) {
172-
return this.nodes.get(index).name;
173-
}
174-
return null;
175-
}
176-
177-
public int length() {
178-
return this.nodes.size();
179-
}
180-
181-
private String range(int start, int end) {
182-
StringBuilder builder = new StringBuilder();
183-
for (int i = start; i < end; i++) {
184-
PathNode node = this.nodes.get(i);
185-
builder.append(node);
186-
}
187-
if (builder.toString().startsWith(("."))) {
188-
builder.replace(0, 1, "");
189-
}
190-
return builder.toString();
191-
}
192-
193-
public boolean isArrayIndex(int index) {
194-
return this.nodes.get(index) instanceof ArrayIndexNode;
195-
}
196-
197-
public boolean isProperty(int index) {
198-
return this.nodes.get(index) instanceof PropertyNode;
199-
}
200-
201-
@Override
202-
public String toString() {
203-
return prefix(this.nodes.size());
204-
}
205-
206-
private static class PathNode {
207-
208-
protected String name;
209-
210-
public PathNode(String name) {
211-
this.name = name;
212-
}
213-
214-
}
215-
216-
private static class ArrayIndexNode extends PathNode {
217-
218-
public ArrayIndexNode(String name) {
219-
super(name);
220-
}
221-
222-
@Override
223-
public String toString() {
224-
return "[" + this.name + "]";
225-
}
226-
227-
}
228-
229-
private static class MapIndexNode extends PathNode {
230-
231-
public MapIndexNode(String name) {
232-
super(name);
233-
}
234-
235-
@Override
236-
public String toString() {
237-
return "[" + this.name + "]";
238-
}
239-
}
240-
241-
private static class PropertyNode extends PathNode {
242-
243-
public PropertyNode(String name) {
244-
super(name);
245-
}
246-
247-
public MapIndexNode mapIndex() {
248-
return new MapIndexNode(this.name);
249-
}
250-
251-
@Override
252-
public String toString() {
253-
return "." + this.name;
254-
}
255-
}
256-
257-
private List<PathNode> splitPath(String path) {
258-
List<PathNode> nodes = new ArrayList<PathNode>();
259-
for (String name : StringUtils.delimitedListToStringArray(path, ".")) {
260-
for (String sub : StringUtils.delimitedListToStringArray(name, "[")) {
261-
if (StringUtils.hasText(sub)) {
262-
if (sub.endsWith("]")) {
263-
sub = sub.substring(0, sub.length() - 1);
264-
if (sub.matches("[0-9]+")) {
265-
nodes.add(new ArrayIndexNode(sub));
266-
}
267-
else {
268-
nodes.add(new MapIndexNode(sub));
269-
}
270-
}
271-
else {
272-
nodes.add(new PropertyNode(sub));
273-
}
274-
}
275-
}
276-
}
277-
return nodes;
278-
}
279-
280-
}
281-
282158
private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
159+
283160
String prefix = path.prefix(index);
284161
String key = path.name(index);
285-
if (key == null) {
286-
return path.toString();
287-
}
288162
if (path.isProperty(index)) {
289163
key = getActualPropertyName(wrapper, prefix, key);
290164
path.rename(index, key);
291165
}
292-
if (index >= path.length() - 1) {
166+
if (path.name(++index) == null) {
293167
return path.toString();
294168
}
295-
String name = path.prefix(++index);
169+
170+
String name = path.prefix(index);
296171
TypeDescriptor descriptor = wrapper.getPropertyTypeDescriptor(name);
297172
if (descriptor == null || descriptor.isMap()) {
298173
if (descriptor != null) {
@@ -302,20 +177,18 @@ private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
302177
extendMapIfNecessary(wrapper, path, index);
303178
}
304179
else if (descriptor.isCollection()) {
305-
// TODO: test collection extension
306180
extendCollectionIfNecessary(wrapper, path, index);
307181
}
308182
else if (descriptor.getType().equals(Object.class)) {
309183
path.mapIndex(index);
310-
name = path.prefix(index + 1);
311-
if (wrapper.getPropertyValue(name) == null) {
312-
wrapper.setPropertyValue(name, new LinkedHashMap<String, Object>());
184+
String next = path.prefix(index + 1);
185+
if (wrapper.getPropertyValue(next) == null) {
186+
wrapper.setPropertyValue(next, new LinkedHashMap<String, Object>());
313187
}
314188
}
315-
if (index < path.length()) {
316-
return initializePath(wrapper, path, index);
317-
}
318-
return path.toString();
189+
190+
return initializePath(wrapper, path, index);
191+
319192
}
320193

321194
private void extendCollectionIfNecessary(BeanWrapper wrapper, BeanPath path, int index) {
@@ -439,4 +312,136 @@ public Map<String, Object> getMap() {
439312
}
440313
}
441314

315+
private static class BeanPath {
316+
317+
private List<PathNode> nodes;
318+
319+
public BeanPath(String path) {
320+
this.nodes = splitPath(path);
321+
}
322+
323+
public void mapIndex(int index) {
324+
PathNode node = this.nodes.get(index);
325+
if (node instanceof PropertyNode) {
326+
node = ((PropertyNode) node).mapIndex();
327+
}
328+
this.nodes.set(index, node);
329+
}
330+
331+
public String prefix(int index) {
332+
return range(0, index);
333+
}
334+
335+
public void rename(int index, String name) {
336+
this.nodes.get(index).name = name;
337+
}
338+
339+
public String name(int index) {
340+
if (index < this.nodes.size()) {
341+
return this.nodes.get(index).name;
342+
}
343+
return null;
344+
}
345+
346+
private String range(int start, int end) {
347+
StringBuilder builder = new StringBuilder();
348+
for (int i = start; i < end; i++) {
349+
PathNode node = this.nodes.get(i);
350+
builder.append(node);
351+
}
352+
if (builder.toString().startsWith(("."))) {
353+
builder.replace(0, 1, "");
354+
}
355+
return builder.toString();
356+
}
357+
358+
public boolean isArrayIndex(int index) {
359+
return this.nodes.get(index) instanceof ArrayIndexNode;
360+
}
361+
362+
public boolean isProperty(int index) {
363+
return this.nodes.get(index) instanceof PropertyNode;
364+
}
365+
366+
@Override
367+
public String toString() {
368+
return prefix(this.nodes.size());
369+
}
370+
371+
private static class PathNode {
372+
373+
protected String name;
374+
375+
public PathNode(String name) {
376+
this.name = name;
377+
}
378+
379+
}
380+
381+
private static class ArrayIndexNode extends PathNode {
382+
383+
public ArrayIndexNode(String name) {
384+
super(name);
385+
}
386+
387+
@Override
388+
public String toString() {
389+
return "[" + this.name + "]";
390+
}
391+
392+
}
393+
394+
private static class MapIndexNode extends PathNode {
395+
396+
public MapIndexNode(String name) {
397+
super(name);
398+
}
399+
400+
@Override
401+
public String toString() {
402+
return "[" + this.name + "]";
403+
}
404+
}
405+
406+
private static class PropertyNode extends PathNode {
407+
408+
public PropertyNode(String name) {
409+
super(name);
410+
}
411+
412+
public MapIndexNode mapIndex() {
413+
return new MapIndexNode(this.name);
414+
}
415+
416+
@Override
417+
public String toString() {
418+
return "." + this.name;
419+
}
420+
}
421+
422+
private List<PathNode> splitPath(String path) {
423+
List<PathNode> nodes = new ArrayList<PathNode>();
424+
for (String name : StringUtils.delimitedListToStringArray(path, ".")) {
425+
for (String sub : StringUtils.delimitedListToStringArray(name, "[")) {
426+
if (StringUtils.hasText(sub)) {
427+
if (sub.endsWith("]")) {
428+
sub = sub.substring(0, sub.length() - 1);
429+
if (sub.matches("[0-9]+")) {
430+
nodes.add(new ArrayIndexNode(sub));
431+
}
432+
else {
433+
nodes.add(new MapIndexNode(sub));
434+
}
435+
}
436+
else {
437+
nodes.add(new PropertyNode(sub));
438+
}
439+
}
440+
}
441+
}
442+
return nodes;
443+
}
444+
445+
}
446+
442447
}

0 commit comments

Comments
 (0)