diff --git a/changes.xml b/changes.xml index 26fc3e5..10ad615 100644 --- a/changes.xml +++ b/changes.xml @@ -23,6 +23,15 @@ xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd"> + + + ImmutableValueMap.of: Retain order of items as stated in JavaDoc. + + + ImmutableValueMap.copyOf: Ensure map is immutable. + + + Switch to Java 11 as minimum version. diff --git a/pom.xml b/pom.xml index c5b62a8..c97c490 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ io.wcm io.wcm.sling.commons - 1.6.2 + 1.6.4 jar Sling Commons @@ -49,7 +49,7 @@ sling/commons - 2023-04-19T11:06:55Z + 2023-07-03T09:55:00Z diff --git a/src/main/java/io/wcm/sling/commons/resource/ImmutableValueMap.java b/src/main/java/io/wcm/sling/commons/resource/ImmutableValueMap.java index 11ca568..7b503d0 100644 --- a/src/main/java/io/wcm/sling/commons/resource/ImmutableValueMap.java +++ b/src/main/java/io/wcm/sling/commons/resource/ImmutableValueMap.java @@ -21,7 +21,7 @@ import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -185,9 +185,10 @@ public void clear() { * @param v1 Value 1 * @return ImmutableValueMap */ - @SuppressWarnings("null") public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1) { - return new ImmutableValueMap(Map.of(k1, v1)); + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } /** @@ -199,10 +200,12 @@ public void clear() { * @return ImmutableValueMap * @throws IllegalArgumentException if duplicate keys are provided */ - @SuppressWarnings("null") public static @NotNull ImmutableValueMap of(@NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2) { - return new ImmutableValueMap(Map.of(k1, v1, k2, v2)); + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + map.put(k2, v2); + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } /** @@ -216,12 +219,15 @@ public void clear() { * @return ImmutableValueMap * @throws IllegalArgumentException if duplicate keys are provided */ - @SuppressWarnings("null") public static @NotNull ImmutableValueMap of( @NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2, @NotNull String k3, @NotNull Object v3) { - return new ImmutableValueMap(Map.of(k1, v1, k2, v2, k3, v3)); + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + map.put(k2, v2); + map.put(k3, v3); + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } /** @@ -237,13 +243,18 @@ public void clear() { * @return ImmutableValueMap * @throws IllegalArgumentException if duplicate keys are provided */ - @SuppressWarnings({ "null", "java:S107", "PMD.UseObjectForClearerAPI" }) + @SuppressWarnings({ "java:S107", "PMD.UseObjectForClearerAPI" }) public static @NotNull ImmutableValueMap of( @NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2, @NotNull String k3, @NotNull Object v3, @NotNull String k4, @NotNull Object v4) { - return new ImmutableValueMap(Map.of(k1, v1, k2, v2, k3, v3, k4, v4)); + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + map.put(k2, v2); + map.put(k3, v3); + map.put(k4, v4); + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } /** @@ -261,14 +272,20 @@ public void clear() { * @return ImmutableValueMap * @throws IllegalArgumentException if duplicate keys are provided */ - @SuppressWarnings({ "null", "java:S107", "PMD.UseObjectForClearerAPI" }) + @SuppressWarnings({ "java:S107", "PMD.UseObjectForClearerAPI" }) public static ImmutableValueMap of( @NotNull String k1, @NotNull Object v1, @NotNull String k2, @NotNull Object v2, @NotNull String k3, @NotNull Object v3, @NotNull String k4, @NotNull Object v4, @NotNull String k5, @NotNull Object v5) { - return new ImmutableValueMap(Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5)); + Map map = new LinkedHashMap<>(); + map.put(k1, v1); + map.put(k2, v2); + map.put(k3, v3); + map.put(k4, v4); + map.put(k5, v5); + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } // looking for of() with > 5 entries? Use the builder instead. @@ -296,12 +313,7 @@ public static ImmutableValueMap of( * @throws NullPointerException if any key or value in {@code map} is null */ public static @NotNull ImmutableValueMap copyOf(@NotNull Map map) { - if (map instanceof ValueMap) { - return new ImmutableValueMap((ValueMap)map); - } - else { - return new ImmutableValueMap(map); - } + return new ImmutableValueMap(Collections.unmodifiableMap(map)); } /** @@ -309,7 +321,7 @@ public static ImmutableValueMap of( */ public static final class Builder { - private final @NotNull Map map = new HashMap<>(); + private final @NotNull Map map = new LinkedHashMap<>(); /** * Associates {@code key} with {@code value} in the built map. Duplicate diff --git a/src/test/java/io/wcm/sling/commons/resource/ImmutableValueMapTest.java b/src/test/java/io/wcm/sling/commons/resource/ImmutableValueMapTest.java index f8a8409..e84c70e 100644 --- a/src/test/java/io/wcm/sling/commons/resource/ImmutableValueMapTest.java +++ b/src/test/java/io/wcm/sling/commons/resource/ImmutableValueMapTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Iterator; import java.util.Map; import org.apache.sling.api.resource.ValueMap; @@ -107,46 +108,31 @@ void testOf() { @Test void testOfx1() { ValueMap map = ImmutableValueMap.of("p1", "v1"); - assertEquals(1, map.size()); - assertEquals("v1", map.get("p1")); + assertWithOrder(map, "p1", "v1"); } @Test void testOfx2() { - ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2"); - assertEquals(2, map.size()); - assertEquals("v1", map.get("p1")); - assertEquals("v2", map.get("p2")); + ValueMap map = ImmutableValueMap.of("p2", "v2", "p1", "v1"); + assertWithOrder(map, "p2", "v2", "p1", "v1"); } @Test void testOfx3() { ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3"); - assertEquals(3, map.size()); - assertEquals("v1", map.get("p1")); - assertEquals("v2", map.get("p2")); - assertEquals("v3", map.get("p3")); + assertWithOrder(map, "p1", "v1", "p2", "v2", "p3", "v3"); } @Test void testOfx4() { - ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3", "p4", "v4"); - assertEquals(4, map.size()); - assertEquals("v1", map.get("p1")); - assertEquals("v2", map.get("p2")); - assertEquals("v3", map.get("p3")); - assertEquals("v4", map.get("p4")); + ValueMap map = ImmutableValueMap.of("p3", "v3", "p2", "v2", "p1", "v1", "p4", "v4"); + assertWithOrder(map, "p3", "v3", "p2", "v2", "p1", "v1", "p4", "v4"); } @Test void testOfx5() { - ValueMap map = ImmutableValueMap.of("p1", "v1", "p2", "v2", "p3", "v3", "p4", "v4", "p5", "v5"); - assertEquals(5, map.size()); - assertEquals("v1", map.get("p1")); - assertEquals("v2", map.get("p2")); - assertEquals("v3", map.get("p3")); - assertEquals("v4", map.get("p4")); - assertEquals("v5", map.get("p5")); + ValueMap map = ImmutableValueMap.of("p1", "v1", "p5", "v5", "p3", "v3", "p4", "v4", "p2", "v2"); + assertWithOrder(map, "p1", "v1", "p5", "v5", "p3", "v3", "p4", "v4", "p2", "v2"); } @Test @@ -211,4 +197,17 @@ void testToString() { assertEquals("{}", ImmutableValueMap.of().toString()); } + private void assertWithOrder(Map map, Object... items) { + int count = items.length / 2; + assertEquals(count, map.size(), "map size"); + Iterator> entries = map.entrySet().iterator(); + for (int i = 0; i < items.length - 1; i = i + 2) { + Object key = items[i]; + Object value = items[i + 1]; + Map.Entry entry = entries.next(); + assertEquals(key, entry.getKey(), "Key Entry #" + (i / 2)); + assertEquals(value, entry.getValue(), "Value Entry #" + (i / 2)); + } + } + }