Last active
October 7, 2019 10:53
-
-
Save juliofalbo/0fb76615139b89c5331e9f33cd4ce859 to your computer and use it in GitHub Desktop.
Custom Bean Utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.beans.FeatureDescriptor; | |
import java.util.Objects; | |
import java.util.stream.Stream; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.beans.BeanUtils; | |
import org.springframework.beans.BeanWrapper; | |
import org.springframework.beans.BeanWrapperImpl; | |
public class CustomBeanUtils { | |
private static final Logger log = LoggerFactory.getLogger(CustomBeanUtils.class); | |
protected static String[] getNullPropertyNames(Object source) { | |
final BeanWrapper wrappedSource = new BeanWrapperImpl(source); | |
return Stream.of(wrappedSource.getPropertyDescriptors()) | |
.map(FeatureDescriptor::getName) | |
.filter(propertyName -> { | |
boolean aNull = true; | |
try { | |
aNull = Objects.isNull(wrappedSource.getPropertyValue(propertyName)); | |
} catch (Exception e) { | |
log.warn("{}. Ignoring this property", e.getMessage()); | |
} | |
return aNull; | |
}) | |
.toArray(String[]::new); | |
} | |
public static <T> T copyProperties(Object source, Class<T> target) throws Exception { | |
T copied; | |
try { | |
copied = target.getConstructor().newInstance(); | |
} catch (NoSuchMethodException e) { | |
throw new NoSuchMethodException("No default constructor defined for class " + target.getSimpleName()); | |
} | |
BeanUtils.copyProperties(source, copied, getNullPropertyNames(source)); | |
return copied; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.hamcrest.CoreMatchers.is; | |
import static org.junit.Assert.assertThat; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.UUID; | |
import org.apache.log4j.AppenderSkeleton; | |
import org.apache.log4j.Level; | |
import org.apache.log4j.Logger; | |
import org.apache.log4j.spi.LoggingEvent; | |
import org.junit.Assert; | |
import org.junit.Test; | |
public class CustomBeanUtilsTest { | |
@Test | |
public void should_copy_all_fields() throws Exception { | |
AnotherObjectForTest anotherObjectForTest = getAnotherObjectForTest(); | |
AnotherObjectForTest copied = CustomBeanUtils.copyProperties(anotherObjectForTest, AnotherObjectForTest.class); | |
Assert.assertEquals(anotherObjectForTest.getFirst(), copied.getFirst()); | |
Assert.assertEquals(anotherObjectForTest.getSecond(), copied.getSecond()); | |
} | |
@Test(expected = NoSuchMethodException.class) | |
public void should_return_an_exception_cause_the_target_class_has_no_def_constructor() throws Exception { | |
AnotherObjectForTestWithoutDefaultConstructor anotherObjectForTest = new AnotherObjectForTestWithoutDefaultConstructor("a", "b"); | |
CustomBeanUtils.copyProperties(anotherObjectForTest, AnotherObjectForTestWithoutDefaultConstructor.class); | |
} | |
@Test | |
public void should_copy_only_filled_fields_with_one_invalid_attribute() throws Exception { | |
final TestAppender appender = new TestAppender(); | |
final Logger logger = Logger.getRootLogger(); | |
logger.addAppender(appender); | |
try { | |
AnotherObjectForTest anotherObjectForTest = getAnotherObjectForTest(); | |
ObjectForTest info = getObjectForTest(anotherObjectForTest); | |
ObjectForTest copied = CustomBeanUtils.copyProperties(info, ObjectForTest.class); | |
Assert.assertEquals(info.getFirstString(), copied.getFirstString()); | |
Assert.assertEquals(info.getSecondString(), copied.getSecondString()); | |
Assert.assertEquals(info.getObjectType(), copied.getObjectType()); | |
Assert.assertEquals(info.getSomeInteger(), info.getSomeInteger()); | |
} finally { | |
logger.removeAppender(appender); | |
} | |
final List<LoggingEvent> log = appender.getLog(); | |
final LoggingEvent firstLogEntry = log.get(0); | |
assertThat(firstLogEntry.getLevel(), is(Level.WARN)); | |
assertThat(firstLogEntry.getMessage(), is("Invalid property 'withoutGetter' of bean class [com.juliofalbo.ObjectForTest]: Bean property 'withoutGetter' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?. Ignoring this property")); | |
assertThat(firstLogEntry.getLoggerName(), is("com.juliofalbo.CustomBeanUtils")); | |
} | |
private ObjectForTest getObjectForTest(AnotherObjectForTest anotherObjectForTest) { | |
ObjectForTest info = new ObjectForTest(); | |
info.setFirstString(UUID.randomUUID().toString()); | |
info.setSecondString(UUID.randomUUID().toString()); | |
info.setObjectType(anotherObjectForTest); | |
info.setWithoutGetter("ccc"); | |
return info; | |
} | |
private AnotherObjectForTest getAnotherObjectForTest() { | |
AnotherObjectForTest anotherObjectForTest = new AnotherObjectForTest(); | |
anotherObjectForTest.setFirst("aaa"); | |
anotherObjectForTest.setSecond("bb"); | |
return anotherObjectForTest; | |
} | |
} | |
class TestAppender extends AppenderSkeleton { | |
private final List<LoggingEvent> log = new ArrayList<>(); | |
@Override | |
public boolean requiresLayout() { | |
return false; | |
} | |
@Override | |
protected void append(final LoggingEvent loggingEvent) { | |
log.add(loggingEvent); | |
} | |
@Override | |
public void close() { | |
} | |
public List<LoggingEvent> getLog() { | |
return new ArrayList<>(log); | |
} | |
} | |
class ObjectForTest { | |
@SuppressWarnings("checkstyle:RedundantModifier") | |
public ObjectForTest() { | |
} | |
private String firstString; | |
private String secondString; | |
private AnotherObjectForTest objectType; | |
private String withoutGetter; | |
private Integer someInteger; | |
public String getFirstString() { | |
return firstString; | |
} | |
public void setFirstString(String firstString) { | |
this.firstString = firstString; | |
} | |
public String getSecondString() { | |
return secondString; | |
} | |
public void setSecondString(String secondString) { | |
this.secondString = secondString; | |
} | |
public AnotherObjectForTest getObjectType() { | |
return objectType; | |
} | |
public void setObjectType(AnotherObjectForTest objectType) { | |
this.objectType = objectType; | |
} | |
public void setWithoutGetter(String withoutGetter) { | |
this.withoutGetter = withoutGetter; | |
} | |
public Integer getSomeInteger() { | |
return someInteger; | |
} | |
public void setSomeInteger(Integer someInteger) { | |
this.someInteger = someInteger; | |
} | |
} | |
class AnotherObjectForTest { | |
private String first; | |
private String second; | |
@SuppressWarnings("checkstyle:RedundantModifier") | |
public AnotherObjectForTest() { | |
} | |
public String getFirst() { | |
return first; | |
} | |
public void setFirst(String first) { | |
this.first = first; | |
} | |
public String getSecond() { | |
return second; | |
} | |
public void setSecond(String second) { | |
this.second = second; | |
} | |
} | |
class AnotherObjectForTestWithoutDefaultConstructor { | |
private String first; | |
private String second; | |
@SuppressWarnings("checkstyle:RedundantModifier") | |
public AnotherObjectForTestWithoutDefaultConstructor(String first, String second) { | |
this.first = first; | |
this.second = second; | |
} | |
public String getFirst() { | |
return first; | |
} | |
public void setFirst(String first) { | |
this.first = first; | |
} | |
public String getSecond() { | |
return second; | |
} | |
public void setSecond(String second) { | |
this.second = second; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment