Add org.apache.commons.collections4.properties.OrderedProperties and

OrderedPropertiesFactory
This commit is contained in:
Gary Gregory 2023-07-13 08:49:52 -04:00
parent d3dbdc80d4
commit 452533ab6d
8 changed files with 510 additions and 5 deletions

View File

@ -647,7 +647,7 @@
<suppressionsLocation>${basedir}/src/conf/checkstyle-suppressions.xml</suppressionsLocation>
<enableRulesSummary>false</enableRulesSummary>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<resourceExcludes>NOTICE.txt,LICENSE.txt,**/pom.properties,**/test.properties,**/resolver-status.properties</resourceExcludes>
<resourceExcludes>NOTICE.txt,LICENSE.txt,**/pom.properties,**/test*.properties,**/resolver-status.properties</resourceExcludes>
</configuration>
</plugin>
<plugin>

View File

@ -270,6 +270,9 @@
<action issue="COLLECTIONS-746" dev="ggregory" type="add" due-to="Gary Gregory">
Add PropertiesFactory.
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add org.apache.commons.collections4.properties.OrderedProperties and OrderedPropertiesFactory.
</action>
<!-- UPDATE -->
<action dev="ggregory" type="update" due-to="Gary Gregory, Dependabot">
Bump org.easymock:easymock from 4.0.2 to 5.1.0 #352, #355, #375.

View File

