diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanAccess.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanAccess.java index b9ebf7007..0af135817 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanAccess.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanAccess.java @@ -30,6 +30,11 @@ public interface BeanAccess { */ GenericProperty getProperty(String name); + /** + * @return the {@link Class} reflecting the owning {@link Bean}. + */ + Class getBeanClass(); + /** * @param name the {@link GenericProperty#getName() name} of the requested property. * @return the requested {@link GenericProperty}. @@ -39,7 +44,7 @@ default GenericProperty getRequiredProperty(String name) throws PojoPropertyN GenericProperty property = getProperty(name); if (property == null) { - throw new PojoPropertyNotFoundException(getClass(), name); + throw new PojoPropertyNotFoundException(getBeanClass(), name); } return property; } @@ -158,8 +163,8 @@ default void setPropertyValue(String name, V value, GenericType type) { boolean isDynamic(); /** - * @return true if this {@link BeanAccess} belongs to a {@link BeanFactory#createPrototype(Class) prototype} - * , false otherwise (if it belongs to an {@link BeanFactory#create(Bean) instance}). + * @return true if this {@link BeanAccess} belongs to a {@link BeanFactory#createPrototype(Class) + * prototype} , false otherwise (if it belongs to an {@link BeanFactory#create(Bean) instance}). */ boolean isPrototype(); diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanFactory.java index 12fa586a9..8dd7a5231 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/api/BeanFactory.java @@ -73,4 +73,14 @@ default BEAN create(Class type) { */ BEAN getReadOnlyBean(BEAN bean); + /** + * Creates a copy of the given {@link Bean}. + * + * @param the generic type of the {@link Bean}. + * @param bean the bean to make {@link BeanAccess#isReadOnly() read-only}. + * @return the {@link BeanAccess#isReadOnly() read-only} view on the given bean. Will be the given instance if already + * {@link BeanAccess#isReadOnly() read-only}. + */ + BEAN copy(BEAN bean); + } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessBase.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessBase.java index 586458cdb..0584c8147 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessBase.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessBase.java @@ -20,28 +20,26 @@ public abstract class BeanAccessBase implements InvocationHandler, BeanAccess, Iterable> { - private final Class beanType; + private final Class beanClass; private final BEAN bean; /** * The constructor. * - * @param beanType - see {@link #getBeanType()}. + * @param beanClass - see {@link #getBeanClass()}. * @param beanFactory the owning {@link BeanFactoryImpl}. */ - public BeanAccessBase(Class beanType, BeanFactoryImpl beanFactory) { + public BeanAccessBase(Class beanClass, BeanFactoryImpl beanFactory) { super(); - this.beanType = beanType; - this.bean = beanFactory.createProxy(this, beanType); + this.beanClass = beanClass; + this.bean = beanFactory.createProxy(this, beanClass); } - /** - * @return the {@link Class} reflecting the {@link #getBean() bean}. - */ - public Class getBeanType() { + @Override + public Class getBeanClass() { - return this.beanType; + return this.beanClass; } /** @@ -68,7 +66,7 @@ public GenericProperty getProperty(String name) { BeanPrototypeProperty prototypeProperty = getPrototype().getName2PropertyMap().get(name); if (prototypeProperty != null) { - return getProperty(prototypeProperty, false); + return getProperty(prototypeProperty, true); } return null; } @@ -77,11 +75,11 @@ public GenericProperty getProperty(String name) { * Gets the {@link GenericProperty} for the given index. * * @param prototypeProperty is the {@link BeanPrototypeProperty}. - * @param required - true if the property is required and shall be created if it {@link #isDynamic() does + * @param create - true if the property is required and shall be created if it {@link #isDynamic() does * not already exist}, false otherwise. * @return the requested {@link GenericProperty}. May be null. */ - protected abstract GenericProperty getProperty(BeanPrototypeProperty prototypeProperty, boolean required); + protected abstract GenericProperty getProperty(BeanPrototypeProperty prototypeProperty, boolean create); @Override public boolean isReadOnly() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessInstance.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessInstance.java index 5a3a68a8b..c0c6d68bf 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessInstance.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessInstance.java @@ -29,14 +29,14 @@ public abstract class BeanAccessInstance extends BeanAccessBa /** * The constructor. * - * @param beanType - see {@link #getBeanType()}. + * @param beanClass - see {@link #getBeanClass()}. * @param beanFactory the owning {@link BeanFactoryImpl}. * @param prototype the {@link BeanAccessPrototype}. */ - public BeanAccessInstance(Class beanType, BeanFactoryImpl beanFactory, + public BeanAccessInstance(Class beanClass, BeanFactoryImpl beanFactory, BeanAccessPrototype prototype) { - super(beanType, beanFactory); + super(beanClass, beanFactory); this.prototype = prototype; this.properties = GenericProperty.NO_PROPERTIES; } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessMutable.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessMutable.java index fed8134a5..f7ff77501 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessMutable.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessMutable.java @@ -20,14 +20,14 @@ public class BeanAccessMutable extends BeanAccessInstance beanType, BeanFactoryImpl beanFactory, + public BeanAccessMutable(Class beanClass, BeanFactoryImpl beanFactory, BeanAccessPrototype prototype) { - super(beanType, beanFactory, prototype); + super(beanClass, beanFactory, prototype); } @Override diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessPrototype.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessPrototype.java index 1c9d42ff4..f83d1c543 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessPrototype.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessPrototype.java @@ -40,12 +40,12 @@ public class BeanAccessPrototype extends BeanAccessBase /** * The constructor. * - * @param beanType - see {@link #getBeanType()}. + * @param beanClass - see {@link #getBeanClass()}. * @param beanFactory the owning {@link BeanFactoryImpl}. */ - public BeanAccessPrototype(Class beanType, BeanFactoryImpl beanFactory) { - super(beanType, beanFactory); - this.beanType = beanType; + public BeanAccessPrototype(Class beanClass, BeanFactoryImpl beanFactory) { + super(beanClass, beanFactory); + this.beanType = beanClass; this.name2PropertyMap = new HashMap<>(); this.method2OperationMap = new HashMap<>(); this.dynamic = false; @@ -137,7 +137,7 @@ protected void addProperty(GenericPropertyImpl property) { * @return the type */ @Override - public Class getBeanType() { + public Class getBeanClass() { return this.beanType; } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessReadOnly.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessReadOnly.java index 56261af74..ca5362ab9 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessReadOnly.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanAccessReadOnly.java @@ -28,7 +28,7 @@ public class BeanAccessReadOnly extends BeanAccessInstance prototype, BeanAccessBase delegate) { - super(prototype.getBeanType(), beanFactory, prototype); + super(prototype.getBeanClass(), beanFactory, prototype); this.delegate = delegate; } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanFactoryImpl.java b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanFactoryImpl.java index 335223cdf..d97fabf69 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanFactoryImpl.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/bean/impl/BeanFactoryImpl.java @@ -19,6 +19,7 @@ import javafx.beans.property.Property; import net.sf.mmm.util.bean.api.Bean; +import net.sf.mmm.util.bean.api.BeanAccess; import net.sf.mmm.util.bean.api.BeanFactory; import net.sf.mmm.util.component.base.AbstractLoggableComponent; import net.sf.mmm.util.property.api.GenericProperty; @@ -115,6 +116,51 @@ BEAN createProxy(BeanAccessBase access, Class be return bean; } + @SuppressWarnings("unchecked") + @Override + public BEAN create(BEAN bean) { + + BeanAccessBase access = (BeanAccessBase) bean.access(); + BeanAccessPrototype beanPrototype = access.getPrototype(); + BeanAccessMutable interceptor = new BeanAccessMutable<>(access.getBeanClass(), this, beanPrototype); + return interceptor.getBean(); + } + + @SuppressWarnings("unchecked") + @Override + public BEAN getReadOnlyBean(BEAN bean) { + + BeanAccessBase access = (BeanAccessBase) bean.access(); + if (access.isReadOnly()) { + return bean; + } + BeanAccessPrototype beanPrototype = access.getPrototype(); + BeanAccessReadOnly interceptor = new BeanAccessReadOnly<>(this, beanPrototype, access); + return interceptor.getBean(); + } + + @SuppressWarnings("unchecked") + @Override + public BEAN getPrototype(BEAN bean) { + + BeanAccessBase access = (BeanAccessBase) bean.access(); + BeanAccessPrototype beanPrototype = access.getPrototype(); + return beanPrototype.getBean(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public BEAN copy(BEAN bean) { + + BEAN copy = create(bean); + BeanAccess access = copy.access(); + for (GenericProperty property : bean.access().getProperties()) { + GenericProperty copyProperty = access.getRequiredProperty(property.getName()); + copyProperty.setValue(property.getValue()); + } + return copy; + } + @Override public BEAN createPrototype(Class type, boolean dynamic) { @@ -345,36 +391,4 @@ private GenericPropertyImpl createPropertyFromSpecifiedClass(String name, Gen } } - @SuppressWarnings("unchecked") - @Override - public BEAN create(BEAN prototype) { - - BeanAccessBase access = (BeanAccessBase) prototype.access(); - BeanAccessPrototype beanPrototype = access.getPrototype(); - BeanAccessMutable interceptor = new BeanAccessMutable<>(access.getBeanType(), this, beanPrototype); - return interceptor.getBean(); - } - - @SuppressWarnings("unchecked") - @Override - public BEAN getReadOnlyBean(BEAN beanProxy) { - - BeanAccessBase access = (BeanAccessBase) beanProxy.access(); - if (access.isReadOnly()) { - return beanProxy; - } - BeanAccessPrototype beanPrototype = access.getPrototype(); - BeanAccessReadOnly interceptor = new BeanAccessReadOnly<>(this, beanPrototype, access); - return interceptor.getBean(); - } - - @SuppressWarnings("unchecked") - @Override - public BEAN getPrototype(BEAN bean) { - - BeanAccessBase access = (BeanAccessBase) bean.access(); - BeanAccessPrototype beanPrototype = access.getPrototype(); - return beanPrototype.getBean(); - } - } diff --git a/mmm-util-property/src/test/java/net/sf/mmm/util/bean/impl/BeanFactoryImplTest.java b/mmm-util-property/src/test/java/net/sf/mmm/util/bean/impl/BeanFactoryImplTest.java index 2d83dc659..202cbb4ed 100644 --- a/mmm-util-property/src/test/java/net/sf/mmm/util/bean/impl/BeanFactoryImplTest.java +++ b/mmm-util-property/src/test/java/net/sf/mmm/util/bean/impl/BeanFactoryImplTest.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; +import net.sf.mmm.test.ObjectHelper; import net.sf.mmm.util.bean.api.Bean; import net.sf.mmm.util.bean.api.BeanAccess; import net.sf.mmm.util.bean.api.BeanFactory; @@ -47,8 +48,8 @@ public void testExamplePropertyBean() { BeanFactory beanFactory = getBeanFactory(); ExamplePropertyBean bean = beanFactory.create(ExamplePropertyBean.class); - verifyBean(bean); - verifyExamplePropertyBean(bean); + verifyBean(bean, beanFactory); + verifyExamplePropertyBean(bean, beanFactory); } @Test @@ -56,7 +57,7 @@ public void testExamplePojoBean() { BeanFactory beanFactory = getBeanFactory(); ExamplePojoBean bean = beanFactory.create(ExamplePojoBean.class); - verifyBean(bean); + verifyBean(bean, beanFactory); verifyExamplePojoBean(bean); } @@ -65,19 +66,19 @@ public void testExampleBean() { BeanFactory beanFactory = getBeanFactory(); ExampleBean bean = beanFactory.create(ExampleBean.class); - verifyBean(bean); + verifyBean(bean, beanFactory); verifyExamplePojoBean(bean); - verifyExamplePropertyBean(bean); + verifyExamplePropertyBean(bean, beanFactory); assertThat(bean.self()).isSameAs(bean); assertThat(bean.sayHi("Peter")).isEqualTo("Hi Peter"); } - private void verifyBean(Bean bean) { + private void verifyBean(Bean bean, BeanFactory beanFactory) { BeanAccess access = bean.access(); assertThat(access).isNotNull(); - assertThat(((BeanAccessBase) access).getBean()).isSameAs(bean); + assertThat(((BeanAccessBase) access).getBean()).isSameAs(bean); String undefinedProperty = "UndefinedProperty"; assertThat(access.getProperty(undefinedProperty)).isNull(); PojoPropertyNotFoundException error = null; @@ -93,9 +94,12 @@ private void verifyBean(Bean bean) { .isSameAs(access.getRequiredProperty(name)) .isSameAs(access.getOrCreateProperty(name, property.getType())); } + + // test equals + ObjectHelper.checkEqualsAndHashCode(bean, beanFactory.copy(bean), true); } - private void verifyExamplePropertyBean(ExamplePropertyBean bean) { + private void verifyExamplePropertyBean(ExamplePropertyBean bean, BeanFactory beanFactory) { assertThat(bean).isNotNull(); assertThat(bean.Name()).isInstanceOf(StringPropertyImpl.class); @@ -169,6 +173,11 @@ private void verifyExamplePropertyBean(ExamplePropertyBean bean) { } catch (Exception e) { fail("Failed to parse bean.toString() as JSON: " + json, e); } + + // equals and hashCode + ExamplePropertyBean copy = beanFactory.copy(bean); + copy.Name().setValue("Heinz"); + ObjectHelper.checkEqualsAndHashCode(bean, copy, false); } private void verifyExamplePojoBean(ExamplePojoBean bean) { diff --git a/mmm-util-test/src/main/java/net/sf/mmm/test/TestUser.java b/mmm-util-test/src/main/java/net/sf/mmm/test/TestUser.java index a95a61890..724385bca 100644 --- a/mmm-util-test/src/main/java/net/sf/mmm/test/TestUser.java +++ b/mmm-util-test/src/main/java/net/sf/mmm/test/TestUser.java @@ -12,7 +12,6 @@ * This is a simple user for testing. It acts as {@link java.security.Principal} and {@link Authentication}. * * @author Joerg Hohwiller (hohwille at users.sourceforge.net) - * @since 0 */ public class TestUser implements Authentication {