Merge branch 'LANG-1031'

LANG-1031: Add annotations to exclude fields from ReflectionEqualsBuilder,
ReflectionToStringBuilder and ReflectionHashCodeBuilder. Thanks to
Felipe Adorno. This closes #29, #77.
This commit is contained in:
Benedikt Ritter 2015-05-04 20:52:04 +02:00
commit 8548b12d8f
11 changed files with 248 additions and 2 deletions

View File

@ -478,6 +478,9 @@
<contributor> <contributor>
<name>Michał Kordas</name> <name>Michał Kordas</name>
</contributor> </contributor>
<contributor>
<name>Felipe Adorno</name>
</contributor>
</contributors> </contributors>
<!-- Lang should depend on very little --> <!-- Lang should depend on very little -->

View File

@ -22,6 +22,7 @@
<body> <body>
<release version="3.5" date="tba" description="tba"> <release version="3.5" date="tba" description="tba">
<action issue="LANG-1031" type="add" dev="britter" due-to="Felipe Adorno">Add annotations to exclude fields from ReflectionEqualsBuilder, ReflectionToStringBuilder and ReflectionHashCodeBuilder</action>
<action issue="LANG-1127" type="add" dev="chas">Unit test helpers which set and reset default Locale and TimeZone</action> <action issue="LANG-1127" type="add" dev="chas">Unit test helpers which set and reset default Locale and TimeZone</action>
<action issue="LANG-1128" type="fix" dev="britter" due-to="jacktan1991">JsonToStringStyle doesn't handle chars and objects correctly</action> <action issue="LANG-1128" type="fix" dev="britter" due-to="jacktan1991">JsonToStringStyle doesn't handle chars and objects correctly</action>
<action issue="LANG-456" type="fix" dev="britter" due-to="Bob Fields, Woosan Ko, Bruno P. Kinoshita">HashCodeBuilder throws StackOverflowError in bidirectional navigable association</action> <action issue="LANG-456" type="fix" dev="britter" due-to="Bob Fields, Woosan Ko, Bruno P. Kinoshita">HashCodeBuilder throws StackOverflowError in bidirectional navigable association</action>

View File

