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