diff --git a/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java b/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java index db97c884..1196c7f5 100644 --- a/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java +++ b/src/main/java/org/springframework/data/gemfire/util/PropertiesBuilder.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.Reader; import java.util.Properties; +import org.springframework.beans.factory.FactoryBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -30,25 +31,88 @@ import org.springframework.util.StringUtils; * * @author John Blum * @see java.util.Properties - * @since 1.0.0 + * @see org.springframework.beans.factory.FactoryBean + * @since 1.9.0 */ @SuppressWarnings("unused") -public class PropertiesBuilder { +public class PropertiesBuilder implements FactoryBean { private final Properties properties; + /** + * Factory method to create a default {@link PropertiesBuilder} instance. + * + * @return an instance of the {@link PropertiesBuilder} class with not {@link Properties}. + * @see #PropertiesBuilder() + */ + public static PropertiesBuilder create() { + return new PropertiesBuilder(); + } + + /** + * Constructs an instance of the {@link PropertiesBuilder} class. + */ public PropertiesBuilder() { this.properties = new Properties(); } + /** + * Constructs an instance of the {@link PropertiesBuilder} class initialized with the default {@link Properties}. + * + * @param defaults {@link Properties} used as the defaults. + * @see java.util.Properties + */ public PropertiesBuilder(Properties defaults) { - this.properties = new Properties(defaults); + this.properties = new Properties(); + this.properties.putAll(defaults); } + /** + * Constructs an instance of the {@link PropertiesBuilder} class initialized with the given + * {@link PropertiesBuilder} providing the default {@link Properties} for this builder. + * + * @param builder {@link PropertiesBuilder} providing the default {@link Properties} for this builder. + * @see #PropertiesBuilder(Properties) + */ public PropertiesBuilder(PropertiesBuilder builder) { this(builder != null ? builder.build() : null); } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObject() + */ + @Override + public Properties getObject() throws Exception { + return build(); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#getObjectType() + */ + @Override + public Class getObjectType() { + return (this.properties != null ? this.properties.getClass() : Properties.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.FactoryBean#isSingleton() + */ + @Override + public boolean isSingleton() { + return true; + } + + /** + * Null-safe method to add all the {@link Properties} to this builder. This operation effectively overwrites + * any properties already set with the same name from the source. + * + * @param properties {@link Properties} to add to this builder. + * @return a reference to this {@link PropertiesBuilder}. + * @see java.util.Properties + */ public PropertiesBuilder add(Properties properties) { if (!CollectionUtils.isEmpty(properties)) { this.properties.putAll(properties); @@ -57,6 +121,14 @@ public class PropertiesBuilder { return this; } + /** + * Null-safe method to add all the {@link Properties} from the provided {@link PropertiesBuilder} to this builder. + * This operation effectively overwrites any properties already set with the same name from the source. + * + * @param builder source of the {@link Properties} to add to this builder. + * @return a reference to this {@link PropertiesBuilder}. + * @see org.springframework.data.gemfire.util.PropertiesBuilder + */ public PropertiesBuilder add(PropertiesBuilder builder) { if (builder != null) { add(builder.build()); @@ -65,6 +137,15 @@ public class PropertiesBuilder { return this; } + /** + * Adds all properties from the given {@link InputStream} to this builder. + * + * @param in {@link InputStream} source containing properties to add to this builder. + * @return a reference to this {@link PropertiesBuilder}. + * @throws IllegalArgumentException if the {@link InputStream} cannot be read. + * @see java.util.Properties#load(InputStream) + * @see java.io.InputStream + */ public PropertiesBuilder from(InputStream in) { try { this.properties.load(in); @@ -75,6 +156,15 @@ public class PropertiesBuilder { } } + /** + * Adds all properties from the given {@link Reader} to this builder. + * + * @param reader {@link Reader} source containing properties to add to this builder. + * @return a reference to this {@link PropertiesBuilder}. + * @throws IllegalArgumentException if the {@link Reader} cannot be read. + * @see java.util.Properties#load(Reader) + * @see java.io.Reader + */ public PropertiesBuilder from(Reader reader) { try { this.properties.load(reader); @@ -85,34 +175,76 @@ public class PropertiesBuilder { } } + /** + * Adds all properties from the given {@link InputStream} in XML format to this builder. + * + * @param xml {@link InputStream} source containing properties in XML format to add to this builder. + * @return a reference to this {@link PropertiesBuilder}. + * @throws IllegalArgumentException if the XML {@link InputStream} cannot be read. + * @see java.util.Properties#loadFromXML(InputStream) + * @see java.io.InputStream + */ public PropertiesBuilder fromXml(InputStream xml) { try { this.properties.loadFromXML(xml); return this; } catch (IOException e) { - throw new IllegalArgumentException("Failed to read properties from XML InputStream", e); + throw new IllegalArgumentException("Failed to read properties from XML", e); } } + /** + * Sets a property with given name to the specified value. The property is only set if the value is not null. + * + * @param name the name of the property to set. + * @param value the value to set the property to. + * @return a reference to this {@link PropertiesBuilder}. + * @see #setProperty(String, String) + */ public PropertiesBuilder setProperty(String name, Object value) { - if (value != null) { - setProperty(name, value.toString()); - } - - return this; + return (value != null ? setProperty(name, value.toString()) : this); } + /** + * Sets a property with the given name to the specified {@link String} value. The property is only set + * if the value is not {@literal null}, an empty {@link String} or not equal to the {@link String} literal + * {@literal null}, ignoring case. + * + * @param name the name of the property to set. + * @param value the value to set the property to. + * @return a reference to this {@link PropertiesBuilder}. + * @throws IllegalArgumentException if the property name is not specified. + * @see java.util.Properties#setProperty(String, String) + */ public PropertiesBuilder setProperty(String name, String value) { - Assert.hasText(name, String.format("Name [%s] must not be null or empty", name)); + Assert.hasText(name, String.format("Name [%s] must not be specified", name)); - if (StringUtils.hasText(value)) { + if (isValuable(value)) { this.properties.setProperty(name, value); } return this; } + /** + * Determine whether the given {@link String} value is a valid {@link Properties} value. A property value is + * considered valid if it is not null, not empty and not equal to (case-insensitive) {@link String} literal + * {@literal null}. + * + * @param value {@link String} value for the property being set. + * @return a boolean value indicating whether the given {@link String} value is a valid {@link Properties} value. + */ + protected boolean isValuable(String value) { + return (StringUtils.hasText(value) && !"null".equalsIgnoreCase(value.trim())); + } + + /** + * Builds the {@link Properties} object from this builder. + * + * @return the {@link Properties} object built by this builder. + * @see java.util.Properties + */ public Properties build() { return this.properties; } diff --git a/src/test/java/org/springframework/data/gemfire/util/PropertiesBuilderTests.java b/src/test/java/org/springframework/data/gemfire/util/PropertiesBuilderTests.java new file mode 100644 index 00000000..7c18b83e --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/util/PropertiesBuilderTests.java @@ -0,0 +1,254 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed 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.springframework.data.gemfire.util; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Properties; + +import org.junit.Test; + +/** + * Test suite of test cases testing the contract and functionality of the {@link PropertiesBuilder} class. + * + * @author John Blum + * @see java.util.Properties + * @see org.springframework.data.gemfire.util.PropertiesBuilder + * @since 1.0.0 + */ +public class PropertiesBuilderTests { + + protected Properties singletonProperties(String name, String value) { + Properties properties = new Properties(); + properties.setProperty(name, value); + return properties; + } + + @Test + @SuppressWarnings("unchecked") + public void constructDefaultPropertiesBuilder() throws Exception { + PropertiesBuilder builder = new PropertiesBuilder(); + + Properties properties = builder.build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(builder.getObject(), is(sameInstance(properties))); + assertThat((Class) builder.getObjectType(), is(equalTo(Properties.class))); + assertThat(properties.isEmpty(), is(true)); + } + + @Test + public void constructPropertiesBuilderWithDefaultProperties() { + Properties defaults = singletonProperties("one", "1"); + + PropertiesBuilder builder = new PropertiesBuilder(defaults); + + Properties properties = builder.build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties, is(not(sameInstance(defaults)))); + assertThat(properties, is(equalTo(defaults))); + } + + @Test + public void constructPropertiesBuilderWithPropertiesBuilder() { + PropertiesBuilder defaults = new PropertiesBuilder().setProperty("one", "1"); + PropertiesBuilder builder = new PropertiesBuilder(defaults); + + Properties properties = builder.build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.size(), is(equalTo(1))); + assertThat(properties.getProperty("one"), is(equalTo("1"))); + } + + @Test + public void propertiesBuilderIsSingleton() { + assertThat(new PropertiesBuilder().isSingleton(), is(true)); + } + + @Test + public void addPropertiesFromPropertiesIsSuccessful() { + PropertiesBuilder builder = PropertiesBuilder.create() + .setProperty("one", "1") + .setProperty("two", "@"); + + Properties sink = builder.build(); + + assertThat(sink, is(notNullValue(Properties.class))); + assertThat(sink.size(), is(2)); + assertThat(sink.getProperty("one"), is(equalTo("1"))); + assertThat(sink.getProperty("two"), is(equalTo("@"))); + assertThat(sink.containsKey("three"), is(false)); + + Properties source = new Properties(); + + source.setProperty("two", "2"); + source.setProperty("three", "3"); + + builder.add(source); + + sink = builder.build(); + + assertThat(sink, is(notNullValue(Properties.class))); + assertThat(sink.size(), is(equalTo(3))); + assertThat(sink, is(not(sameInstance(source)))); + assertThat(sink.getProperty("one"), is(equalTo("1"))); + assertThat(sink.getProperty("two"), is(equalTo("2"))); + assertThat(sink.getProperty("three"), is(equalTo("3"))); + } + + @Test + public void addPropertiesFromPropertiesBuilderIsSuccessful() { + PropertiesBuilder source = PropertiesBuilder.create() + .setProperty("one", "1") + .setProperty("two", "2"); + + Properties properties = new PropertiesBuilder().add(source).build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.size(), is(equalTo(2))); + assertThat(properties.getProperty("one"), is(equalTo("1"))); + assertThat(properties.getProperty("two"), is(equalTo("2"))); + } + + @Test + public void fromInputStreamIsSuccessful() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + Properties source = singletonProperties("one", "1"); + + source.store(out, "fromInputStreamIsSuccessfulTest"); + + Properties sink = PropertiesBuilder.create().from(new ByteArrayInputStream(out.toByteArray())).build(); + + assertThat(sink, is(notNullValue(Properties.class))); + assertThat(sink, is(not(sameInstance(source)))); + assertThat(sink, is(equalTo(source))); + } + + @Test + public void fromReaderIsSuccessful() throws IOException { + StringWriter writer = new StringWriter(); + + Properties source = singletonProperties("one", "1"); + + source.store(writer, "fromReaderIsSuccessfulTest"); + + Properties sink = PropertiesBuilder.create().from(new StringReader(writer.toString())).build(); + + assertThat(sink, is(notNullValue(Properties.class))); + assertThat(sink, is(not(sameInstance(source)))); + assertThat(sink, is(equalTo(source))); + } + + @Test + public void fromXmlInputStreamIsSuccessful() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + Properties source = singletonProperties("one", "1"); + + source.storeToXML(out, "fromXmlInputStreamIsSuccessfulTest"); + + Properties sink = PropertiesBuilder.create().fromXml(new ByteArrayInputStream(out.toByteArray())).build(); + + assertThat(sink, is(notNullValue(Properties.class))); + assertThat(sink, is(not(sameInstance(source)))); + assertThat(sink, is(equalTo(source))); + } + + @Test + public void setObjectPropertyValuesIsSuccessful() { + Properties properties = PropertiesBuilder.create() + .setProperty("boolean", Boolean.TRUE) + .setProperty("character", 'A') + .setProperty("integer", 1) + .setProperty("double", Math.PI) + .setProperty("string", (Object) "test") + .build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.size(), is(equalTo(5))); + assertThat(properties.getProperty("boolean"), is(equalTo(Boolean.TRUE.toString()))); + assertThat(properties.getProperty("character"), is(equalTo("A"))); + assertThat(properties.getProperty("integer"), is(equalTo("1"))); + assertThat(properties.getProperty("double"), is(equalTo(String.valueOf(Math.PI)))); + assertThat(properties.getProperty("string"), is(equalTo("test"))); + } + + @Test + public void setPropertyIgnoresNullObjectValue() { + Properties properties = PropertiesBuilder.create().setProperty("object", null).build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.isEmpty(), is(true)); + } + + @Test + public void setPropertyIgnoresEmptyAndNullLiteralStringValues() { + Properties properties = PropertiesBuilder.create() + .setProperty("blank", " ") + .setProperty("empty", "") + .setProperty("null", "null") + .build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.isEmpty(), is(true)); + } + + @Test + public void setStringPropertyValuesIsSuccessful() { + Properties properties = PropertiesBuilder.create() + .setProperty("one", "1") + .setProperty("two", "2") + .build(); + + assertThat(properties, is(notNullValue(Properties.class))); + assertThat(properties.size(), is(equalTo(2))); + assertThat(properties.getProperty("one"), is(equalTo("1"))); + assertThat(properties.getProperty("two"), is(equalTo("2"))); + } + + @Test + public void stringLiteralIsValuable() { + assertThat(PropertiesBuilder.create().isValuable("test"), is(true)); + } + + @Test + public void nullStringLiteralIsNotValuable() { + assertThat(PropertiesBuilder.create().isValuable("null"), is(false)); + assertThat(PropertiesBuilder.create().isValuable("Null"), is(false)); + assertThat(PropertiesBuilder.create().isValuable("NULL"), is(false)); + } + + @Test + public void emptyStringLiteralIsNotValuable() { + assertThat(PropertiesBuilder.create().isValuable(" "), is(false)); + assertThat(PropertiesBuilder.create().isValuable(""), is(false)); + } +}