From 067c9283f702bf3339d867b7552ac41967bcbcb5 Mon Sep 17 00:00:00 2001 From: Joerg Hohwiller Date: Sun, 7 Feb 2016 15:41:29 +0100 Subject: [PATCH] #163: support for custom name as arg when creating prototype, naming convention for PropertyFactory impls changed, fixed PropertyFactoryManagerImpl --- .../net/sf/mmm/util/bean/api/BeanAccess.java | 86 ++++++++++++---- .../net/sf/mmm/util/bean/api/BeanFactory.java | 23 ++++- .../util/bean/impl/BeanAccessInstance.java | 8 +- .../util/bean/impl/BeanAccessPrototype.java | 13 ++- .../mmm/util/bean/impl/BeanFactoryImpl.java | 46 +++++---- .../property/api/factory/PropertyFactory.java | 7 ++ .../api/factory/PropertyFactoryManager.java | 78 +++++++++++++-- ...ctory.java => PropertyFactoryBoolean.java} | 8 +- ...actory.java => PropertyFactoryDouble.java} | 8 +- ...Factory.java => PropertyFactoryFloat.java} | 8 +- ...ctory.java => PropertyFactoryGeneric.java} | 8 +- ...ctory.java => PropertyFactoryInteger.java} | 8 +- ...yFactory.java => PropertyFactoryList.java} | 8 +- ...yFactory.java => PropertyFactoryLong.java} | 8 +- .../factory/PropertyFactoryManagerImpl.java | 98 ++++++++++++++----- ...tyFactory.java => PropertyFactoryMap.java} | 8 +- ...tyFactory.java => PropertyFactorySet.java} | 8 +- ...actory.java => PropertyFactoryString.java} | 8 +- 18 files changed, 343 insertions(+), 96 deletions(-) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{BooleanPropertyFactory.java => PropertyFactoryBoolean.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{DoublePropertyFactory.java => PropertyFactoryDouble.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{FloatPropertyFactory.java => PropertyFactoryFloat.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{GenericPropertyFactory.java => PropertyFactoryGeneric.java} (91%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{IntegerPropertyFactory.java => PropertyFactoryInteger.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{ListPropertyFactory.java => PropertyFactoryList.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{LongPropertyFactory.java => PropertyFactoryLong.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{MapPropertyFactory.java => PropertyFactoryMap.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{SetPropertyFactory.java => PropertyFactorySet.java} (90%) rename mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/{StringPropertyFactory.java => PropertyFactoryString.java} (90%) 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 5dcd1f418..73e74a13b 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 @@ -81,42 +81,62 @@ default ValidationFailure validate() { * * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param name the {@link WritableProperty#getName() property name}. - * @param type the {@link WritableProperty#getType() property type}. + * @param valueType the {@link WritableProperty#getType() property type}. * @return the requested property. */ @SuppressWarnings("unchecked") - default WritableProperty getOrCreateProperty(String name, GenericType type) { + default WritableProperty getOrCreateProperty(String name, GenericType valueType) { WritableProperty property = getProperty(name); if (property != null) { - if (!property.getType().equals(type)) { - throw new ObjectMismatchException(type, property.getType(), getBeanClass(), name + ".type"); + if (!property.getType().equals(valueType)) { + throw new ObjectMismatchException(valueType, property.getType(), getBeanClass(), name + ".type"); } return (WritableProperty) property; } - return createProperty(name, type); + return createProperty(name, valueType); } /** * {@link #getProperty(String) Gets} or {@link #createProperty(String, Class) creates} the specified property. If the * property already exists it also has to match the given {@code type} or an exception will be thrown. * + * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param the generic type of the {@link WritableProperty property}. * @param name the {@link WritableProperty#getName() property name}. - * @param type the Class reflecting the {@link WritableProperty} to create. + * @param propertyType the Class reflecting the {@link WritableProperty} to create. * @return the requested property. */ - default > PROPERTY getOrCreateProperty(String name, Class type) { + default > PROPERTY getOrCreateProperty(String name, + Class propertyType) { + + GenericType valueType = null; + return getOrCreateProperty(name, valueType, propertyType); + } + + /** + * {@link #getProperty(String) Gets} or {@link #createProperty(String, Class) creates} the specified property. If the + * property already exists it also has to match the given {@code type} or an exception will be thrown. + * + * @param the generic type of the {@link WritableProperty#getValue() property value}. + * @param the generic type of the {@link WritableProperty property}. + * @param name the {@link WritableProperty#getName() property name}. + * @param valueType the {@link WritableProperty#getType() property type}. + * @param propertyType the Class reflecting the {@link WritableProperty} to create. + * @return the requested property. + */ + default > PROPERTY getOrCreateProperty(String name, + GenericType valueType, Class propertyType) { WritableProperty property = getProperty(name); if (property != null) { try { - return type.cast(property); + return propertyType.cast(property); } catch (ClassCastException e) { - throw new ObjectMismatchException(e, type, property.getClass(), getBeanClass(), name + ".class"); + throw new ObjectMismatchException(e, propertyType, property.getClass(), getBeanClass(), name + ".class"); } } - return createProperty(name, type); + return createProperty(name, valueType, propertyType); } /** @@ -137,19 +157,26 @@ default Object getPropertyValue(String name) { * @param name the {@link WritableProperty#getName() name} of the property. * @param value new {@link WritableProperty#getValue() value} of the specified property. */ + @SuppressWarnings({ "unchecked", "rawtypes" }) default void setPropertyValue(String name, Object value) { - setPropertyValue(name, value, null); + WritableProperty property = getRequiredProperty(name); + ((WritableProperty) property).setValue(value); } /** * This method sets the {@link WritableProperty property} with the given {@link WritableProperty#getName() name} to - * the specified {@code value}. + * the specified {@code value}. If the {@link WritableProperty property} does not already exist, it will + * {@link #isDynamic() dynamically} be {@link #createProperty(String, GenericType) created}. * * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param name the {@link WritableProperty#getName() property name}. - * @param value new {@link WritableProperty#getValue() value} of the specified property. - * @param type the {@link WritableProperty#getType() property type}. + * @param value new {@link WritableProperty#getValue() value} of the specified property. Maybe {@code null} and in + * such case a missing {@link WritableProperty property} will NOT be + * {@link #createProperty(String, GenericType) created}. + * @param type the {@link WritableProperty#getType() property type}. May be null if the + * {@link WritableProperty property} has to be {@link #createProperty(String, GenericType) created} then the + * type will be derived from {@code value} as fallback. */ @SuppressWarnings({ "unchecked", "rawtypes" }) default void setPropertyValue(String name, V value, GenericType type) { @@ -174,21 +201,44 @@ default void setPropertyValue(String name, V value, GenericType type) { * * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param name the {@link WritableProperty#getName() property name}. - * @param type the {@link WritableProperty#getType() property type}. + * @param valueType the {@link WritableProperty#getType() property type}. * @return the newly created property. */ - WritableProperty createProperty(String name, GenericType type); + default WritableProperty createProperty(String name, GenericType valueType) { + + return createProperty(name, valueType, null); + } /** * Creates and adds the specified {@link WritableProperty} on the fly. Creating and adding new properties is only * possible for {@link #isDynamic() dynamic} beans. * + * @param the generic type of the {@link WritableProperty#getValue() property value}. + * @param the generic type of the {@link WritableProperty property}. + * @param name the {@link WritableProperty#getName() property name}. + * @param propertyType the Class reflecting the {@link WritableProperty} to create. + * @return the newly created property. + */ + default > PROPERTY createProperty(String name, + Class propertyType) { + + GenericType valueType = null; + return createProperty(name, valueType, propertyType); + } + + /** + * Creates and adds the specified {@link WritableProperty} on the fly. Creating and adding new properties is only + * possible for {@link #isDynamic() dynamic} beans. + * + * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param the generic type of the {@link WritableProperty property}. * @param name the {@link WritableProperty#getName() property name}. - * @param type the Class reflecting the {@link WritableProperty} to create. + * @param valueType + * @param propertyType the Class reflecting the {@link WritableProperty} to create. * @return the newly created property. */ - > PROPERTY createProperty(String name, Class type); + > PROPERTY createProperty(String name, GenericType valueType, + Class propertyType); /** * @see BeanFactory#getReadOnlyBean(Bean) 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 3338965bb..52d6143c7 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 @@ -27,15 +27,32 @@ default BEAN createPrototype(Class type) { /** * Creates a prototype of the given {@link Bean}. A prototype is used as template to {@link #create(Bean) create} * regular {@link Bean}s. Such beans will inherit the defaults from the prototype what are the - * {@link BeanAccess#getProperties() available properties} as well as their default - * {@link WritableProperty#getValue() value}. + * {@link BeanAccess#getProperties() available properties} as well as their default {@link WritableProperty#getValue() + * value}. * * @param the generic type of the {@link Bean}. * @param type the {@link Class} reflecting the {@link Bean}. * @param dynamic the {@link BeanAccess#isDynamic() dynamic flag} of the {@link Bean}. * @return the prototype instance of the specified {@link Bean}. */ - BEAN createPrototype(Class type, boolean dynamic); + default BEAN createPrototype(Class type, boolean dynamic) { + + return createPrototype(type, dynamic, null); + } + + /** + * Creates a prototype of the given {@link Bean}. A prototype is used as template to {@link #create(Bean) create} + * regular {@link Bean}s. Such beans will inherit the defaults from the prototype what are the + * {@link BeanAccess#getProperties() available properties} as well as their default {@link WritableProperty#getValue() + * value}. + * + * @param the generic type of the {@link Bean}. + * @param type the {@link Class} reflecting the {@link Bean}. + * @param dynamic the {@link BeanAccess#isDynamic() dynamic flag} of the {@link Bean}. + * @param name the explicit {@link BeanAccess#getName() name} of the {@link Bean}. + * @return the prototype instance of the specified {@link Bean}. + */ + BEAN createPrototype(Class type, boolean dynamic, String name); /** * @param the generic type of the {@link Bean}. 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 39c287bcc..5041d4699 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 @@ -80,14 +80,16 @@ public WritableProperty createProperty(String name, GenericType type) return (WritableProperty) getProperty(name); } + @SuppressWarnings("unchecked") @Override - public > PROPERTY createProperty(String name, Class type) { + public > PROPERTY createProperty(String name, GenericType valueType, + Class propertyType) { if (isReadOnly()) { throw new UnsupportedOperationException(); } - getPrototype().createProperty(name, type); - return type.cast(getProperty(name)); + getPrototype().createProperty(name, valueType, propertyType); + return (PROPERTY) getProperty(name); } void createProperties() { 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 9e526c45f..ebcce792b 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 @@ -57,11 +57,12 @@ public BeanAccessPrototype(Class beanClass, String name, BeanFactoryImpl b * The constructor. * * @param master the {@link BeanAccessPrototype} to copy. + * @param name - see {@link #getName()}. * @param dynamic - see {@link #isDynamic()}. */ - public BeanAccessPrototype(BeanAccessPrototype master, boolean dynamic) { + public BeanAccessPrototype(BeanAccessPrototype master, boolean dynamic, String name) { - super(master.beanType, master.getName(), master.beanFactory); + super(master.beanType, name, master.beanFactory); this.beanType = master.beanType; this.name2PropertyMap = new HashMap<>(master.name2PropertyMap.size()); for (BeanPrototypeProperty prototypeProperty : master.name2PropertyMap.values()) { @@ -103,8 +104,10 @@ public WritableProperty createProperty(String name, GenericType proper return property; } + @SuppressWarnings("unchecked") @Override - public > PROPERTY createProperty(String name, Class type) { + public > PROPERTY createProperty(String name, GenericType valueType, + Class propertyType) { if (!this.dynamic) { throw new ReadOnlyException(this.beanType.getSimpleName(), "access.properties"); @@ -113,9 +116,9 @@ public > PROPERTY createProperty(String nam if (prototypeProperty != null) { throw new DuplicateObjectException(this, name, prototypeProperty); } - AbstractProperty property = this.beanFactory.createProperty(name, null, getBean(), type); + AbstractProperty property = this.beanFactory.createProperty(name, valueType, getBean(), propertyType); addProperty(property); - return type.cast(property); + return (PROPERTY) property; } @Override 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 794dcd7b9..4c515c7a1 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 @@ -26,12 +26,8 @@ import net.sf.mmm.util.bean.api.BeanFactory; import net.sf.mmm.util.component.base.AbstractLoggableComponent; import net.sf.mmm.util.property.api.AbstractProperty; -import net.sf.mmm.util.property.api.BooleanProperty; import net.sf.mmm.util.property.api.GenericProperty; -import net.sf.mmm.util.property.api.IntegerProperty; -import net.sf.mmm.util.property.api.LongProperty; import net.sf.mmm.util.property.api.ReadableProperty; -import net.sf.mmm.util.property.api.StringProperty; import net.sf.mmm.util.property.api.WritableProperty; import net.sf.mmm.util.property.api.factory.PropertyFactory; import net.sf.mmm.util.property.api.factory.PropertyFactoryManager; @@ -221,10 +217,14 @@ public BEAN copy(BEAN bean) { } @Override - public BEAN createPrototype(Class type, boolean dynamic) { + public BEAN createPrototype(Class type, boolean dynamic, String name) { BeanAccessPrototype prototype = getPrototypeInternal(type); - BeanAccessPrototype copy = new BeanAccessPrototype<>(prototype, dynamic); + String prototypeName = name; + if (prototypeName == null) { + prototypeName = prototype.getName(); + } + BeanAccessPrototype copy = new BeanAccessPrototype<>(prototype, dynamic, prototypeName); return copy.getBean(); } @@ -382,6 +382,7 @@ private AbstractProperty createProperty(BeanMethod beanMethod, GenericType * @param bean the {@link WritableProperty#getBean() property bean}. * @return the new property instance. */ + @SuppressWarnings("unchecked") protected AbstractProperty createProperty(String name, GenericType type, Bean bean) { return createProperty(name, type, bean, WritableProperty.class); @@ -389,36 +390,33 @@ protected AbstractProperty createProperty(String name, GenericType typ /** * @param the generic property type. + * @param the generic type of the {@link ReadableProperty property}. * @param name the {@link WritableProperty#getName() property name}. - * @param type the {@link WritableProperty#getType() property type}. + * @param valueType the {@link WritableProperty#getType() property type}. * @param bean the {@link WritableProperty#getBean() property bean}. * @param propertyClass the {@link Class} reflecting the {@link WritableProperty} or null if no property * method exists and this method is called for plain getter or setter. * @return the new instance of {@link AbstractProperty}. */ @SuppressWarnings({ "rawtypes", "unchecked" }) - protected AbstractProperty createProperty(String name, GenericType type, Bean bean, - Class propertyClass) { + protected > AbstractProperty createProperty(String name, + GenericType valueType, Bean bean, Class propertyClass) { AbstractProperty result; - PropertyFactory factory = this.propertyFactoryManager.getFactory(propertyClass); + Class valueClass = null; + if (valueType != null) { + valueClass = valueType.getRetrievalClass(); + } + PropertyFactory factory = this.propertyFactoryManager.getFactory(propertyClass, valueClass, false); if (factory != null) { - result = (AbstractProperty) factory.create(name, type, bean, null); + result = (AbstractProperty) factory.create(name, valueType, bean, null); } else if ((!propertyClass.isInterface()) && (!Modifier.isAbstract(propertyClass.getModifiers()))) { - result = createPropertyFromSpecifiedClass(name, type, bean, propertyClass); + result = createPropertyFromSpecifiedClass(name, valueType, bean, propertyClass); } else { - Class valueClass = type.getRetrievalClass(); - if (valueClass == String.class) { - result = new StringProperty(name, bean); - } else if (valueClass == Boolean.class) { - result = new BooleanProperty(name, bean); - } else if (valueClass == Integer.class) { - result = new IntegerProperty(name, bean); - } else if (valueClass == Long.class) { - result = new LongProperty(name, bean); - } else { - result = new GenericProperty<>(name, type, bean); - } + getLogger().debug( + "Could not resolve specific property for class '{}' and value-type '{}'. Using GenericProperty as fallback.", + propertyClass, valueType); + result = new GenericProperty<>(name, valueType, bean); } return result; } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactory.java index 0cf6ae6a3..86a28f406 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactory.java @@ -26,6 +26,12 @@ @ComponentSpecification(plugin = true) public interface PropertyFactory> { + /** + * @return the {@link Class} of the {@link WritableProperty#getValue() property value}. May be null for + * {@link net.sf.mmm.util.property.api.GenericProperty}. + */ + Class getValueClass(); + /** * @return the {@link Class} reflecting the {@link ReadableProperty} interface. */ @@ -43,6 +49,7 @@ public interface PropertyFactory> { /** * Creates a new instance of the property. + * * @param name the {@link ReadableProperty#getName() property name}. * @param valueType is the {@link GenericType} of the value. Only needed for generic properties such as * {@link net.sf.mmm.util.property.api.GenericProperty} or {@link net.sf.mmm.util.property.api.ListProperty}. diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactoryManager.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactoryManager.java index 51c09fb0b..7ace6e0cd 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactoryManager.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/api/factory/PropertyFactoryManager.java @@ -28,9 +28,73 @@ public interface PropertyFactoryManager { * {@link PropertyFactory#getImplementationClass() implementation}. * @return the according {@link PropertyFactory} or {@code null} if no such factory is registered. */ - > PropertyFactory getFactory( + > PropertyFactory getFactoryForPropertyType( Class propertyType); + /** + * @see PropertyFactory#getValueClass() + * + * @param the generic type of the {@link WritableProperty#getValue() property value}. + * @param valueType the {@link Class} reflecting the {@link WritableProperty#getValue() property value}. + * @param polymorphic - {@code true} if also {@link Class#isAssignableFrom(Class) sub-types} of + * {@link PropertyFactory#getValueClass() value classes} should be accepted as {@code valueType}, {@code false} + * otherwise (more efficient). + * @return the according {@link PropertyFactory} or {@code null} if no such factory is registered. + */ + PropertyFactory> getFactoryForValueType(Class valueType, + boolean polymorphic); + + /** + * @param the generic type of the {@link WritableProperty#getValue() property value}. + * @param the generic type of the {@link WritableProperty property}. + * @param propertyType the {@link Class} reflecting the property to create. May be the + * {@link PropertyFactory#getReadableInterface() readable interface}, + * {@link PropertyFactory#getWritableInterface() writable interface}, or the + * {@link PropertyFactory#getImplementationClass() implementation}. + * @param valueType the {@link Class} reflecting the {@link WritableProperty#getValue() property value}. + * @param polymorphic - see {@link #getFactoryForValueType(Class, boolean)}. + * @return the according {@link PropertyFactory} or {@code null} if no such factory is registered. + */ + @SuppressWarnings("unchecked") + default > PropertyFactory getFactory( + Class propertyType, Class valueType, boolean polymorphic) { + + PropertyFactory factory = getFactoryForPropertyType(propertyType); + if (valueType != null) { + if ((factory == null) || (factory.getValueClass() == null)) { + PropertyFactory> valueFactory = getFactoryForValueType(valueType, + polymorphic); + if (valueFactory != null) { + if ((propertyType == null) || (propertyType.isAssignableFrom(valueFactory.getImplementationClass()))) { + factory = (PropertyFactory) valueFactory; + } + } + } + } + return factory; + } + + /** + * @param the generic type of the {@link WritableProperty#getValue() property value}. + * @param the generic type of the {@link WritableProperty property}. + * @param propertyType the {@link Class} reflecting the property to create. May be the + * {@link PropertyFactory#getReadableInterface() readable interface}, + * {@link PropertyFactory#getWritableInterface() writable interface}, or the + * {@link PropertyFactory#getImplementationClass() implementation}. + * @param valueType the {@link Class} reflecting the {@link WritableProperty#getValue() property value}. + * @param polymorphic - see {@link #getFactoryForValueType(Class, boolean)}. + * @return the according {@link PropertyFactory} or {@code null} if no such factory is registered. + */ + default > PropertyFactory getRequiredFactory( + Class propertyType, Class valueType, boolean polymorphic) { + + PropertyFactory factory = getFactory(propertyType, valueType, polymorphic); + if (factory == null) { + throw new ObjectNotFoundException(PropertyFactory.class, propertyType); + } + return factory; + } + /** * @param the generic type of the {@link WritableProperty#getValue() property value}. * @param the generic type of the {@link WritableProperty property} to create. @@ -38,18 +102,20 @@ public interface PropertyFactoryManager { * {@link PropertyFactory#getReadableInterface() readable interface}, * {@link PropertyFactory#getWritableInterface() writable interface}, or the * {@link PropertyFactory#getImplementationClass() implementation}. - * @param name the {@link ReadableProperty#getName() property name}. * @param valueType is the {@link GenericType} of the value. Only needed for generic properties such as * {@link net.sf.mmm.util.property.api.GenericProperty} or {@link net.sf.mmm.util.property.api.ListProperty}. * Can be {@code null} if the generic value type is already bound and should be ignored then. + * @param polymorphic - see {@link #getFactoryForValueType(Class, boolean)}. + * @param name the {@link ReadableProperty#getName() property name}. * @param bean the {@link ReadableProperty#getBean() property bean}. * @param validator the {@link AbstractValidator validator} used for {@link WritableProperty#validate() validation}. * May be {@code null}. * @return the new instance of the property. - * @throws ObjectNotFoundException if no {@link PropertyFactory} was {@link #getFactory(Class) found} for - * {@code propertyType}. + * @throws ObjectNotFoundException if no {@link PropertyFactory} was {@link #getFactoryForPropertyType(Class) found} + * for {@code propertyType}. */ - > PROPERTY create(Class propertyType, String name, - GenericType valueType, Bean bean, AbstractValidator validator) throws ObjectNotFoundException; + > PROPERTY create(Class propertyType, GenericType valueType, + boolean polymorphic, String name, Bean bean, AbstractValidator validator) + throws ObjectNotFoundException; } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/BooleanPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryBoolean.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/BooleanPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryBoolean.java index 3e46d6a21..96ed58040 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/BooleanPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryBoolean.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class BooleanPropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryBoolean extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return Boolean.class; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/DoublePropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryDouble.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/DoublePropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryDouble.java index 0df0d3f84..aa9f14263 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/DoublePropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryDouble.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class DoublePropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryDouble extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return Double.class; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/FloatPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryFloat.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/FloatPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryFloat.java index 6b41c7b7e..242cd2bfe 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/FloatPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryFloat.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class FloatPropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryFloat extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return Float.class; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/GenericPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryGeneric.java similarity index 91% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/GenericPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryGeneric.java index 55b041328..543c7cd86 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/GenericPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryGeneric.java @@ -23,7 +23,13 @@ */ @Named @SuppressWarnings({ "rawtypes", "unchecked" }) -public class GenericPropertyFactory extends AbstractPropertyFactory> { +public class PropertyFactoryGeneric extends AbstractPropertyFactory> { + + @Override + public Class getValueClass() { + + return null; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/IntegerPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryInteger.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/IntegerPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryInteger.java index 14add0105..836dc299d 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/IntegerPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryInteger.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class IntegerPropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryInteger extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return Integer.class; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/ListPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryList.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/ListPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryList.java index 926dea434..8f6cb7b26 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/ListPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryList.java @@ -25,7 +25,13 @@ */ @Named @SuppressWarnings({ "unchecked", "rawtypes" }) -public class ListPropertyFactory extends AbstractPropertyFactory, ListProperty> { +public class PropertyFactoryList extends AbstractPropertyFactory, ListProperty> { + + @Override + public Class> getValueClass() { + + return (Class) ObservableList.class; + } @Override public Class>> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/LongPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryLong.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/LongPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryLong.java index 953fae943..8f184e0c5 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/LongPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryLong.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class LongPropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryLong extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return Long.class; + } @Override public Class> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryManagerImpl.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryManagerImpl.java index 074715c3e..e33c145cd 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryManagerImpl.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryManagerImpl.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import javax.inject.Inject; import javax.inject.Named; @@ -12,7 +13,6 @@ import net.sf.mmm.util.bean.api.Bean; import net.sf.mmm.util.component.base.AbstractLoggableComponent; import net.sf.mmm.util.exception.api.DuplicateObjectException; -import net.sf.mmm.util.exception.api.ObjectNotFoundException; import net.sf.mmm.util.pojo.api.PojoFactory; import net.sf.mmm.util.property.api.ReadableProperty; import net.sf.mmm.util.property.api.factory.PropertyFactory; @@ -31,14 +31,17 @@ public class PropertyFactoryManagerImpl extends AbstractLoggableComponent implem private static PropertyFactoryManagerImpl instance; - private final Map, PropertyFactory> type2factoryMap; + private final Map, PropertyFactory> propertyType2factoryMap; + + private final Map, PropertyFactory> valueType2factoryMap; /** * The constructor. */ public PropertyFactoryManagerImpl() { super(); - this.type2factoryMap = new HashMap<>(); + this.propertyType2factoryMap = new HashMap<>(); + this.valueType2factoryMap = new HashMap<>(); } /** @@ -57,19 +60,41 @@ public void setFactories(List> factories) { */ public void registerFactory(PropertyFactory factory) { + registerFactory(factory, false); + } + + /** + * @param factory the {@link PropertyFactory} to register. + * @param allowOverride - {@code true} if the given {@link PropertyFactory} may override (replace) a previously + * {@link #registerFactory(PropertyFactory, boolean) registered} one. + */ + protected void registerFactory(PropertyFactory factory, boolean allowOverride) { + getInitializationState().requireNotInitilized(); - register(factory.getReadableInterface(), factory); - register(factory.getWritableInterface(), factory); - register(factory.getImplementationClass(), factory); + registerPropertyType(factory.getReadableInterface(), factory, allowOverride); + registerPropertyType(factory.getWritableInterface(), factory, allowOverride); + registerPropertyType(factory.getImplementationClass(), factory, allowOverride); + registerValueType(factory.getValueClass(), factory, allowOverride); + } + + private void registerValueType(Class type, PropertyFactory factory, boolean allowOverride) { + + register(this.valueType2factoryMap, type, factory, allowOverride); } - private void register(Class type, PropertyFactory factory) { + private void registerPropertyType(Class type, PropertyFactory factory, boolean allowOverride) { + + register(this.propertyType2factoryMap, type, factory, allowOverride); + } + + private static void register(Map, PropertyFactory> map, Class type, + PropertyFactory factory, boolean allowOverride) { if (type == null) { return; } - PropertyFactory old = this.type2factoryMap.put(type, factory); - if (old != null) { + PropertyFactory old = map.put(type, factory); + if ((old != null) && !allowOverride) { throw new DuplicateObjectException(factory, type, old); } } @@ -78,7 +103,7 @@ private void register(Class type, PropertyFactory factory) { protected void doInitialize() { super.doInitialize(); - if (this.type2factoryMap.isEmpty()) { + if (this.propertyType2factoryMap.isEmpty()) { registerDefaults(); } } @@ -89,16 +114,16 @@ protected void doInitialize() { @SuppressWarnings("rawtypes") protected void registerDefaults() { - registerFactory(new StringPropertyFactory()); - registerFactory(new GenericPropertyFactory()); - registerFactory(new BooleanPropertyFactory()); - registerFactory(new DoublePropertyFactory()); - registerFactory(new FloatPropertyFactory()); - registerFactory(new IntegerPropertyFactory()); - registerFactory(new ListPropertyFactory()); - registerFactory(new LongPropertyFactory()); - registerFactory(new SetPropertyFactory()); - registerFactory(new MapPropertyFactory()); + registerFactory(new PropertyFactoryString()); + registerFactory(new PropertyFactoryGeneric()); + registerFactory(new PropertyFactoryBoolean()); + registerFactory(new PropertyFactoryDouble()); + registerFactory(new PropertyFactoryFloat()); + registerFactory(new PropertyFactoryInteger()); + registerFactory(new PropertyFactoryList()); + registerFactory(new PropertyFactoryLong()); + registerFactory(new PropertyFactorySet()); + registerFactory(new PropertyFactoryMap()); } /** @@ -132,20 +157,39 @@ protected void doInitialized() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override - public > PropertyFactory getFactory( + public > PropertyFactory getFactoryForPropertyType( Class propertyType) { - return (PropertyFactory) this.type2factoryMap.get(propertyType); + return (PropertyFactory) this.propertyType2factoryMap.get(propertyType); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public PropertyFactory> getFactoryForValueType(Class valueType, + boolean polymorphic) { + + PropertyFactory factory = this.valueType2factoryMap.get(valueType); + if ((factory == null) && polymorphic) { + for (Entry, PropertyFactory> entry : this.valueType2factoryMap.entrySet()) { + if (entry.getKey().isAssignableFrom(valueType)) { + factory = entry.getValue(); + break; + } + } + } + return (PropertyFactory) factory; } @Override - public > PROPERTY create(Class propertyType, String name, - GenericType valueType, Bean bean, AbstractValidator validator) { + public > PROPERTY create(Class propertyType, + GenericType valueType, boolean polymorphic, String name, Bean bean, + AbstractValidator validator) { - PropertyFactory factory = getFactory(propertyType); - if (factory == null) { - throw new ObjectNotFoundException(PropertyFactory.class, propertyType); + Class valueClass = null; + if (valueType != null) { + valueClass = valueType.getRetrievalClass(); } + PropertyFactory factory = getRequiredFactory(propertyType, valueClass, polymorphic); return factory.create(name, valueType, bean, validator); } diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/MapPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryMap.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/MapPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryMap.java index 819212638..5e4b984f5 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/MapPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryMap.java @@ -27,7 +27,13 @@ */ @Named @SuppressWarnings({ "unchecked", "rawtypes" }) -public class MapPropertyFactory extends AbstractPropertyFactory, MapProperty> { +public class PropertyFactoryMap extends AbstractPropertyFactory, MapProperty> { + + @Override + public Class> getValueClass() { + + return (Class) ObservableMap.class; + } @Override public Class>> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/SetPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactorySet.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/SetPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactorySet.java index c4bf56ed1..224e2c8cb 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/SetPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactorySet.java @@ -25,7 +25,13 @@ */ @Named @SuppressWarnings({ "unchecked", "rawtypes" }) -public class SetPropertyFactory extends AbstractPropertyFactory, SetProperty> { +public class PropertyFactorySet extends AbstractPropertyFactory, SetProperty> { + + @Override + public Class> getValueClass() { + + return (Class) ObservableSet.class; + } @Override public Class>> getReadableInterface() { diff --git a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/StringPropertyFactory.java b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryString.java similarity index 90% rename from mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/StringPropertyFactory.java rename to mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryString.java index cc6bfe665..0bc263fca 100644 --- a/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/StringPropertyFactory.java +++ b/mmm-util-property/src/main/java/net/sf/mmm/util/property/impl/factory/PropertyFactoryString.java @@ -21,7 +21,13 @@ * @since 8.0.0 */ @Named -public class StringPropertyFactory extends AbstractPropertyFactory { +public class PropertyFactoryString extends AbstractPropertyFactory { + + @Override + public Class getValueClass() { + + return String.class; + } @Override public Class> getReadableInterface() {