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());
+    }
+}

Reply via email to