@ -0,0 +1,133 @@
/*
* 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.commons.collections4.properties;
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.function.BiFunction;
import java.util.function.Function;
/**
* A drop-in replacement for {@link Properties} for ordered keys.
* <p>
* Overrides methods to keep keys in insertion order. Allows other methods in the superclass to work with ordered keys.
* </p>
*
* @see OrderedPropertiesFactory#INSTANCE
* @since 4.5
*/
public class OrderedProperties extends Properties {
private static final long serialVersionUID = 1L;
/**
* Preserves the insertion order.
*/
private final LinkedHashSet<Object> orderedKeys = new LinkedHashSet<>();
@Override
public synchronized void clear() {
orderedKeys.clear();
super.clear();
}
@Override
public synchronized Object compute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
Object compute = super.compute(key, remappingFunction);
if (compute != null) {
orderedKeys.add(key);
}
return compute;
}
@Override
public synchronized Object computeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
Object computeIfAbsent = super.computeIfAbsent(key, mappingFunction);
if (computeIfAbsent != null) {
orderedKeys.add(key);
}
return computeIfAbsent;
}
@Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(orderedKeys);
}
@Override
public Set<Object> keySet() {
return orderedKeys;
}
@Override
public synchronized Object merge(final Object key, final Object value,
final BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
orderedKeys.add(key);
return super.merge(key, value, remappingFunction);
}
@Override
public Enumeration<?> propertyNames() {
return Collections.enumeration(orderedKeys);
}
@Override
public synchronized Object put(final Object key, final Object value) {
Object put = super.put(key, value);
if (put == null) {
orderedKeys.add(key);
}
return put;
}
@Override
public synchronized void putAll(final Map<? extends Object, ? extends Object> t) {
orderedKeys.addAll(t.keySet());
super.putAll(t);
}
@Override
public synchronized Object putIfAbsent(final Object key, final Object value) {
Object putIfAbsent = super.putIfAbsent(key, value);
if (putIfAbsent == null) {
orderedKeys.add(key);
}
return putIfAbsent;
}
@Override
public synchronized Object remove(final Object key) {
Object remove = super.remove(key);
if (remove != null) {
orderedKeys.remove(key);
}
return remove;
}
@Override
public synchronized boolean remove(final Object key, final Object value) {
boolean remove = super.remove(key, value);
if (remove) {
orderedKeys.remove(key);
}
return remove;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.commons.collections4.properties;
/**
* Creates and loads {@link OrderedProperties}.
*
* @see OrderedProperties
* @since 4.5
*/
public class OrderedPropertiesFactory extends AbstractPropertiesFactory<OrderedProperties> {
/**
* The singleton instance.
*/
public static final OrderedPropertiesFactory INSTANCE = new OrderedPropertiesFactory();
/**
* Constructs an instance.
*/
private OrderedPropertiesFactory() {
// There is only one instance.
}
/**
* Subclasses override to provide customized properties instances.
*
* @return a new Properties instance.
*/
@Override
protected OrderedProperties createProperties() {
return new OrderedProperties();
}
}

View File

@ -64,14 +64,14 @@ public abstract class AbstractPropertiesFactoryTest<T extends Properties> {
assertEquals("value11", properties.getProperty("key11"));
}
private boolean isXmlTest(final String fileExtension) {
return ".xml".equals(fileExtension);
}
private String getPathString(final String fileExtension) {
return BulkTest.TEST_PROPERTIES_PATH + "test" + fileExtension;
}
private boolean isXmlTest(final String fileExtension) {
return ".xml".equals(fileExtension);
}
@Test
public void testInstance() {
assertNotNull(PropertiesFactory.INSTANCE);

View File

@ -0,0 +1,38 @@
/*
* 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.commons.collections4.properties;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
/**
* Tests {@link OrderedPropertiesFactory}.
*/
public class OrderedPropertiesFactoryTest extends AbstractPropertiesFactoryTest<OrderedProperties> {
public OrderedPropertiesFactoryTest() {
super(OrderedPropertiesFactory.INSTANCE);
}
@Test
@Override
public void testInstance() {
assertNotNull(OrderedPropertiesFactory.INSTANCE);
}
}

View File

@ -0,0 +1,256 @@
/*
* 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.commons.collections4.properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
/**
* Tests {@link OrderedProperties}.
*/
public class OrderedPropertiesTest {
private void assertAscendingOrder(final OrderedProperties orderedProperties) {
final int first = 1;
final int last = 11;
final Enumeration<Object> enumObjects = orderedProperties.keys();
for (int i = first; i <= last; i++) {
assertEquals("key" + i, enumObjects.nextElement());
}
final Iterator<Object> iterSet = orderedProperties.keySet().iterator();
for (int i = first; i <= last; i++) {
assertEquals("key" + i, iterSet.next());
}
final Enumeration<?> propertyNames = orderedProperties.propertyNames();
for (int i = first; i <= last; i++) {
assertEquals("key" + i, propertyNames.nextElement());
}
}
private OrderedProperties assertDescendingOrder(final OrderedProperties orderedProperties) {
final int first = 11;
final int last = 1;
final Enumeration<Object> enumObjects = orderedProperties.keys();
for (int i = first; i <= last; i--) {
assertEquals("key" + i, enumObjects.nextElement());
}
final Iterator<Object> iterSet = orderedProperties.keySet().iterator();
for (int i = first; i <= last; i--) {
assertEquals("key" + i, iterSet.next());
}
final Enumeration<?> propertyNames = orderedProperties.propertyNames();
for (int i = first; i <= last; i--) {
assertEquals("key" + i, propertyNames.nextElement());
}
return orderedProperties;
}
private OrderedProperties loadOrderedKeysReverse() throws FileNotFoundException, IOException {
final OrderedProperties orderedProperties = new OrderedProperties();
try (FileReader reader = new FileReader("src/test/resources/org/apache/commons/collections4/properties/test-reverse.properties")) {
orderedProperties.load(reader);
}
return assertDescendingOrder(orderedProperties);
}
@Test
public void testCompute() {
final OrderedProperties orderedProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
final AtomicInteger aInt = new AtomicInteger(i);
orderedProperties.compute("key" + i, (k, v) -> "value" + aInt.get());
}
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
final AtomicInteger aInt = new AtomicInteger(i);
orderedProperties.compute("key" + i, (k, v) -> "value" + aInt.get());
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testComputeIfAbsent() {
final OrderedProperties orderedProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
final AtomicInteger aInt = new AtomicInteger(i);
orderedProperties.computeIfAbsent("key" + i, k -> "value" + aInt.get());
}
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
final AtomicInteger aInt = new AtomicInteger(i);
orderedProperties.computeIfAbsent("key" + i, k -> "value" + aInt.get());
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testEntrySet() {
final OrderedProperties orderedProperties = new OrderedProperties();
for (char ch = 'Z'; ch >= 'A'; ch--) {
orderedProperties.put(String.valueOf(ch), "Value" + ch);
}
final Iterator<Map.Entry<Object, Object>> entries = orderedProperties.entrySet().iterator();
for (char ch = 'Z'; ch <= 'A'; ch++) {
final Map.Entry<Object, Object> entry = entries.next();
assertEquals(String.valueOf(ch), entry.getKey());
assertEquals("Value" + ch, entry.getValue());
}
}
@Test
public void testKeys() {
final OrderedProperties orderedProperties = new OrderedProperties();
for (char ch = 'Z'; ch >= 'A'; ch--) {
orderedProperties.put(String.valueOf(ch), "Value" + ch);
}
final Enumeration<Object> keys = orderedProperties.keys();
for (char ch = 'Z'; ch <= 'A'; ch++) {
assertEquals(String.valueOf(ch), keys.nextElement());
}
}
@Test
public void testLoadOrderedKeys() throws FileNotFoundException, IOException {
final OrderedProperties orderedProperties = new OrderedProperties();
try (FileReader reader = new FileReader("src/test/resources/org/apache/commons/collections4/properties/test.properties")) {
orderedProperties.load(reader);
}
assertAscendingOrder(orderedProperties);
}
@Test
public void testLoadOrderedKeysReverse() throws FileNotFoundException, IOException {
loadOrderedKeysReverse();
}
@Test
public void testMerge() {
final OrderedProperties orderedProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
orderedProperties.merge("key" + i, "value" + i, (k, v) -> v);
}
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
orderedProperties.merge("key" + i, "value" + i, (k, v) -> v);
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testPut() {
final OrderedProperties orderedProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
orderedProperties.put("key" + i, "value" + i);
}
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
orderedProperties.put("key" + i, "value" + i);
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testPutAll() {
final OrderedProperties sourceProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
sourceProperties.put("key" + i, "value" + i);
}
final OrderedProperties orderedProperties = new OrderedProperties();
orderedProperties.putAll(sourceProperties);
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
orderedProperties.put("key" + i, "value" + i);
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testPutIfAbsent() {
final OrderedProperties orderedProperties = new OrderedProperties();
int first = 1;
int last = 11;
for (int i = first; i <= last; i++) {
orderedProperties.putIfAbsent("key" + i, "value" + i);
}
assertAscendingOrder(orderedProperties);
orderedProperties.clear();
first = 11;
last = 1;
for (int i = first; i >= last; i--) {
orderedProperties.putIfAbsent("key" + i, "value" + i);
}
assertDescendingOrder(orderedProperties);
}
@Test
public void testRemoveKey() throws FileNotFoundException, IOException {
final OrderedProperties 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
public void testRemoveKeyValue() throws FileNotFoundException, IOException {
final OrderedProperties 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));
}
}

View File

@ -0,0 +1,25 @@
# 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.
key11 = value11
key10 = value10
key9 = value9
key8 = value8
key7 = value7
key6 = value6
key5 = value5
key4 = value4
key3 = value3
key2 = value2
key1 = value1