/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jspresso is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.util.accessor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.apache.commons.beanutils.PropertyUtilsBean; import org.apache.commons.beanutils.expression.DefaultResolver; import org.jspresso.framework.util.bean.PropertyHelper; /** * Abstract class for property accessors. * * @author Vincent Vandenschrick */ public abstract class AbstractPropertyAccessor implements IAccessor { private final String property; private static final PropertyUtilsBean PROPERTY_UTILS_BEAN = new PropertyUtilsBean(); static { PROPERTY_UTILS_BEAN.setResolver(new DefaultResolver() { /** * Supports properties starting with a single lowercase letter. * <p/> * {@inheritDoc} */ @Override public String getProperty(String expression) { String prop = super.getProperty(expression); prop = PropertyHelper.toJavaBeanPropertyName(prop); return prop; } }); } /** * Constructs a new {@code AbstractPropertyAccessor} instance. * * @param property * the property to access. */ public AbstractPropertyAccessor(String property) { this.property = property; } /** * Gets a property value, taking care of Map vs bean implementation and nested * properties. * * @param <T> * type inference return. * @param target * the target object. * @return the property value. * * @throws IllegalAccessException * whenever an exception occurs. * @throws InvocationTargetException * whenever an exception occurs. * @throws NoSuchMethodException * whenever an exception occurs. */ @SuppressWarnings("unchecked") @Override public <T> T getValue(Object target) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Object finalTarget = getLastNestedTarget(target, getProperty()); if (finalTarget != null) { if (finalTarget instanceof Map<?, ?>) { if (PropertyHelper.getPropertyNames(finalTarget.getClass()).contains(getLastNestedProperty())) { // We are explicitly on a bean property. Do not use // PROPERTY_UTILS_BEAN.getProperty since it will detect that the // target // is a Map and access its properties as such. return (T) PROPERTY_UTILS_BEAN.getSimpleProperty(finalTarget, getLastNestedProperty()); } return (T) PROPERTY_UTILS_BEAN.getProperty(finalTarget, getLastNestedProperty()); } return (T) PROPERTY_UTILS_BEAN.getProperty(finalTarget, getLastNestedProperty()); } return null; } /** * Sets a property value, taking care of Map vs bean implementation and nested * properties. * * @param target * the target object. * @param value * the value to set. * @throws IllegalAccessException * whenever an exception occurs. * @throws InvocationTargetException * whenever an exception occurs. * @throws NoSuchMethodException * whenever an exception occurs. */ @Override public void setValue(Object target, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { try { Object finalTarget = getLastNestedTarget(target, getProperty()); if (finalTarget != null) { if (finalTarget instanceof Map<?, ?>) { if (PropertyHelper.getPropertyNames(finalTarget.getClass()).contains(getLastNestedProperty())) { // We are explicitly on a bean property. Do not use // PROPERTY_UTILS_BEAN.getProperty since it will detect that the // target is a Map and access its properties as such. PROPERTY_UTILS_BEAN.setSimpleProperty(finalTarget, getLastNestedProperty(), value); } else { PROPERTY_UTILS_BEAN.setProperty(finalTarget, getLastNestedProperty(), value); } } else { PROPERTY_UTILS_BEAN.setProperty(finalTarget, getLastNestedProperty(), value); } } } catch (InvocationTargetException ex) { // un-nest invocation target exceptions so that the original // one can be correctly handled by the exception handlers. if (ex.getCause() instanceof RuntimeException) { throw (RuntimeException) ex.getCause(); } throw ex; } } /** * Gets the final nested property. * * @return the final nested property. */ protected String getLastNestedProperty() { return getProperty().substring(getProperty().lastIndexOf(IAccessor.NESTED_DELIM) + 1); } /** * Gets the last target of a nested property. * * @param target * the starting target. * @param prop * the property. * @return the last target of a nested property. * * @throws IllegalAccessException * whenever an exception occurs. * @throws InvocationTargetException * whenever an exception occurs. * @throws NoSuchMethodException * whenever an exception occurs. */ protected Object getLastNestedTarget(Object target, String prop) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { if (target != null) { int indexOfNestedDelim = prop.indexOf(IAccessor.NESTED_DELIM); if (indexOfNestedDelim < 0) { return target; } if (target instanceof Map<?, ?>) { if (PropertyHelper.getPropertyNames(target.getClass()).contains(prop)) { // We are explicitly on a bean property. Do not use // PROPERTY_UTILS_BEAN.getProperty since it will detect that the // target is // a // Map and access its properties as such. return getLastNestedTarget(PROPERTY_UTILS_BEAN.getSimpleProperty(target, prop.substring(0, indexOfNestedDelim)), prop.substring(indexOfNestedDelim + 1)); } return getLastNestedTarget(PROPERTY_UTILS_BEAN.getProperty(target, prop.substring(0, indexOfNestedDelim)), prop.substring(indexOfNestedDelim + 1)); } return getLastNestedTarget(PROPERTY_UTILS_BEAN.getProperty(target, prop.substring(0, indexOfNestedDelim)), prop.substring(indexOfNestedDelim + 1)); } return null; } /** * Gets the property property. * * @return the property. */ protected String getProperty() { return property; } }