SimpleBeanReadWrapper.java

package fr.sii.ogham.core.util.bean;

import static fr.sii.ogham.core.util.bean.BeanWrapperUtils.getClassName;
import static fr.sii.ogham.core.util.bean.BeanWrapperUtils.getReadMethods;
import static fr.sii.ogham.core.util.bean.BeanWrapperUtils.isInvalid;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import fr.sii.ogham.core.exception.util.InvalidPropertyException;

/**
 * Simple implementation that wraps a bean in order to access the properties of
 * the bean.
 * 
 * This implementation delegates the access to the properties to
 * {@link Accessor}s.
 * 
 * @author Aurélien Baudet
 *
 */
public class SimpleBeanReadWrapper implements BeanReadWrapper {
	private final Object bean;
	private final Map<String, Accessor<Object>> accessors;
	private final boolean failOnMissingProperty;

	/**
	 * Builds the map of accessors for each bean property.
	 * 
	 * If a property doesn't exist, an {@link InvalidPropertyException} is
	 * thrown.
	 * 
	 * @param bean
	 *            the bean that may have properties to access later
	 */
	public SimpleBeanReadWrapper(Object bean) {
		this(bean, true);
	}

	/**
	 * Builds the map of accessors for each bean property.
	 * 
	 * @param bean
	 *            the bean that may have properties to access later
	 * @param failOnMissingProperty
	 *            if false null is returned if the property doesn't exist, if
	 *            true an {@link InvalidPropertyException} is thrown if the
	 *            property doesn't exist
	 */
	public SimpleBeanReadWrapper(Object bean, boolean failOnMissingProperty) {
		super();
		this.bean = bean;
		this.accessors = new HashMap<>();
		this.failOnMissingProperty = failOnMissingProperty;
		initialize(bean);
	}

	@Override
	public Object getPropertyValue(String name) {
		if (getWrappedBean() == null) {
			return null;
		}

		Accessor<Object> accessor = accessors.get(name);
		if (failOnMissingProperty && accessor == null) {
			throw new InvalidPropertyException("No accessor for property '" + name + "' on bean '" + getClassName(bean) + "'", bean, name);
		}

		return accessor == null ? null : accessor.getValue();
	}

	@Override
	public List<String> getProperties() {
		return new ArrayList<>(accessors.keySet());
	}

	@Override
	public Object getWrappedBean() {
		return bean;
	}

	@SuppressWarnings("unchecked")
	private void initialize(Object bean) {
		if (bean == null) {
			return;
		}

		if (isInvalid(bean)) {
			throw new IllegalArgumentException(getClassName(bean) + " values can't be used as bean");
		} else if (bean instanceof Collection) {
			initializeCollection((Collection<Object>) bean, accessors);
		} else if (bean instanceof Map) {
			initializeMap((Map<Object, Object>) bean, accessors);
		} else {
			initializeBean(bean, accessors);
		}
	}

	private static void initializeCollection(Collection<Object> collection, Map<String, Accessor<Object>> accessors) {
		int i = 0;
		for (Iterator<Object> it = collection.iterator(); it.hasNext(); i++) {
			accessors.put(Integer.toString(i), new DirectAccessor<>(it.next()));
		}
	}

	private static void initializeMap(Map<Object, Object> map, Map<String, Accessor<Object>> accessors) {
		for (Entry<Object, Object> entry : map.entrySet()) {
			accessors.put(entry.getKey().toString(), new DirectAccessor<>(entry.getValue()));
		}
	}

	private static void initializeBean(Object bean, Map<String, Accessor<Object>> accessors) {
		Map<String, Method> readMethods = getReadMethods(bean);
		for(Entry<String, Method> entry : readMethods.entrySet()) {
			String name = entry.getKey();
			Method readMethod = entry.getValue();
			if (readMethod != null) {
				accessors.put(name, new ReadMethodAccessor<>(bean, name, readMethod));
			}
		}
	}
}