LANG-1167: Added isExcludeNullValues to ReflectionToStringBuilder and test
Project: http://git-wip-us.apache.org/repos/asf/commons-lang/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-lang/commit/661d16d1 Tree: http://git-wip-us.apache.org/repos/asf/commons-lang/tree/661d16d1 Diff: http://git-wip-us.apache.org/repos/asf/commons-lang/diff/661d16d1 Branch: refs/heads/master Commit: 661d16d190708a1a396d8b75ba10738e4574c11d Parents: 40b8ecd Author: MarkDacek <mark.da...@richmond.edu> Authored: Sat Mar 18 15:47:09 2017 -0400 Committer: MarkDacek <mark.da...@richmond.edu> Committed: Sat Mar 18 15:47:09 2017 -0400 ---------------------------------------------------------------------- .../builder/ReflectionToStringBuilder.java | 122 ++++++++++++++++++- ...ionToStringBuilderExcludeNullValuesTest.java | 99 +++++++++++++++ 2 files changed, 220 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-lang/blob/661d16d1/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java index d40a838..0b4d7e5 100644 --- a/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java +++ b/src/main/java/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java @@ -300,6 +300,64 @@ public class ReflectionToStringBuilder extends ToStringBuilder { return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) .toString(); } + + /** + * <p> + * Builds a <code>toString</code> value through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + * </p> + * + * <p> + * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * <code>java.lang.Object</code>. + * </p> + * + * <p> + * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used. + * </p> + * + * @param <T> + * the type of the object + * @param object + * the Object to be output + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param excludeNulls + * whether to exclude fields whose values are null + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be <code>null</code> + * @return the String result + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * + * @see ToStringExclude + * @since 2.1 + */ + public static <T> String toString( + final T object, final ToStringStyle style, final boolean outputTransients, + final boolean outputStatics, boolean excludeNulls, final Class<? super T> reflectUpToClass) { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics, excludeNulls) + .toString(); + } /** * Builds a String for a toString method excluding the given field names. @@ -379,6 +437,11 @@ public class ReflectionToStringBuilder extends ToStringBuilder { * Whether or not to append transient fields. */ private boolean appendTransients = false; + + /** + * Whether or not to append fields that are null. + */ + private boolean excludeNullValues; /** * Which field names to exclude from output. Intended for fields like <code>"password"</code>. @@ -483,6 +546,38 @@ public class ReflectionToStringBuilder extends ToStringBuilder { this.setAppendTransients(outputTransients); this.setAppendStatics(outputStatics); } + + /** + * Constructor. + * + * @param <T> + * the type of the object + * @param object + * the Object to build a <code>toString</code> for + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param buffer + * the <code>StringBuffer</code> to populate, may be <code>null</code> + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param excludeNullValues + * whether to exclude fields who value is null + * @since 2.1 + */ + public <T> ReflectionToStringBuilder( + final T object, final ToStringStyle style, final StringBuffer buffer, + final Class<? super T> reflectUpToClass, final boolean outputTransients, final boolean outputStatics, + final boolean excludeNullValues) { + super(checkNotNull(object), style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + this.setExcludeNullValues(excludeNullValues); + } /** * Returns whether or not to append the given <code>Field</code>. @@ -547,7 +642,9 @@ public class ReflectionToStringBuilder extends ToStringBuilder { // Warning: Field.get(Object) creates wrappers objects // for primitive types. final Object fieldValue = this.getValue(field); - this.append(fieldName, fieldValue); + if(!excludeNullValues || fieldValue != null){ + this.append(fieldName, fieldValue); + } } catch (final IllegalAccessException ex) { //this can't happen. Would get a Security exception // instead @@ -619,6 +716,17 @@ public class ReflectionToStringBuilder extends ToStringBuilder { public boolean isAppendTransients() { return this.appendTransients; } + + /** + * <p> + * Gets whether or not to append fields whose values are null. + * </p> + * + * @return Whether or not to append fields whose values are null. + */ + public boolean isExcludeNullValues() { + return this.excludeNullValues; + } /** * <p> @@ -658,6 +766,18 @@ public class ReflectionToStringBuilder extends ToStringBuilder { public void setAppendTransients(final boolean appendTransients) { this.appendTransients = appendTransients; } + + /** + * <p> + * Sets whether or not to append fields whose values are null. + * </p> + * + * @param excludeNullValues + * Whether or not to append fields whose values are null. + */ + public void setExcludeNullValues(final boolean excludeNullValues) { + this.excludeNullValues = excludeNullValues; + } /** * Sets the field names to exclude. http://git-wip-us.apache.org/repos/asf/commons-lang/blob/661d16d1/src/test/java/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeNullValuesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeNullValuesTest.java b/src/test/java/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeNullValuesTest.java new file mode 100644 index 0000000..be368d6 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeNullValuesTest.java @@ -0,0 +1,99 @@ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class ReflectionToStringBuilderExcludeNullValuesTest { + + class TestFixture{ + private Integer testIntegerField; + private String testStringField; + + public TestFixture(Integer a, String b){ + this.testIntegerField = a; + this.testStringField = b; + } + } + + private static final String INTEGER_FIELD_NAME = "testIntegerField"; + private static final String STRING_FIELD_NAME = "testStringField"; + private final TestFixture BOTH_NON_NULL = new TestFixture(0, "str"); + private final TestFixture FIRST_NULL = new TestFixture(null, "str"); + private final TestFixture SECOND_NULL = new TestFixture(0, null); + private final TestFixture BOTH_NULL = new TestFixture(null, null); + + @Test + public void test_NonExclude(){ + //normal case= + String toString = ReflectionToStringBuilder.toString(BOTH_NON_NULL, null, false, false, false, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + //make one null + toString = ReflectionToStringBuilder.toString(FIRST_NULL, null, false, false, false, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + //other one null + toString = ReflectionToStringBuilder.toString(SECOND_NULL, null, false, false, false, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + //make the both null + toString = ReflectionToStringBuilder.toString(BOTH_NULL, null, false, false, false, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + } + + @Test + public void test_excludeNull(){ + + //test normal case + String toString = ReflectionToStringBuilder.toString(BOTH_NON_NULL, null, false, false, true, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + //make one null + toString = ReflectionToStringBuilder.toString(FIRST_NULL, null, false, false, true, null); + assertFalse(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + //other one null + toString = ReflectionToStringBuilder.toString(SECOND_NULL, null, false, false, true, null); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertFalse(toString.contains(STRING_FIELD_NAME)); + + //both null + toString = ReflectionToStringBuilder.toString(BOTH_NULL, null, false, false, true, null); + assertFalse(toString.contains(INTEGER_FIELD_NAME)); + assertFalse(toString.contains(STRING_FIELD_NAME)); + } + + @Test + public void test_ConstructorOption(){ + ReflectionToStringBuilder builder = new ReflectionToStringBuilder(BOTH_NON_NULL, null, null, null, false, false, false); + builder.setExcludeNullValues(true); + assertTrue(builder.isExcludeNullValues()); + + String toString = builder.toString(); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + builder = new ReflectionToStringBuilder(FIRST_NULL, null, null, null, false, false, true); + toString = builder.toString(); + assertFalse(toString.contains(INTEGER_FIELD_NAME)); + assertTrue(toString.contains(STRING_FIELD_NAME)); + + builder = new ReflectionToStringBuilder(SECOND_NULL, null, null, null, false, false, true); + toString = builder.toString(); + assertTrue(toString.contains(INTEGER_FIELD_NAME)); + assertFalse(toString.contains(STRING_FIELD_NAME)); + + builder = new ReflectionToStringBuilder(BOTH_NULL, null, null, null, false, false, true); + toString = builder.toString(); + assertFalse(toString.contains(INTEGER_FIELD_NAME)); + assertFalse(toString.contains(STRING_FIELD_NAME)); + } + +}