This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git


The following commit(s) were added to refs/heads/master by this push:
     new 415a24f56 SortedProperties doesn't always process keys in sorted 
sequence
415a24f56 is described below

commit 415a24f56dd24ad49538f3116f50059e67a1e852
Author: Gary D. Gregory <[email protected]>
AuthorDate: Sat Oct 11 07:46:35 2025 -0400

    SortedProperties doesn't always process keys in sorted sequence
    
    - Fix SortedProperties.keySet() returned an unsorted Set.
    - Fix SortedProperties.propertyNames() returned an unsorted Enumeration.
    - Fix SortedProperties.stringPropertyNames() returned an unsorted Set.
    - Fix SortedProperties.forEach() called its consumer out of order.
---
 src/changes/changes.xml                            |   6 +-
 .../collections4/properties/SortedProperties.java  |  43 ++++
 .../properties/SortedPropertiesTest.java           | 258 +++++++++++++++++++++
 3 files changed, 306 insertions(+), 1 deletion(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d3b79e20b..0e99416f8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -35,7 +35,11 @@
     <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Joerg 
Budischewski" issue="COLLECTIONS-838">Calling SetUtils.union on multiple 
instances of SetView causes JVM to hang</action>
     <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Joerg 
Budischewski" issue="COLLECTIONS-722">Improve IteratorUtils.chainedIterator() 
performance.</action>
     <action type="fix" dev="ggregory" due-to="Gary Gregory, PMD">Fix 
UselessOverridingMethod in 
org.apache.commons.collections4.properties.PropertiesFactory.</action>
-    <action type="fix" dev="ggregory" due-to="Adam Rauch, Gary Gregory" 
issue="COLLECTIONS-693">OrderedProperties.stringPropertyNames() returns an 
unordered set.</action>
+    <action type="fix" dev="ggregory" due-to="Adam Rauch, Gary Gregory" 
issue="COLLECTIONS-693">OrderedProperties.stringPropertyNames() returns an 
unordered Set.</action>
+    <action type="fix" dev="ggregory" due-to="Gary Gregory" >Fix 
SortedProperties.keySet() returned an unsorted Set.</action>
+    <action type="fix" dev="ggregory" due-to="Gary Gregory" >Fix 
SortedProperties.propertyNames() returned an unsorted Enumeration.</action>
+    <action type="fix" dev="ggregory" due-to="Gary Gregory" >Fix 
SortedProperties.stringPropertyNames() returned an unsorted Set.</action>
+    <action type="fix" dev="ggregory" due-to="Gary Gregory" >Fix 
SortedProperties.forEach() called its consumer out of order.</action>
     <!-- ADD -->
     <action type="add" dev="ggregory" due-to="Gary Gregory">Add generics to 
UnmodifiableIterator for the wrapped type.</action>
     <!-- UPDATE -->
diff --git 
a/src/main/java/org/apache/commons/collections4/properties/SortedProperties.java
 
b/src/main/java/org/apache/commons/collections4/properties/SortedProperties.java
index fd950e8e1..02a6f7890 100644
--- 
a/src/main/java/org/apache/commons/collections4/properties/SortedProperties.java
+++ 
b/src/main/java/org/apache/commons/collections4/properties/SortedProperties.java
@@ -19,11 +19,14 @@ package org.apache.commons.collections4.properties;
 
 import java.util.AbstractMap;
 import java.util.AbstractMap.SimpleEntry;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -55,12 +58,52 @@ public class SortedProperties extends Properties {
         return stream.collect(Collectors.toCollection(LinkedHashSet::new));
     }
 
+    /**
+     * Enumerates all key/value pairs in the specified TreeSet and omits the 
property if the key or value is not a string.
+     *
+     * @param result The result set to populate.
+     * @return The given set.
+     */
+    private synchronized TreeSet<String> enumerateStringProperties(final 
TreeSet<String> result) {
+        if (defaults != null) {
+            result.addAll(defaults.stringPropertyNames());
+        }
+        for (final Enumeration<?> e = keys(); e.hasMoreElements();) {
+            final Object k = e.nextElement();
+            final Object v = get(k);
+            if (k instanceof String && v instanceof String) {
+                result.add((String) k);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public synchronized void forEach(BiConsumer<? super Object, ? super 
Object> action) {
+        keySet().stream().forEach(k -> action.accept(k, get(k)));
+    }
+
     @Override
     public synchronized Enumeration<Object> keys() {
         return new 
IteratorEnumeration<>(sortedKeys().collect(Collectors.toList()).iterator());
     }
 
+    @Override
+    public Set<Object> keySet() {
+        return new TreeSet<>(super.keySet());
+    }
+
+    @Override
+    public Enumeration<?> propertyNames() {
+        return Collections.enumeration(keySet());
+    }
+
     private Stream<String> sortedKeys() {
         return keySet().stream().map(Object::toString).sorted();
     }
+
+    @Override
+    public Set<String> stringPropertyNames() {
+        return enumerateStringProperties(new TreeSet<>());
+    }
 }
diff --git 
a/src/test/java/org/apache/commons/collections4/properties/SortedPropertiesTest.java
 
b/src/test/java/org/apache/commons/collections4/properties/SortedPropertiesTest.java
index 9682f208a..7d25f9234 100644
--- 
a/src/test/java/org/apache/commons/collections4/properties/SortedPropertiesTest.java
+++ 
b/src/test/java/org/apache/commons/collections4/properties/SortedPropertiesTest.java
@@ -17,17 +17,116 @@
 package org.apache.commons.collections4.properties;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.junit.jupiter.api.Test;
 
 class SortedPropertiesTest {
 
+    private SortedProperties assertAscendingOrder(final SortedProperties 
sortedProperties) {
+        final String[] keys = { "1", "10", "11", "2", "3", "4", "5", "6", "7", 
"8", "9" };
+        final Enumeration<Object> enumObjects = sortedProperties.keys();
+        for (int i = 0; i < keys.length; i++) {
+            assertEquals("key" + keys[i], enumObjects.nextElement());
+        }
+        final Iterator<Object> iterSet = sortedProperties.keySet().iterator();
+        for (int i = 0; i < keys.length; i++) {
+            assertEquals("key" + keys[i], iterSet.next());
+        }
+        final Iterator<Entry<Object, Object>> iterEntrySet = 
sortedProperties.entrySet().iterator();
+        for (int i = 0; i < keys.length; i++) {
+            final Entry<Object, Object> next = iterEntrySet.next();
+            assertEquals("key" + keys[i], next.getKey());
+            assertEquals("value" + keys[i], next.getValue());
+        }
+        final Enumeration<?> propertyNames = sortedProperties.propertyNames();
+        for (int i = 0; i < keys.length; i++) {
+            assertEquals("key" + keys[i], propertyNames.nextElement());
+        }
+        final Set<String> propertyNameSet = 
sortedProperties.stringPropertyNames();
+        final AtomicInteger i = new AtomicInteger(0);
+        propertyNameSet.forEach(e -> assertEquals("key" + 
keys[i.getAndIncrement()], e));
+        return sortedProperties;
+    }
+
+    private SortedProperties loadOrderedKeysReverse() throws 
FileNotFoundException, IOException {
+        final SortedProperties sortedProperties = new SortedProperties();
+        try (FileReader reader = new 
FileReader("src/test/resources/org/apache/commons/collections4/properties/test-reverse.properties"))
 {
+            sortedProperties.load(reader);
+        }
+        return assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testCompute() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            final AtomicInteger aInt = new AtomicInteger(i);
+            sortedProperties.compute("key" + i, (k, v) -> "value" + 
aInt.get());
+        }
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            final AtomicInteger aInt = new AtomicInteger(i);
+            sortedProperties.compute("key" + i, (k, v) -> "value" + 
aInt.get());
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testComputeIfAbsent() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            final AtomicInteger aInt = new AtomicInteger(i);
+            sortedProperties.computeIfAbsent("key" + i, k -> "value" + 
aInt.get());
+        }
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            final AtomicInteger aInt = new AtomicInteger(i);
+            sortedProperties.computeIfAbsent("key" + i, k -> "value" + 
aInt.get());
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
     @Test
     void testEntrySet() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        final char first = 'Z';
+        final char last = 'A';
+        for (char ch = first; ch >= last; ch--) {
+            sortedProperties.put(String.valueOf(ch), "Value" + ch);
+        }
+        final Iterator<Map.Entry<Object, Object>> entries = 
sortedProperties.entrySet().iterator();
+        for (char ch = first; ch <= last; ch++) {
+            final Map.Entry<Object, Object> entry = entries.next();
+            assertEquals(String.valueOf(ch), entry.getKey());
+            assertEquals("Value" + ch, entry.getValue());
+        }
+    }
+
+    @Test
+    void testEntrySet2() {
         final SortedProperties sortedProperties = new SortedProperties();
         for (char ch = 'Z'; ch >= 'A'; ch--) {
             sortedProperties.put(String.valueOf(ch), "Value" + ch);
@@ -40,8 +139,38 @@ class SortedPropertiesTest {
         }
     }
 
+    @Test
+    void testForEach() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        final char first = 'Z';
+        final char last = 'A';
+        for (char ch = first; ch >= last; ch--) {
+            sortedProperties.put(String.valueOf(ch), "Value" + ch);
+        }
+        final AtomicInteger aCh = new AtomicInteger(last);
+        sortedProperties.forEach((k, v) -> {
+            final char ch = (char) aCh.getAndIncrement();
+            assertEquals(String.valueOf(ch), k);
+            assertEquals("Value" + ch, v);
+        });
+    }
+
     @Test
     void testKeys() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        final char first = 'Z';
+        final char last = 'A';
+        for (char ch = first; ch >= last; ch--) {
+            sortedProperties.put(String.valueOf(ch), "Value" + ch);
+        }
+        final Enumeration<Object> keys = sortedProperties.keys();
+        for (char ch = first; ch <= last; ch++) {
+            assertEquals(String.valueOf(ch), keys.nextElement());
+        }
+    }
+
+    @Test
+    void testKeys2() {
         final SortedProperties sortedProperties = new SortedProperties();
         for (char ch = 'Z'; ch >= 'A'; ch--) {
             sortedProperties.put(String.valueOf(ch), "Value" + ch);
@@ -52,4 +181,133 @@ class SortedPropertiesTest {
         }
     }
 
+    @Test
+    void testLoadOrderedKeys() throws IOException {
+        final SortedProperties sortedProperties = new SortedProperties();
+        try (FileReader reader = new 
FileReader("src/test/resources/org/apache/commons/collections4/properties/test.properties"))
 {
+            sortedProperties.load(reader);
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testLoadOrderedKeysReverse() throws IOException {
+        loadOrderedKeysReverse();
+    }
+
+    @Test
+    void testMerge() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            sortedProperties.merge("key" + i, "value" + i, (k, v) -> v);
+        }
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            sortedProperties.merge("key" + i, "value" + i, (k, v) -> v);
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testPut() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            sortedProperties.put("key" + i, "value" + i);
+        }
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            sortedProperties.put("key" + i, "value" + i);
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testPutAll() {
+        final SortedProperties sourceProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            sourceProperties.put("key" + i, "value" + i);
+        }
+        final SortedProperties sortedProperties = new SortedProperties();
+        sortedProperties.putAll(sourceProperties);
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            sortedProperties.put("key" + i, "value" + i);
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testPutIfAbsent() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        int first = 1;
+        int last = 11;
+        for (int i = first; i <= last; i++) {
+            sortedProperties.putIfAbsent("key" + i, "value" + i);
+        }
+        assertAscendingOrder(sortedProperties);
+        sortedProperties.clear();
+        first = 11;
+        last = 1;
+        for (int i = first; i >= last; i--) {
+            sortedProperties.putIfAbsent("key" + i, "value" + i);
+        }
+        assertAscendingOrder(sortedProperties);
+    }
+
+    @Test
+    void testRemoveKey() throws FileNotFoundException, IOException {
+        final SortedProperties props = loadOrderedKeysReverse();
+        final String k = "key1";
+        props.remove(k);
+        assertFalse(props.contains(k));
+        assertFalse(props.containsKey(k));
+        assertFalse(Collections.list(props.keys()).contains(k));
+        assertFalse(Collections.list(props.propertyNames()).contains(k));
+    }
+
+    @Test
+    void testRemoveKeyValue() throws FileNotFoundException, IOException {
+        final SortedProperties props = loadOrderedKeysReverse();
+        final String k = "key1";
+        props.remove(k, "value1");
+        assertFalse(props.contains(k));
+        assertFalse(props.containsKey(k));
+        assertFalse(Collections.list(props.keys()).contains(k));
+        assertFalse(Collections.list(props.propertyNames()).contains(k));
+    }
+
+    @Test
+    void testStringPropertyName() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        assertTrue(sortedProperties.stringPropertyNames().isEmpty());
+    }
+
+    @Test
+    void testToString() {
+        final SortedProperties sortedProperties = new SortedProperties();
+        final char first = 'Z';
+        final char last = 'A';
+        for (char ch = first; ch >= last; ch--) {
+            sortedProperties.put(String.valueOf(ch), "Value" + ch);
+        }
+        assertEquals(
+                "{A=ValueA, B=ValueB, C=ValueC, D=ValueD, E=ValueE, F=ValueF, 
G=ValueG, H=ValueH, I=ValueI, J=ValueJ, K=ValueK, L=ValueL, M=ValueM, N=ValueN, 
O=ValueO, P=ValueP, Q=ValueQ, R=ValueR, S=ValueS, T=ValueT, U=ValueU, V=ValueV, 
W=ValueW, X=ValueX, Y=ValueY, Z=ValueZ}",
+                sortedProperties.toString());
+    }
+
 }

Reply via email to