diff --git a/pom.xml b/pom.xml index f9afc4e73..9f08bdd27 100644 --- a/pom.xml +++ b/pom.xml @@ -463,10 +463,16 @@ test - commons-codec - commons-codec - 1.14 - true + commons-io + commons-io + 2.6 + test + + + commons-codec + commons-codec + 1.14 + true @@ -534,10 +540,10 @@ 2.8 - 0.8.4 + 0.8.5 - 4.3 + 4.4 true Gary Gregory 86fdc7e2a11262cb diff --git a/src/changes/changes.xml b/src/changes/changes.xml index bde17b387..ca6b38704 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -120,6 +120,12 @@ [build] Update Apache commons-parent from 48 to 50. + + Add org.apache.commons.collections4.properties.PropertiesFactory.EMPTY_PROPERTIES. + + + [build] Update Jacoco from 0.8.4 to 0.8.5. + diff --git a/src/main/java/org/apache/commons/collections4/properties/PropertiesFactory.java b/src/main/java/org/apache/commons/collections4/properties/PropertiesFactory.java index 04de759d2..40f6359aa 100644 --- a/src/main/java/org/apache/commons/collections4/properties/PropertiesFactory.java +++ b/src/main/java/org/apache/commons/collections4/properties/PropertiesFactory.java @@ -17,7 +17,26 @@ package org.apache.commons.collections4.properties; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.InvalidPropertiesFormatException; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; import java.util.Properties; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; /** * Creates and loads {@link Properties}. @@ -27,6 +46,282 @@ import java.util.Properties; */ public class PropertiesFactory extends AbstractPropertiesFactory { + private static class EmptyProperties extends Properties { + + private static final long serialVersionUID = 1L; + + @Override + public synchronized void clear() { + // Noop + } + + @Override + public synchronized Object compute(final Object key, + final BiFunction remappingFunction) { + Objects.requireNonNull(key); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object computeIfAbsent(final Object key, + final Function mappingFunction) { + Objects.requireNonNull(key); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object computeIfPresent(final Object key, + final BiFunction remappingFunction) { + Objects.requireNonNull(key); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized boolean contains(final Object value) { + return false; + } + + @Override + public synchronized boolean containsKey(final Object key) { + return false; + } + + @Override + public boolean containsValue(final Object value) { + return false; + } + + @Override + public synchronized Enumeration elements() { + return Collections.emptyEnumeration(); + } + + @Override + public Set> entrySet() { + return Collections.emptySet(); + } + + @Override + public synchronized boolean equals(final Object o) { + return (o instanceof Properties) && ((Properties) o).isEmpty(); + } + + @Override + public synchronized void forEach(final BiConsumer action) { + Objects.requireNonNull(action); + } + + @Override + public synchronized Object get(final Object key) { + return null; + } + + @Override + public synchronized Object getOrDefault(final Object key, final Object defaultValue) { + return defaultValue; + } + + @Override + public String getProperty(final String key) { + return null; + } + + @Override + public String getProperty(final String key, final String defaultValue) { + return defaultValue; + } + + @Override + public synchronized int hashCode() { + return 0; + } + + @Override + public synchronized boolean isEmpty() { + return true; + } + + @Override + public synchronized Enumeration keys() { + return Collections.emptyEnumeration(); + } + + @Override + public Set keySet() { + return Collections.emptySet(); + } + + @Override + public void list(final PrintStream out) { + // Implement as super + super.list(out); + } + + @Override + public void list(final PrintWriter out) { + // Implement as super + super.list(out); + } + + @Override + public synchronized void load(final InputStream inStream) throws IOException { + Objects.requireNonNull(inStream); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void load(final Reader reader) throws IOException { + Objects.requireNonNull(reader); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void loadFromXML(final InputStream in) + throws IOException, InvalidPropertiesFormatException { + Objects.requireNonNull(in); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object merge(final Object key, final Object value, + final BiFunction remappingFunction) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + public Enumeration propertyNames() { + return Collections.emptyEnumeration(); + } + + @Override + public synchronized Object put(final Object key, final Object value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void putAll(final Map t) { + Objects.requireNonNull(t); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object putIfAbsent(final Object key, final Object value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + protected void rehash() { + // Noop + } + + @Override + public synchronized Object remove(final Object key) { + Objects.requireNonNull(key); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized boolean remove(final Object key, final Object value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized Object replace(final Object key, final Object value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized boolean replace(final Object key, final Object oldValue, final Object newValue) { + Objects.requireNonNull(key); + Objects.requireNonNull(oldValue); + Objects.requireNonNull(newValue); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized void replaceAll( + final BiFunction function) { + Objects.requireNonNull(function); + throw new UnsupportedOperationException(); + } + + @Override + public void save(final OutputStream out, final String comments) { + // Implement as super + super.save(out, comments); + } + + @Override + public synchronized Object setProperty(final String key, final String value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + throw new UnsupportedOperationException(); + } + + @Override + public synchronized int size() { + return 0; + } + + @Override + public void store(final OutputStream out, final String comments) throws IOException { + // Implement as super + super.store(out, comments); + } + + @Override + public void store(final Writer writer, final String comments) throws IOException { + // Implement as super + super.store(writer, comments); + } + + @Override + public void storeToXML(final OutputStream os, final String comment) throws IOException { + // Implement as super + super.storeToXML(os, comment); + } + + @Override + public void storeToXML(final OutputStream os, final String comment, final String encoding) throws IOException { + // Implement as super + super.storeToXML(os, comment, encoding); + } + + @Override + public Set stringPropertyNames() { + return Collections.emptySet(); + } + + @Override + public synchronized String toString() { + // Implement as super + return super.toString(); + } + + @Override + public Collection values() { + return Collections.emptyList(); + } + + } + + /** + * The empty map (immutable). This map is serializable. + * + * @since 4.5 + */ + public static final Properties EMPTY_PROPERTIES = new EmptyProperties(); + /** * The singleton instance. */ diff --git a/src/test/java/org/apache/commons/collections4/properties/EmptyPropertiesTest.java b/src/test/java/org/apache/commons/collections4/properties/EmptyPropertiesTest.java new file mode 100644 index 000000000..82d06c95b --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/properties/EmptyPropertiesTest.java @@ -0,0 +1,349 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Properties; + +import org.apache.commons.io.input.NullReader; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Assert; +import org.junit.Test; + +public class EmptyPropertiesTest { + + @Test + public void testClear() { + PropertiesFactory.EMPTY_PROPERTIES.clear(); + Assert.assertEquals(0, PropertiesFactory.EMPTY_PROPERTIES.size()); + } + + @Test + public void testClone() { + // TODO Better test? + PropertiesFactory.EMPTY_PROPERTIES.clone(); + Assert.assertEquals(0, PropertiesFactory.EMPTY_PROPERTIES.size()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCompute() { + PropertiesFactory.EMPTY_PROPERTIES.compute("key", (k, v) -> "foo"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testComputeIfAbsent() { + PropertiesFactory.EMPTY_PROPERTIES.computeIfAbsent("key", k -> "foo"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testComputeIfPresent() { + PropertiesFactory.EMPTY_PROPERTIES.computeIfPresent("key", (k, v) -> "foo"); + } + + @Test + public void testContains() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.contains("foo")); + } + + @Test + public void testContainsKey() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.containsKey("foo")); + } + + @Test + public void testContainsValue() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.containsValue("foo")); + } + + @Test + public void testElements() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.elements().hasMoreElements()); + } + + @Test + public void testEntrySet() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.entrySet().isEmpty()); + } + + @Test + public void testEquals() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.equals(PropertiesFactory.EMPTY_PROPERTIES)); + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.equals(new Properties())); + Assert.assertTrue(new Properties().equals(PropertiesFactory.EMPTY_PROPERTIES)); + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.equals(null)); + final Properties p = new Properties(); + p.put("Key", "Value"); + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.equals(p)); + Assert.assertFalse(p.equals(PropertiesFactory.EMPTY_PROPERTIES)); + } + + public void testForEach() { + PropertiesFactory.EMPTY_PROPERTIES.forEach((k, v) -> Assert.fail()); + } + + @Test + public void testGet() { + Assert.assertNull(PropertiesFactory.EMPTY_PROPERTIES.get("foo")); + } + + @Test + public void testGetOrDefault() { + Assert.assertEquals("bar", PropertiesFactory.EMPTY_PROPERTIES.getOrDefault("foo", "bar")); + } + + @Test + public void testGetProperty() { + Assert.assertNull(PropertiesFactory.EMPTY_PROPERTIES.getProperty("foo")); + } + + @Test + public void testGetPropertyDefault() { + Assert.assertEquals("bar", PropertiesFactory.EMPTY_PROPERTIES.getProperty("foo", "bar")); + } + + @Test + public void testHashCode() { + Assert.assertEquals(PropertiesFactory.EMPTY_PROPERTIES.hashCode(), + PropertiesFactory.EMPTY_PROPERTIES.hashCode()); + // Should be equals? + // Assert.assertEquals(PropertiesFactory.EMPTY_PROPERTIES.hashCode(), new Properties().hashCode()); + } + + @Test + public void testIsEmpty() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.isEmpty()); + } + + @Test + public void testKeys() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.keys().hasMoreElements()); + } + + @Test + public void testKeySet() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.keySet().isEmpty()); + } + + @Test + public void testListToPrintStream() { + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.list(new PrintStream(actual)); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().list(new PrintStream(expected)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().list(new PrintStream(expected)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test + public void testListToPrintWriter() { + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.list(new PrintWriter(actual)); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().list(new PrintWriter(expected)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().list(new PrintWriter(expected)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testLoadFromXML() throws IOException { + PropertiesFactory.EMPTY_PROPERTIES.loadFromXML(new ByteArrayInputStream(ArrayUtils.EMPTY_BYTE_ARRAY)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testLoadInputStream() throws IOException { + PropertiesFactory.EMPTY_PROPERTIES.load(new ByteArrayInputStream(ArrayUtils.EMPTY_BYTE_ARRAY)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testLoadReader() throws IOException { + try (final NullReader reader = new NullReader(0)) { + PropertiesFactory.EMPTY_PROPERTIES.load(reader); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void testMerge() { + PropertiesFactory.EMPTY_PROPERTIES.merge("key", "value", (k, v) -> "foo"); + } + + @Test + public void testPropertyName() { + Assert.assertFalse(PropertiesFactory.EMPTY_PROPERTIES.propertyNames().hasMoreElements()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testPut() { + PropertiesFactory.EMPTY_PROPERTIES.put("Key", "Value"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testPutAll() { + PropertiesFactory.EMPTY_PROPERTIES.putAll(new HashMap<>()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testPutIfAbsent() { + PropertiesFactory.EMPTY_PROPERTIES.putIfAbsent("Key", "Value"); + } + + @Test + public void testRehash() { + // Can't really test without extending and casting to a currently private class + // PropertiesFactory.EMPTY_PROPERTIES.rehash(); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRemove() { + PropertiesFactory.EMPTY_PROPERTIES.remove("key", "value"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRemoveKey() { + PropertiesFactory.EMPTY_PROPERTIES.remove("key"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testReplace() { + PropertiesFactory.EMPTY_PROPERTIES.replace("key", "value1"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testReplaceAll() { + PropertiesFactory.EMPTY_PROPERTIES.replaceAll((k, v) -> "value1"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testReplaceOldValue() { + PropertiesFactory.EMPTY_PROPERTIES.replace("key", "value1", "value2"); + } + + @Test + public void testSave() { + final String comments = "Hello world!"; + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.save(new PrintStream(actual), comments); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().save(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().save(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testSetProperty() { + PropertiesFactory.EMPTY_PROPERTIES.setProperty("Key", "Value"); + } + + @Test + public void testSize() { + Assert.assertEquals(0, PropertiesFactory.EMPTY_PROPERTIES.size()); + } + + @Test + public void testStoreToOutputStream() throws IOException { + final String comments = "Hello world!"; + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.store(new PrintStream(actual), comments); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().store(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().store(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test + public void testStoreToPrintWriter() throws IOException { + final String comments = "Hello world!"; + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.store(new PrintWriter(actual), comments); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().store(new PrintWriter(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().store(new PrintWriter(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test + public void testStoreToXMLOutputStream() throws IOException { + final String comments = "Hello world!"; + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.storeToXML(new PrintStream(actual), comments); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().storeToXML(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().storeToXML(new PrintStream(expected), comments); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test + public void testStoreToXMLOutputStreamWithEncoding() throws IOException { + final String comments = "Hello world!"; + final String encoding = StandardCharsets.UTF_8.name(); + // actual + final ByteArrayOutputStream actual = new ByteArrayOutputStream(); + PropertiesFactory.EMPTY_PROPERTIES.storeToXML(new PrintStream(actual), comments, encoding); + // expected + final ByteArrayOutputStream expected = new ByteArrayOutputStream(); + PropertiesFactory.INSTANCE.createProperties().storeToXML(new PrintStream(expected), comments, encoding); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + expected.reset(); + new Properties().storeToXML(new PrintStream(expected), comments, encoding); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + @Test + public void testStringPropertyName() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.stringPropertyNames().isEmpty()); + } + + @Test + public void testToString() { + Assert.assertEquals(new Properties().toString(), PropertiesFactory.EMPTY_PROPERTIES.toString()); + } + + @Test + public void testValues() { + Assert.assertTrue(PropertiesFactory.EMPTY_PROPERTIES.values().isEmpty()); + } +}