input)
+ {
+ return flattenMapProperties(input, false);
+ }
+
+ /**
+ * Converts a {@link Map} of objects to a flattened {@link Map} of {@link String} values.
+ *
+ * For example, with the given input:
+ *
+ *
+ * Map<String, Object> application = Map.of(
+ * "name", "My App",
+ * "prefixes", List.of(">", "$")
+ * );
+ *
+ * Map<String, Object> map = Map.of("application", application);
+ * Map<String, String> result = MapUtils.flattenMapProperties(map);
+ *
+ *
+ * Will result in the following properties, assuming indexed is false:
+ *
+ *
+ * application.name=My App
+ * application.prefixes=>,$
+ *
+ *
+ * If indexed is true, the result would be:
+ *
+ *
+ * application.name=My App
+ * application.prefixes[0]=>
+ * application.prefixes[1]=$
+ *
+ *
+ *
+ * @param input Map of properties that may contain nested Maps.
+ * @param indexed If arrays are converted to multiple properties, or a comma separated list.
+ * @param Type of values the {@link Map} contains.
+ * @return Map of all properties indexed by their fully qualified names.
+ */
+ public static Map flattenMapProperties(final Map input, final boolean indexed)
+ {
+ final Map result = new HashMap<>();
+ flattenMapProperties(input, result, indexed);
+ return result;
+ }
+
+ /**
+ * Calls {@link #flattenMapProperties(Map, Map, boolean, String)} with parameter prefix as null, since when we begin
+ * flattening the map, there is no prefix by default.
+ *
+ * @param input Map of properties that may contain nested Maps.
+ * @param output Map that all properties are added to.
+ * @param indexed If arrays are converted to multiple properties, or a comma separated list.
+ * @param Type of values the {@link Map} contains.
+ * @see #flattenMapProperties(Map, Map, boolean, String)
+ */
+ private static void flattenMapProperties(final Map input,
+ final Map output,
+ final boolean indexed)
+ {
+ flattenMapProperties(input, output, indexed, null);
+ }
+
+ /**
+ * @param input Map of properties that may contain nested Maps.
+ * @param output Map that all properties are added to.
+ * @param indexed If arrays are converted to multiple properties, or a comma separated list.
+ * @param prefix Name to prefix to any properties found on this level.
+ * @param Type of values the {@link Map} contains.
+ */
+ private static void flattenMapProperties(final Map input,
+ final Map output,
+ final boolean indexed,
+ final String prefix)
+ {
+ input.forEach((key, value) ->
+ {
+ if (value == null)
+ {
+ return;
+ }
+
+ final String k = (prefix == null) ? key : (prefix + '.' + key);
+
+ if (value instanceof Map)
+ {
+ flattenMapProperties((Map) value, output, indexed, k);
+ }
+ else if (value instanceof Iterable)
+ {
+ addIterable((Iterable) value, k, output, indexed);
+ }
+ else
+ {
+ output.put(k, (output.containsKey(k)) ? output.get(k) + "," + value : value.toString());
+ }
+ });
+ }
+
+ /**
+ * @param value Array of values that needs to be flattened.
+ * @param key Property name for this value.
+ * @param output Map that all properties are added to.
+ * @param indexed If arrays are converted to multiple properties, or a comma separated list.
+ * @param Type of values the {@link Map} contains.
+ */
+ private static void addIterable(final Iterable value,
+ final String key,
+ final Map output,
+ final boolean indexed)
+ {
+ final StringJoiner joiner = new StringJoiner(",");
+ int index = 0;
+
+ for (final Object o : value)
+ {
+ if (o instanceof Map)
+ {
+ final Map map = (Map) o;
+
+ if (map.isEmpty())
+ {
+ continue;
+ }
+
+ final String keyPrefix = (indexed) ? key + "[" + index++ + "]" : key;
+ flattenMapProperties((Map) map, output, indexed, keyPrefix);
+ }
+ else
+ {
+ joiner.add(o.toString());
+ }
+ }
+
+ if (joiner.length() > 0)
+ {
+ output.put(key, joiner.toString());
+ }
+ }
+}
diff --git a/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java b/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java
new file mode 100644
index 000000000..867abfb4f
--- /dev/null
+++ b/deltaspike/core/api/src/test/java/org/apache/deltaspike/core/util/MapUtilsTest.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.deltaspike.core.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MapUtilsTest
+{
+ /**
+ *
+ * application:
+ * name: MyApp
+ * database:
+ * host: 127.0.0.1
+ * username: seth
+ * password: password
+ *
+ */
+ @Test
+ public void testFlattenMap()
+ {
+ Map application = new HashMap<>();
+ application.put("name", "MyApp");
+
+ Map database = new HashMap<>();
+ database.put("host", "127.0.0.1");
+ database.put("username", "username");
+ database.put("password", "password");
+
+ Map map = new HashMap<>();
+ map.put("application", application);
+ map.put("database", database);
+
+ Map result = MapUtils.flattenMapProperties(map);
+
+ Assert.assertEquals(4, result.size());
+ Assert.assertEquals("MyApp", result.get("application.name"));
+ Assert.assertEquals("127.0.0.1", result.get("database.host"));
+ Assert.assertEquals("username", result.get("database.username"));
+ Assert.assertEquals("password", result.get("database.password"));
+ }
+
+ /**
+ *
+ * application:
+ * name: Another App
+ * database:
+ * host: localhost
+ * username: username
+ * password: password
+ *
+ */
+ @Test
+ public void testFlattenNestedMap()
+ {
+ Map database = new HashMap<>();
+ database.put("host", "localhost");
+ database.put("username", "username");
+ database.put("password", "password");
+
+ Map application = new HashMap<>();
+ application.put("name", "Another App");
+ application.put("database", database);
+
+ Map map = new HashMap<>();
+ map.put("application", application);
+
+ Map result = MapUtils.flattenMapProperties(map);
+
+ Assert.assertEquals(4, result.size());
+ Assert.assertEquals("Another App", result.get("application.name"));
+ Assert.assertEquals("localhost", result.get("application.database.host"));
+ Assert.assertEquals("username", result.get("application.database.username"));
+ Assert.assertEquals("password", result.get("application.database.password"));
+ }
+
+ /**
+ *
+ * application:
+ * name: Yet Another App
+ * prefixes:
+ * - >
+ * - $
+ *
+ */
+ @Test
+ public void testFlattenWithList()
+ {
+ List prefixes = new ArrayList<>();
+ prefixes.add(">");
+ prefixes.add("$");
+
+ Map application = new HashMap<>();
+ application.put("name", "Yet Another App");
+ application.put("prefixes", prefixes);
+
+ Map map = new HashMap<>();
+ map.put("application", application);
+
+ Map result = MapUtils.flattenMapProperties(map);
+
+ Assert.assertEquals(2, result.size());
+ Assert.assertEquals("Yet Another App", result.get("application.name"));
+ Assert.assertEquals(">,$", result.get("application.prefixes"));
+ }
+
+ /**
+ *
+ * application:
+ * name: More Apps
+ * messages:
+ * - source: one
+ * target: two
+ * - source: three
+ * target: four
+ *
+ */
+ @Test
+ public void testFlattenWithObjectArrayLists()
+ {
+ Map message1 = new HashMap<>();
+ message1.put("source", "one");
+ message1.put("target", "two");
+
+ Map message2 = new HashMap<>();
+ message2.put("source", "three");
+ message2.put("target", "four");
+
+ List