/** * Copyright (C) 2015 Valkyrie RCP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.valkyriercp.binding.beans; import org.springframework.beans.*; import org.springframework.binding.collection.AbstractCachingMapDecorator; import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; /** * This implementation extends {@link AbstractMemberPropertyAccessor} with the * functionality of nested property handling. * * @author Arne Limburg * */ public abstract class AbstractNestedMemberPropertyAccessor extends AbstractMemberPropertyAccessor { private AbstractNestedMemberPropertyAccessor parentPropertyAccessor; private String basePropertyName; private final ChildPropertyAccessorCache childPropertyAccessors = new ChildPropertyAccessorCache(); private final boolean strictNullHandlingEnabled; protected AbstractNestedMemberPropertyAccessor(Class targetClass, boolean fieldAccessEnabled, boolean strictNullHandlingEnabled) { super(targetClass, fieldAccessEnabled); this.strictNullHandlingEnabled = strictNullHandlingEnabled; } public AbstractNestedMemberPropertyAccessor( AbstractNestedMemberPropertyAccessor parent, String baseProperty) { super(parent.getPropertyType(baseProperty), parent .isFieldAccessEnabled()); parentPropertyAccessor = parent; basePropertyName = baseProperty; strictNullHandlingEnabled = parent.strictNullHandlingEnabled; } public boolean isStrictNullHandlingEnabled() { return strictNullHandlingEnabled; } protected AbstractNestedMemberPropertyAccessor getParentPropertyAccessor() { return parentPropertyAccessor; } protected String getBasePropertyName() { return basePropertyName; } public Object getTarget() { if (parentPropertyAccessor != null && basePropertyName != null) { return parentPropertyAccessor.getPropertyValue(basePropertyName); } else { return null; } } @Override public Class getTargetClass() { if (parentPropertyAccessor != null) { return parentPropertyAccessor.getPropertyType(basePropertyName); } else { return super.getTargetClass(); } } @Override public boolean isReadableProperty(String propertyPath) { if (PropertyAccessorUtils.isNestedProperty(propertyPath)) { String baseProperty = getBasePropertyName(propertyPath); String childPropertyPath = getChildPropertyPath(propertyPath); if (!super.isReadableProperty(baseProperty)) { return false; } else { return ((PropertyAccessor) childPropertyAccessors .get(baseProperty)) .isReadableProperty(childPropertyPath); } } else { return super.isReadableProperty(propertyPath); } } @Override public boolean isWritableProperty(String propertyPath) { if (PropertyAccessorUtils.isNestedProperty(propertyPath)) { String baseProperty = getBasePropertyName(propertyPath); String childPropertyPath = getChildPropertyPath(propertyPath); return super.isReadableProperty(baseProperty) && ((PropertyAccessor) childPropertyAccessors .get(baseProperty)) .isWritableProperty(childPropertyPath); } else { return super.isWritableProperty(propertyPath); } } @Override public Class getPropertyType(String propertyPath) { if (PropertyAccessorUtils.isNestedProperty(propertyPath)) { String baseProperty = getBasePropertyName(propertyPath); String childPropertyPath = getChildPropertyPath(propertyPath); return ((PropertyAccessor) childPropertyAccessors.get(baseProperty)) .getPropertyType(childPropertyPath); } else { return super.getPropertyType(propertyPath); } } @Override public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException { try { String actualPropertyName = org.springframework.beans.PropertyAccessorUtils .getPropertyName(propertyName); PropertyDescriptor pd = new PropertyDescriptor(actualPropertyName, getTargetClass()); if (pd != null) { Class type = getPropertyType(propertyName); if (pd.getReadMethod() != null) { return new TypeDescriptor(new MethodParameter( pd.getReadMethod(), -1)); } else if (pd.getWriteMethod() != null) { return new TypeDescriptor( BeanUtils.getWriteMethodParameter(pd)); } } } catch (InvalidPropertyException ex) { // Consider as not determinable. } catch (IntrospectionException e) { throw new RuntimeException("Error creating property descriptor", e); } return null; } @Override public Object getPropertyValue(String propertyPath) { if (PropertyAccessorUtils.isNestedProperty(propertyPath)) { String baseProperty = getBasePropertyName(propertyPath); String childPropertyPath = getChildPropertyPath(propertyPath); return ((PropertyAccessor) childPropertyAccessors.get(baseProperty)) .getPropertyValue(childPropertyPath); } else if (isStrictNullHandlingEnabled() && getTarget() == null) { throw new NullValueInNestedPathException(getTargetClass(), propertyPath); } else { return super.getPropertyValue(propertyPath); } } @Override public void setPropertyValue(String propertyPath, Object value) { if (PropertyAccessorUtils.isNestedProperty(propertyPath)) { String baseProperty = getBasePropertyName(propertyPath); String childPropertyPath = getChildPropertyPath(propertyPath); ((PropertyAccessor) childPropertyAccessors.get(baseProperty)) .setPropertyValue(childPropertyPath, value); } else if (isStrictNullHandlingEnabled() && getTarget() == null) { throw new NullValueInNestedPathException(getTargetClass(), propertyPath); } else { super.setPropertyValue(propertyPath, value); } } protected String getBasePropertyName(String propertyPath) { int index = PropertyAccessorUtils .getFirstNestedPropertySeparatorIndex(propertyPath); return index == -1 ? propertyPath : propertyPath.substring(0, index); } protected String getChildPropertyPath(String propertyPath) { int index = PropertyAccessorUtils .getFirstNestedPropertySeparatorIndex(propertyPath); if (index == -1) { return ""; } return propertyPath.substring(index + 1); } protected PropertyAccessor getChildPropertyAccessor(String propertyName) { return (PropertyAccessor) childPropertyAccessors.get(propertyName); } protected abstract AbstractNestedMemberPropertyAccessor createChildPropertyAccessor( String propertyName); protected void clearChildPropertyAccessorCache() { childPropertyAccessors.clear(); } private class ChildPropertyAccessorCache extends AbstractCachingMapDecorator { @Override protected Object create(Object propertyName) { return createChildPropertyAccessor((String) propertyName); } } }