@ -411,7 +411,8 @@ public class EqualsBuilder implements Builder<Boolean> {
if (!ArrayUtils.contains(excludeFields, f.getName()) if (!ArrayUtils.contains(excludeFields, f.getName())
&& !f.getName().contains("$") && !f.getName().contains("$")
&& (useTransients || !Modifier.isTransient(f.getModifiers())) && (useTransients || !Modifier.isTransient(f.getModifiers()))
&& !Modifier.isStatic(f.getModifiers())) { && (!Modifier.isStatic(f.getModifiers()))
&& (!f.isAnnotationPresent(EqualsExclude.class))) {
try { try {
builder.append(f.get(lhs), f.get(rhs)); builder.append(f.get(lhs), f.get(rhs));
} catch (final IllegalAccessException e) { } catch (final IllegalAccessException e) {

View File

@ -0,0 +1,32 @@
/*
* 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.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Use this annotation to builds a equals excluding the annotated field.
*/
@Retention(RUNTIME)
@Target(ElementType.FIELD)
public @interface EqualsExclude {
}

View File

@ -192,7 +192,8 @@ public class HashCodeBuilder implements Builder<Integer> {
if (!ArrayUtils.contains(excludeFields, field.getName()) if (!ArrayUtils.contains(excludeFields, field.getName())
&& !field.getName().contains("$") && !field.getName().contains("$")
&& (useTransients || !Modifier.isTransient(field.getModifiers())) && (useTransients || !Modifier.isTransient(field.getModifiers()))
&& !Modifier.isStatic(field.getModifiers())) { && (!Modifier.isStatic(field.getModifiers()))
&& (!field.isAnnotationPresent(HashCodeExclude.class))) {
try { try {
final Object fieldValue = field.get(object); final Object fieldValue = field.get(object);
builder.append(fieldValue); builder.append(fieldValue);

View File

@ -0,0 +1,32 @@
/*
* 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.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Use this annotation to builds a hash code excluding the annotated field.
*/
@Retention(RUNTIME)
@Target(ElementType.FIELD)
public @interface HashCodeExclude {
}

View File

@ -494,6 +494,9 @@ public class ReflectionToStringBuilder extends ToStringBuilder {
// Reject fields from the getExcludeFieldNames list. // Reject fields from the getExcludeFieldNames list.
return false; return false;
} }
if(field.isAnnotationPresent(ToStringExclude.class)) {
return false;
}
return true; return true;
} }

View File

@ -0,0 +1,32 @@
/*
* 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.lang3.builder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Use this annotation to builds a String excluding the annotated field.
*/
@Retention(RUNTIME)
@Target(ElementType.FIELD)
public @interface ToStringExclude {
}

View File

@ -1150,5 +1150,37 @@ public class EqualsBuilderTest {
assertTrue(EqualsBuilder.reflectionEquals(d1, d3)); assertTrue(EqualsBuilder.reflectionEquals(d1, d3));
} }
static class TestObjectEqualsExclude {
@EqualsExclude
private int a;
private int b;
public TestObjectEqualsExclude(int a, int b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
@Test
public void testToEqualsExclude() {
TestObjectEqualsExclude one = new TestObjectEqualsExclude(1, 2);
TestObjectEqualsExclude two = new TestObjectEqualsExclude(1, 3);
assertFalse(EqualsBuilder.reflectionEquals(one, two));
one = new TestObjectEqualsExclude(1, 2);
two = new TestObjectEqualsExclude(2, 2);
assertTrue(EqualsBuilder.reflectionEquals(one, two));
}
} }

View File

@ -622,4 +622,51 @@ public class HashCodeBuilderTest {
hcb.toHashCode(), hcb.hashCode()); hcb.toHashCode(), hcb.hashCode());
} }
static class TestObjectHashCodeExclude {
@HashCodeExclude
private int a;
private int b;
public TestObjectHashCodeExclude(int a, int b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
static class TestObjectHashCodeExclude2 {
@HashCodeExclude
private int a;
@HashCodeExclude
private int b;
public TestObjectHashCodeExclude2(int a, int b) {
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public int getB() {
return b;
}
}
@Test
public void testToHashCodeExclude() {
TestObjectHashCodeExclude one = new TestObjectHashCodeExclude(1, 2);
TestObjectHashCodeExclude2 two = new TestObjectHashCodeExclude2(1, 2);
assertEquals(17 * 37 + 2, HashCodeBuilder.reflectionHashCode(one));
assertEquals(17, HashCodeBuilder.reflectionHashCode(two));
}
} }

View File

@ -0,0 +1,62 @@
/*
* 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.lang3.builder;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.Assert;
import org.junit.Test;
/**
* Test class for ToStringExclude annotation
* @version $Id$
*/
public class ReflectionToStringBuilderExcludeWithAnnotationTest {
class TestFixture {
@ToStringExclude
private final String secretField = SECRET_VALUE;
@SuppressWarnings("unused")
private final String showField = NOT_SECRET_VALUE;
}
private static final String NOT_SECRET_FIELD = "showField";
private static final String NOT_SECRET_VALUE = "Hello World!";
private static final String SECRET_FIELD = "secretField";
private static final String SECRET_VALUE = "secret value";
@Test
public void test_toStringExclude() {
final String toString = ReflectionToStringBuilder.toString(new TestFixture());
this.validateSecretFieldAbsent(toString);
}
private void validateNonSecretField(final String toString) {
Assert.assertTrue(toString.indexOf(NOT_SECRET_FIELD) > ArrayUtils.INDEX_NOT_FOUND);
Assert.assertTrue(toString.indexOf(NOT_SECRET_VALUE) > ArrayUtils.INDEX_NOT_FOUND);
}
private void validateSecretFieldAbsent(final String toString) {
Assert.assertEquals(ArrayUtils.INDEX_NOT_FOUND, toString.indexOf(SECRET_FIELD));
Assert.assertEquals(ArrayUtils.INDEX_NOT_FOUND, toString.indexOf(SECRET_VALUE));
this.validateNonSecretField(toString);
}
}