This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git
The following commit(s) were added to refs/heads/master by this push:
new b284bd7 SLING-12488 Always emit properties in alphabetical order of
their keys
b284bd7 is described below
commit b284bd7bbaae38d3c7f2cca40a5297d54ce73299
Author: Konrad Windszus <[email protected]>
AuthorDate: Fri Nov 15 18:07:19 2024 +0100
SLING-12488 Always emit properties in alphabetical order of their keys
---
.../ConfigurationSerializerWebConsolePlugin.java | 4 +-
.../configuration/impl/SortedDictionary.java | 113 +++++++++++++++++++++
.../configuration/impl/SortedDictionaryTest.java | 84 +++++++++++++++
3 files changed, 200 insertions(+), 1 deletion(-)
diff --git
a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
index 46ce0b8..7eb8ecc 100644
---
a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
+++
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java
@@ -258,7 +258,9 @@ public class ConfigurationSerializerWebConsolePlugin
extends GenericServlet {
if (removeMergedDefaultProperties) {
ConfigUtil.removeRedundantProperties(properties,
mergedProperties);
}
-
ConfigurationSerializerFactory.create(serializationFormat).serialize(properties,
baos);
+ // always emit in alphabetical order of keys
+ ConfigurationSerializerFactory.create(serializationFormat)
+ .serialize(new SortedDictionary<>(properties),
baos);
pw.println("<textarea rows=\"20\" cols=\"120\"
id=\"output\" readonly>");
pw.print(new String(baos.toByteArray(),
StandardCharsets.UTF_8));
pw.println("</textarea>");
diff --git
a/src/main/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionary.java
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionary.java
new file mode 100644
index 0000000..4878be1
--- /dev/null
+++
b/src/main/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionary.java
@@ -0,0 +1,113 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Objects;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * A sorted dictionary is a view on an existing {@link Dictionary} that is
sorted by its keys
+ * in natural ordering.
+ * This is just a view on top of the delegate dictionary. All write operations
modify
+ * the underlying delegate dictionary.
+ * @param <K> the type of the keys
+ * @param <V> the type of the values
+ */
+public class SortedDictionary<K, V> extends Dictionary<K, V> {
+
+ private final Dictionary<K, V> delegate;
+
+ public SortedDictionary(Dictionary<K, V> delegate) {
+ this.delegate = delegate;
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(delegate);
+ }
+
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public Enumeration<K> keys() {
+ return sortedEnumeration(delegate.keys());
+ }
+
+ public Enumeration<V> elements() {
+ // this needs to be sorted by keys
+ Enumeration<K> sortedKeys = keys();
+ Collection<V> sortedValues = new ArrayList<V>();
+ while (sortedKeys.hasMoreElements()) {
+ K key = sortedKeys.nextElement();
+ sortedValues.add(delegate.get(key));
+ }
+ return Collections.enumeration(sortedValues);
+ }
+
+ public V get(Object key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ SortedDictionary other = (SortedDictionary) obj;
+ return Objects.equals(delegate, other.delegate);
+ }
+
+ public V put(K key, V value) {
+ return delegate.put(key, value);
+ }
+
+ public V remove(Object key) {
+ return delegate.remove(key);
+ }
+
+ public String toString() {
+ return delegate.toString();
+ }
+
+ /**
+ * Returns an sorted enumeration of the given enumeration, sorted
according to the natural ordering of the elements.
+ * For Strings, this is the lexicographic order.
+ *
+ * @param enumeration the enumeration to sort
+ * @return the sorted enumeration
+ */
+ private static <T> Enumeration<T> sortedEnumeration(Enumeration<T>
enumeration) {
+ SortedSet<T> sortedSet = new TreeSet<>();
+ while (enumeration.hasMoreElements()) {
+ sortedSet.add(enumeration.nextElement());
+ }
+ return Collections.enumeration(sortedSet);
+ }
+}
diff --git
a/src/test/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionaryTest.java
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionaryTest.java
new file mode 100644
index 0000000..9c2cbd5
--- /dev/null
+++
b/src/test/java/org/apache/sling/installer/factories/configuration/impl/SortedDictionaryTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.sling.installer.factories.configuration.impl;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class SortedDictionaryTest {
+
+ @Test
+ public void testKeysAndElements() {
+ Dictionary<String, Object> dictionary = new Hashtable<>();
+ dictionary.put("Z", "z");
+ dictionary.put("A", "a");
+ dictionary.put("B", "b");
+ SortedDictionary<String, Object> sortedDictionary = new
SortedDictionary<>(dictionary);
+ assertArrayEquals(
+ new String[] {"A", "B", "Z"},
+ Collections.list(sortedDictionary.keys()).toArray(new String[]
{}));
+ assertArrayEquals(
+ new String[] {"a", "b", "z"},
+ Collections.list(sortedDictionary.elements()).toArray(new
String[] {}));
+ }
+
+ @Test
+ public void testPutGetRemove() {
+ Dictionary<String, Object> dictionary = new Hashtable<>();
+ Dictionary<String, Object> sortedDictionary = new
SortedDictionary<>(dictionary);
+ sortedDictionary.put("foo", "bar");
+ assertEquals("bar", sortedDictionary.get("foo"));
+ assertEquals("bar", dictionary.get("foo"));
+ sortedDictionary.remove("foo");
+ assertNull(sortedDictionary.get("foo"));
+ assertNull(dictionary.get("foo"));
+ }
+
+ @Test
+ public void testDelegatedGetters() {
+ Dictionary<String, Object> dictionary = new Hashtable<>();
+ dictionary.put("foo", "bar");
+ dictionary.put("bar", "baz");
+ Dictionary<String, Object> sortedDictionary = new
SortedDictionary<>(dictionary);
+ assertEquals(dictionary.size(), sortedDictionary.size());
+ assertEquals(dictionary.isEmpty(), sortedDictionary.isEmpty());
+ assertEquals(dictionary.toString(), sortedDictionary.toString());
+ }
+
+ @Test
+ public void testEqualsAndHashcode() {
+ Dictionary<String, Object> dictionary = new Hashtable<>();
+ dictionary.put("foo", "bar");
+ dictionary.put("bar", "baz");
+ Dictionary<String, Object> sortedDictionary = new
SortedDictionary<>(dictionary);
+ Dictionary<String, Object> dictionary2 = new Hashtable<>();
+ dictionary2.put("foo", "bar");
+ dictionary2.put("bar", "baz");
+ Dictionary<String, Object> sortedDictionary2 = new
SortedDictionary<>(dictionary2);
+ assertEquals(sortedDictionary, sortedDictionary2);
+ assertEquals(sortedDictionary.hashCode(),
sortedDictionary2.hashCode());
+ }
+}