Skip to content

Commit

Permalink
#163: support for property alias
Browse files Browse the repository at this point in the history
  • Loading branch information
hohwille committed Feb 9, 2016
1 parent becd04f commit 519231b
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,24 @@ public interface BeanAccess {
Iterable<WritableProperty<?>> getProperties();

/**
* @param name the {@link WritableProperty#getName() name} of the requested property.
* @return an {@link Iterable} with all defined {@link #getPropertyNameForAlias(String) aliases}.
*/
Iterable<String> getAliases();

/**
* An alias is an alternative for a {@link #getProperty(String) property} {@link WritableProperty#getName() name}. It
* is defined by annotating the property method with {@link Named} and allows to support a property under a legacy
* name after it has been renamed as well as to use a technical name containing special characters (e.g. "@" or ".")
* for very specific cases.
*
* @param alias the alias name.
* @return the resolved {@link WritableProperty#getName() property name} or {@code null} if no such alias is defined.
*/
String getPropertyNameForAlias(String alias);

/**
* @param name the {@link WritableProperty#getName() name} of the requested property or a potential
* {@link #getPropertyNameForAlias(String) alias} of the property.
* @return the requested {@link WritableProperty} or <code>null</code> if no such property exists.
*/
WritableProperty<?> getProperty(String name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface BeanFactory {
*/
default <BEAN extends Bean> BEAN createPrototype(Class<BEAN> type) {

return createPrototype(type, false);
return createPrototype(type, false, null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,6 @@ public Iterable<WritableProperty<?>> getProperties() {
return this;
}

@Override
public WritableProperty<?> getProperty(String propertyName) {

BeanPrototypeProperty prototypeProperty = getPrototype().getName2PropertyMap().get(propertyName);
if (prototypeProperty != null) {
return getProperty(prototypeProperty, true);
}
return null;
}

/**
* Gets the {@link WritableProperty} for the given <code>index</code>.
*
Expand All @@ -97,20 +87,11 @@ public boolean isReadOnly() {
return false;
}

// /**
// * @param bean is the bean to set
// */
// void setBean(Bean bean) {
//
// assert ((this.bean == null) || (this.bean == bean));
// this.bean = bean;
// }

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

Object result = null;
BeanPrototypeOperation operation = getPrototype().getMethod2OperationMap().get(method);
BeanPrototypeOperation operation = getPrototype().getOperation(method);
if (operation != null) {
result = operation.invoke(this, args);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.bean.impl;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import net.sf.mmm.util.bean.api.Bean;
import net.sf.mmm.util.bean.api.BeanAccess;
Expand Down Expand Up @@ -48,13 +48,35 @@ public BeanAccessPrototype<BEAN> getPrototype() {
return this.prototype;
}

@Override
public WritableProperty<?> getProperty(String propertyName) {

BeanPrototypeProperty prototypeProperty = getPrototype().getPrototypeProperty(propertyName);
if (prototypeProperty != null) {
return getProperty(prototypeProperty, true);
}
return null;
}

@Override
public Iterator<WritableProperty<?>> iterator() {

createProperties();
return new ArrayIterator<>(this.properties);
}

@Override
public Iterable<String> getAliases() {

return this.prototype.getAliases();
}

@Override
public String getPropertyNameForAlias(String alias) {

return this.prototype.getPropertyNameForAlias(alias);
}

@Override
protected WritableProperty<?> getProperty(BeanPrototypeProperty prototypeProperty, boolean required) {

Expand Down Expand Up @@ -94,15 +116,15 @@ public <V, PROPERTY extends WritableProperty<V>> PROPERTY createProperty(String

void createProperties() {

Map<String, BeanPrototypeProperty> name2PropertyMap = getPrototype().getName2PropertyMap();
int size = name2PropertyMap.size();
Collection<BeanPrototypeProperty> prototypeProperties = this.prototype.getPrototypeProperties();
int size = prototypeProperties.size();
int length = this.properties.length;
if (length == size) {
return;
}
WritableProperty<?>[] newProperties = new WritableProperty<?>[size];
System.arraycopy(this.properties, 0, newProperties, 0, length);
for (BeanPrototypeProperty prototypeProperty : name2PropertyMap.values()) {
for (BeanPrototypeProperty prototypeProperty : prototypeProperties) {
int i = prototypeProperty.getIndex();
if (i >= length) {
newProperties[i] = createProperty(prototypeProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
package net.sf.mmm.util.bean.impl;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.mmm.util.bean.api.Bean;
import net.sf.mmm.util.bean.api.BeanAccess;
import net.sf.mmm.util.bean.api.BeanFactory;
Expand All @@ -27,11 +32,15 @@
*/
public class BeanAccessPrototype<BEAN extends Bean> extends BeanAccessBase<BEAN> {

private static final Logger LOG = LoggerFactory.getLogger(BeanAccessPrototype.class);

private final Map<String, BeanPrototypeProperty> name2PropertyMap;

private final Map<Method, BeanPrototypeOperation> method2OperationMap;

private final Class<BEAN> beanType;
private final Map<String, String> aliasMap;

private final Collection<String> aliases;

private final boolean dynamic;

Expand All @@ -46,9 +55,10 @@ public class BeanAccessPrototype<BEAN extends Bean> extends BeanAccessBase<BEAN>
*/
public BeanAccessPrototype(Class<BEAN> beanClass, String name, BeanFactoryImpl beanFactory) {
super(beanClass, name, beanFactory);
this.beanType = beanClass;
this.name2PropertyMap = new HashMap<>();
this.method2OperationMap = new HashMap<>();
this.aliasMap = new HashMap<>();
this.aliases = Collections.unmodifiableSet(this.aliasMap.keySet());
this.dynamic = false;
this.beanFactory = beanFactory;
}
Expand All @@ -62,9 +72,10 @@ public BeanAccessPrototype(Class<BEAN> beanClass, String name, BeanFactoryImpl b
*/
public BeanAccessPrototype(BeanAccessPrototype<BEAN> master, boolean dynamic, String name) {

super(master.beanType, name, master.beanFactory);
this.beanType = master.beanType;
super(master.getBeanClass(), name, master.beanFactory);
this.name2PropertyMap = new HashMap<>(master.name2PropertyMap.size());
this.aliasMap = master.aliasMap;
this.aliases = master.aliases;
for (BeanPrototypeProperty prototypeProperty : master.name2PropertyMap.values()) {
AbstractProperty<?> property = prototypeProperty.getProperty();
BeanPrototypeProperty copy = new BeanPrototypeProperty(property.copy(getBean()),
Expand All @@ -89,28 +100,13 @@ public Iterator<WritableProperty<?>> iterator() {
return (Iterator) this.name2PropertyMap.values().stream().map(x -> x.getProperty()).iterator();
}

@Override
public <V> WritableProperty<V> createProperty(String name, GenericType<V> propertyType) {

if (!this.dynamic) {
throw new ReadOnlyException(this.beanType.getSimpleName(), "access.properties");
}
BeanPrototypeProperty prototypeProperty = this.name2PropertyMap.get(name);
if (prototypeProperty != null) {
throw new DuplicateObjectException(this, name, prototypeProperty);
}
AbstractProperty<V> property = this.beanFactory.createProperty(name, propertyType, getBean());
addProperty(property);
return property;
}

@SuppressWarnings("unchecked")
@Override
public <V, PROPERTY extends WritableProperty<V>> PROPERTY createProperty(String name, GenericType<V> valueType,
Class<PROPERTY> propertyType) {

if (!this.dynamic) {
throw new ReadOnlyException(this.beanType.getSimpleName(), "access.properties");
throw new ReadOnlyException(getBeanClass().getSimpleName(), "access.properties");
}
BeanPrototypeProperty prototypeProperty = this.name2PropertyMap.get(name);
if (prototypeProperty != null) {
Expand All @@ -128,19 +124,77 @@ protected WritableProperty<?> getProperty(BeanPrototypeProperty prototypePropert
}

/**
* @return the method2operationMap
* @see #getOperation(Method)
* @param operation the {@link BeanPrototypeOperation} to register.
*/
protected void registerOperation(BeanPrototypeOperation operation) {

this.method2OperationMap.put(operation.getMethod(), operation);
}

/**
* @param method the {@link Method} to lookup.
* @return the {@link BeanPrototypeOperation} for the given {@link Method} or {@code null} if not defined.
*/
protected BeanPrototypeOperation getOperation(Method method) {

return this.method2OperationMap.get(method);
}

/**
* @see #getPropertyNameForAlias(String)
*
* @param alias the new {@link #getPropertyNameForAlias(String) alias} to register.
* @param propertyName the {@link WritableProperty#getName() property name} to map to.
*/
protected void registerAlias(String alias, String propertyName) {

String old = this.aliasMap.put(alias, propertyName);
if ((old != null) && !propertyName.equals(old)) {
LOG.warn("Replaced alias {} to {} with {}.", alias, old, propertyName);
}
}

@Override
public WritableProperty<?> getProperty(String propertyName) {

BeanPrototypeProperty prototypeProperty = getPrototypeProperty(propertyName);
if (prototypeProperty != null) {
return prototypeProperty.getProperty();
}
return null;
}

/**
* @param name the {@link WritableProperty#getName() property name}.
* @return the corresponding {@link BeanPrototypeProperty} or {@code null} if not defined.
*/
protected BeanPrototypeProperty getPrototypeProperty(String name) {

BeanPrototypeProperty result = this.name2PropertyMap.get(name);
if (result == null) {
String propertyName = this.aliasMap.get(name);
if (propertyName != null) {
result = this.name2PropertyMap.get(propertyName);
}
}
return result;
}

/**
* @return the current number of {@link #getProperties() properties} defined in this prototype.
*/
protected Map<Method, BeanPrototypeOperation> getMethod2OperationMap() {
protected int getPropertyCount() {

return this.method2OperationMap;
return this.name2PropertyMap.size();
}

/**
* @return the name2propertyMap
* @return a {@link Collection} with all {@link BeanPrototypeProperty properties} defined in this prototype.
*/
protected Map<String, BeanPrototypeProperty> getName2PropertyMap() {
protected Collection<BeanPrototypeProperty> getPrototypeProperties() {

return this.name2PropertyMap;
return this.name2PropertyMap.values();
}

/**
Expand All @@ -152,13 +206,16 @@ protected void addProperty(AbstractProperty<?> property) {
this.name2PropertyMap.put(property.getName(), prototypeProperty);
}

/**
* @return the type
*/
@Override
public Class<BEAN> getBeanClass() {
public String getPropertyNameForAlias(String alias) {

return this.aliasMap.get(alias);
}

@Override
public Iterable<String> getAliases() {

return this.beanType;
return this.aliases;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public BeanAccessReadOnly(BeanFactoryImpl beanFactory, BeanAccessPrototype<BEAN>
@Override
protected WritableProperty<?> createProperty(BeanPrototypeProperty prototypeProperty) {

WritableProperty<?> property = this.delegate.getProperty(prototypeProperty, true);
WritableProperty<?> property = this.delegate.getProperty(prototypeProperty.getProperty().getName());
return new ReadOnlyPropertyImpl<>(property);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,20 +321,19 @@ protected void collectMethods(Class<?> type, BeanIntrospectionData introspection
private void processMethods(BeanIntrospectionData introspectionData, BeanAccessPrototype<?> prototype,
GenericType<?> beanType) {

Map<Method, BeanPrototypeOperation> method2OperationMap = prototype.getMethod2OperationMap();
Map<String, BeanPrototypeProperty> name2PropertyMap = prototype.getName2PropertyMap();
for (BeanMethod beanMethod : introspectionData.methods) {
BeanMethodType methodType = beanMethod.getMethodType();
if ((methodType == BeanMethodType.GET) || (methodType == BeanMethodType.SET)) {
String propertyName = beanMethod.getPropertyName();
BeanPrototypeProperty prototypeProperty = name2PropertyMap.get(propertyName);
BeanPrototypeProperty prototypeProperty = prototype.getPrototypeProperty(propertyName);
if (prototypeProperty == null) {
GenericType<?> propertyType = this.reflectionUtil.createGenericType(beanMethod.getPropertyType(),
beanType);
AbstractProperty<?> property = createProperty(propertyName, propertyType, prototype.getBean());
prototype.addProperty(property);
}
}

BeanPrototypeOperation operation;
if ((methodType == BeanMethodType.EQUALS) && (introspectionData.customEquals != null)) {
operation = new BeanPrototypeOperationCustomEquals(prototype, beanMethod.getMethod(),
Expand All @@ -345,7 +344,14 @@ private void processMethods(BeanIntrospectionData introspectionData, BeanAccessP
} else {
operation = BeanPrototypeOperation.create(beanMethod, prototype);
}
method2OperationMap.put(beanMethod.getMethod(), operation);
prototype.registerOperation(operation);

if ((methodType == BeanMethodType.GET) || (methodType == BeanMethodType.PROPERTY)) {
Named alias = beanMethod.getMethod().getAnnotation(Named.class);
if (alias != null) {
prototype.registerAlias(alias.value(), beanMethod.getPropertyName());
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public static BeanPrototypeOperation create(BeanMethod beanMethod, BeanAccessPro
if (methodType != null) {
String propertyName = beanMethod.getPropertyName();
Method method = beanMethod.getMethod();
BeanPrototypeProperty prototypeProperty = prototype.getName2PropertyMap().get(propertyName);
BeanPrototypeProperty prototypeProperty = prototype.getPrototypeProperty(propertyName);
switch (methodType) {
case GET:
return new BeanPrototypeOperationGet(prototype, method, prototypeProperty);
Expand Down
Loading

0 comments on commit 519231b

Please sign in to comment.