package de.invesdwin.util.bean; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.mutable.MutableInt; import org.junit.Test; import org.mockito.Mockito; import de.invesdwin.util.assertions.Assertions; import de.invesdwin.util.lang.Objects; import de.invesdwin.util.lang.Strings; // CHECKSTYLE:OFF abstract class name @ThreadSafe public class AValueObjectTest { //CHECKSTYLE:ON private boolean propertyChanged; @Test public void testDeepClone() { final CloneableVO vo = new CloneableVO(); vo.setValue(5); vo.setMutableValue(new MutableInt(5)); vo.setCloneableClass(new CloneableClass()); vo.getCloneableClass().setOtherValue(8); vo.setCloneableVO(new CloneableVO()); vo.getCloneableVO().setValue(6); final CloneableVO voClone = (CloneableVO) vo.clone(); Assertions.assertThat(vo).isNotSameAs(voClone); //FST does not clone immutable values Assertions.assertThat(vo.getValue()).isSameAs(voClone.getValue()); Assertions.assertThat(vo.getValue()).isEqualTo(voClone.getValue()); //only mutable ones Assertions.assertThat(vo.getMutableValue()).isNotSameAs(voClone.getMutableValue()); Assertions.assertThat(vo.getMutableValue()).isEqualTo(voClone.getMutableValue()); //and cloneables Assertions.assertThat(vo.getCloneableClass()).isNotSameAs(voClone.getCloneableClass()); Assertions.assertThat(vo.getCloneableClass()).isEqualTo(voClone.getCloneableClass()); //and value objects Assertions.assertThat(vo.getCloneableVO()).isNotSameAs(voClone.getCloneableVO()); Assertions.assertThat(vo.getCloneableVO()).isEqualTo(voClone.getCloneableVO()); } @Test public void testShallowCloneableClass() { final CloneableClass vo = new CloneableClass(); final CloneableVO value = new CloneableVO(); value.setValue(5); vo.setValue(value); final CloneableClass voClone = (CloneableClass) vo.clone(); Assertions.assertThat(vo).isNotSameAs(voClone); Assertions.assertThat(vo.getValue()).isSameAs(voClone.getValue()); Assertions.assertThat(vo.getValue()).isEqualTo(voClone.getValue()); Assertions.assertThat(vo.getValue().getValue()).isSameAs(voClone.getValue().getValue()); Assertions.assertThat(vo.getValue().getValue()).isEqualTo(voClone.getValue().getValue()); } @Test public void testShallowClone() { final CloneableVO vo = new CloneableVO(); vo.setValue(5); vo.setMutableValue(new MutableInt(5)); vo.setCloneableClass(new CloneableClass()); vo.setCloneableVO(new CloneableVO()); vo.getCloneableVO().setValue(6); final CloneableVO voClone = (CloneableVO) vo.shallowClone(); Assertions.assertThat(vo).isNotSameAs(voClone); //shallow clone does not clone constants Assertions.assertThat(vo.getValue()).isSameAs(voClone.getValue()); Assertions.assertThat(vo.getValue()).isEqualTo(voClone.getValue()); //also not mutable values Assertions.assertThat(vo.getMutableValue()).isSameAs(voClone.getMutableValue()); Assertions.assertThat(vo.getMutableValue()).isEqualTo(voClone.getMutableValue()); //nor cloneables Assertions.assertThat(vo.getCloneableClass()).isSameAs(voClone.getCloneableClass()); Assertions.assertThat(vo.getCloneableClass()).isEqualTo(voClone.getCloneableClass()); //and not value objects Assertions.assertThat(vo.getCloneableVO()).isSameAs(voClone.getCloneableVO()); Assertions.assertThat(vo.getCloneableVO()).isEqualTo(voClone.getCloneableVO()); } @Test public void testShallowCloneReflective() { final CloneableVO vo = new CloneableVO(); vo.setValue(5); vo.setMutableValue(new MutableInt(5)); vo.setCloneableClass(new CloneableClass()); vo.getCloneableClass().setOtherValue(8); vo.setCloneableVO(new CloneableVO()); vo.getCloneableVO().setValue(6); final CloneableVO voClone = (CloneableVO) vo.shallowCloneReflective(); Assertions.assertThat(vo).isNotSameAs(voClone); //refletive clone does not clone constants Assertions.assertThat(vo.getValue()).isSameAs(voClone.getValue()); Assertions.assertThat(vo.getValue()).isEqualTo(voClone.getValue()); //also it does not clone classes that do not implement cloneable Assertions.assertThat(vo.getMutableValue()).isSameAs(voClone.getMutableValue()); Assertions.assertThat(vo.getMutableValue()).isEqualTo(voClone.getMutableValue()); //but it does cloneables Assertions.assertThat(vo.getCloneableClass()).isNotSameAs(voClone.getCloneableClass()); Assertions.assertThat(vo.getCloneableClass()).isEqualTo(voClone.getCloneableClass()); //and value objects Assertions.assertThat(vo.getCloneableVO()).isNotSameAs(voClone.getCloneableVO()); Assertions.assertThat(vo.getCloneableVO()).isEqualTo(voClone.getCloneableVO()); } @Test public void testShallowCloneReflectiveOnFields() { final FieldCloneableVO vo = new FieldCloneableVO(); vo.value = 5; vo.mutableValue = new MutableInt(5); vo.cloneableClass = new FieldCloneableClass(); vo.cloneableClass.otherValue = 8; vo.cloneableVO = new FieldCloneableVO(); vo.cloneableVO.value = 6; final FieldCloneableVO voClone = (FieldCloneableVO) vo.shallowCloneReflective(); Assertions.assertThat(vo).isNotSameAs(voClone); //refletive clone does not clone constants Assertions.assertThat(vo.value).isSameAs(voClone.value); Assertions.assertThat(vo.value).isEqualTo(voClone.value); //also it does not clone classes that do not implement cloneable Assertions.assertThat(vo.mutableValue).isSameAs(voClone.mutableValue); Assertions.assertThat(vo.mutableValue).isEqualTo(voClone.mutableValue); //but it does cloneables Assertions.assertThat(vo.cloneableClass).isNotSameAs(voClone.cloneableClass); Assertions.assertThat(vo.cloneableClass).isEqualTo(voClone.cloneableClass); //and value objects Assertions.assertThat(vo.cloneableVO).isNotSameAs(voClone.cloneableVO); Assertions.assertThat(vo.cloneableVO).isEqualTo(voClone.cloneableVO); } @Test public void testMergeFrom() { final Integer value = 5; final CloneableVO vo = new CloneableVO(); vo.setValue(value); final CloneableVO newVo = new CloneableVO(); newVo.mergeFrom(vo); Assertions.assertThat(value).isEqualTo(newVo.getValue()); Assertions.assertThat(vo.getValue()).isEqualTo(newVo.getValue()); newVo.setValue(null); vo.mergeFrom(newVo); Assertions.assertThat(newVo.getValue()).isNull(); Assertions.assertThat(vo.getValue()).as("null values are not getting ignored!").isNotNull(); final CloneableClass clazz = new CloneableClass(); clazz.setOtherValue(1); vo.mergeFrom(clazz); } @Test public void testPropertyChangeSupport() { final CloneableVO vo = new CloneableVO(); final PropertyChangeListener pcl = Mockito.mock(PropertyChangeListener.class); vo.addPropertyChangeListener(pcl); vo.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { Assertions.assertThat(evt.getPropertyName()).isEqualTo("value"); Assertions.assertThat(evt.getOldValue()).isNull(); Assertions.assertThat(evt.getNewValue()).isEqualTo(5); Assertions.assertThat(evt.getSource()).isSameAs(vo); System.out.println(Strings.asStringReflective(evt)); //SUPPRESS CHECKSTYLE single line propertyChanged = true; } }); vo.setValue(5); Mockito.verify(pcl, Mockito.only()).propertyChange((PropertyChangeEvent) Mockito.any()); Assertions.assertThat(propertyChanged).isTrue(); } public static class CloneableVO extends AValueObject { private static final long serialVersionUID = 1L; private Integer value; private MutableInt mutableValue; private CloneableClass cloneableClass; private CloneableVO cloneableVO; public Integer getValue() { return value; } public void setValue(final Integer value) { final Integer oldValue = this.value; this.value = value; firePropertyChange("value", oldValue, value); } public MutableInt getMutableValue() { return mutableValue; } public void setMutableValue(final MutableInt mutableValue) { final MutableInt oldValue = this.mutableValue; this.mutableValue = mutableValue; firePropertyChange("mutableValue", oldValue, value); } public CloneableClass getCloneableClass() { return cloneableClass; } public void setCloneableClass(final CloneableClass cloneableClass) { this.cloneableClass = cloneableClass; } public CloneableVO getCloneableVO() { return cloneableVO; } public void setCloneableVO(final CloneableVO cloneableVO) { this.cloneableVO = cloneableVO; } } public static class CloneableClass implements Cloneable, Serializable { private CloneableVO value; private Integer otherValue; public CloneableVO getValue() { return value; } public void setValue(final CloneableVO value) { this.value = value; } public Integer getOtherValue() { return otherValue; } public void setOtherValue(final Integer otherValue) { this.otherValue = otherValue; } @Override public Object clone() { try { return super.clone(); } catch (final CloneNotSupportedException e) { throw new RuntimeException(e); } } @Override public int hashCode() { return Objects.reflectionHashCode(this); } @Override public boolean equals(final Object obj) { return Objects.reflectionEquals(this, obj); } } public static class FieldCloneableVO extends AValueObject { //CHECKSTYLE:OFF public Integer value; public MutableInt mutableValue; public FieldCloneableClass cloneableClass; public FieldCloneableVO cloneableVO; //CHECKSTYLE:ON } public static class FieldCloneableClass implements Cloneable, Serializable { //CHECKSTYLE:OFF public FieldCloneableVO value; public Integer otherValue; //CHECKSTYLE:ON @Override public Object clone() { try { return super.clone(); } catch (final CloneNotSupportedException e) { throw new RuntimeException(e); } } @Override public int hashCode() { return Objects.reflectionHashCode(this); } @Override public boolean equals(final Object obj) { return Objects.reflectionEquals(this, obj); } } }