From 87f1512e8867a884615d79ab52bf1283f7c573e1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 2 Dec 2014 17:14:32 -0800 Subject: [PATCH] Add protected YamlProcessor.getFlattenedMap method Add a protected getFlattenedMap method to the YamlProcessor that subclasses can use to flatten a source Map so that it has the same entries as the Properties, but retains order. Issue: SPR-12499 --- .../beans/factory/config/YamlProcessor.java | 29 ++++++++++++++----- .../factory/config/YamlProcessorTests.java | 22 +++++++++++++- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java index 1b3d2cb201..ef04e79d2d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java @@ -212,7 +212,7 @@ public abstract class YamlProcessor { private boolean process(Map map, MatchCallback callback) { Properties properties = new Properties(); - assignProperties(properties, map, null); + properties.putAll(getFlattenedMap(map)); if (this.documentMatchers.isEmpty()) { if (this.logger.isDebugEnabled()) { @@ -247,8 +247,23 @@ public abstract class YamlProcessor { return false; } - private void assignProperties(Properties properties, Map input, String path) { - for (Entry entry : input.entrySet()) { + /** + * Return a flattened version of the given map, recursively following any nested Map + * or Collection values. Entries from the resulting map retain the same order as the + * source. When called with the Map from a {@link MatchCallback} the result will + * contain the same values as the {@link MatchCallback} Properties. + * @param source the source map + * @return a flattened map + * @since 4.2.3 + */ + protected final Map getFlattenedMap(Map source) { + Map result = new LinkedHashMap(); + buildFlattenedMap(result, source, null); + return result; + } + + private void buildFlattenedMap(Map result, Map source, String path) { + for (Entry entry : source.entrySet()) { String key = entry.getKey(); if (StringUtils.hasText(path)) { if (key.startsWith("[")) { @@ -260,13 +275,13 @@ public abstract class YamlProcessor { } Object value = entry.getValue(); if (value instanceof String) { - properties.put(key, value); + result.put(key, value); } else if (value instanceof Map) { // Need a compound key @SuppressWarnings("unchecked") Map map = (Map) value; - assignProperties(properties, map, key); + buildFlattenedMap(result, map, key); } else if (value instanceof Collection) { // Need a compound key @@ -274,12 +289,12 @@ public abstract class YamlProcessor { Collection collection = (Collection) value; int count = 0; for (Object object : collection) { - assignProperties(properties, + buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key); } } else { - properties.put(key, value == null ? "" : value); + result.put(key, value == null ? "" : value); } } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java index 02a535ea67..fd90fbeb66 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java @@ -15,6 +15,7 @@ */ package org.springframework.beans.factory.config; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @@ -23,7 +24,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.yaml.snakeyaml.parser.ParserException; import org.yaml.snakeyaml.scanner.ScannerException; - import org.springframework.core.io.ByteArrayResource; import static org.junit.Assert.*; @@ -135,4 +135,24 @@ public class YamlProcessorTests { } }); } + + @Test + @SuppressWarnings("unchecked") + public void flattenedMapIsSameAsPropertiesButOrdered() { + this.processor.setResources(new ByteArrayResource( + "foo: bar\nbar:\n spam: bucket".getBytes())); + this.processor.process(new MatchCallback() { + @Override + public void process(Properties properties, Map map) { + assertEquals("bucket", properties.get("bar.spam")); + assertEquals(2, properties.size()); + Map flattenedMap = processor.getFlattenedMap(map); + assertEquals("bucket", flattenedMap.get("bar.spam")); + assertEquals(2, flattenedMap.size()); + assertTrue(flattenedMap instanceof LinkedHashMap); + Map bar = (Map) map.get("bar"); + assertEquals("bucket", bar.get("spam")); + } + }); + } }