001    /*
002     * Copyright 2007 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.beans;
017    
018    import org.springframework.beans.NullValueInNestedPathException;
019    import org.springframework.beans.PropertyAccessor;
020    import org.springframework.util.CachingMapDecorator;
021    
022    /**
023     * This implementation extends {@link AbstractMemberPropertyAccessor} with the
024     * functionality of nested property handling.
025     *
026     * @author Arne Limburg
027     *
028     */
029    public abstract class AbstractNestedMemberPropertyAccessor extends AbstractMemberPropertyAccessor {
030    
031            private AbstractNestedMemberPropertyAccessor parentPropertyAccessor;
032    
033            private String basePropertyName;
034    
035            private final ChildPropertyAccessorCache childPropertyAccessors = new ChildPropertyAccessorCache();
036    
037            private final boolean strictNullHandlingEnabled;
038    
039            protected AbstractNestedMemberPropertyAccessor(Class targetClass, boolean fieldAccessEnabled,
040                            boolean strictNullHandlingEnabled) {
041                    super(targetClass, fieldAccessEnabled);
042                    this.strictNullHandlingEnabled = strictNullHandlingEnabled;
043            }
044    
045            public AbstractNestedMemberPropertyAccessor(AbstractNestedMemberPropertyAccessor parent, String baseProperty) {
046                    super(parent.getPropertyType(baseProperty), parent.isFieldAccessEnabled());
047                    parentPropertyAccessor = parent;
048                    basePropertyName = baseProperty;
049                    strictNullHandlingEnabled = parent.strictNullHandlingEnabled;
050            }
051    
052            public boolean isStrictNullHandlingEnabled() {
053                    return strictNullHandlingEnabled;
054            }
055    
056            protected AbstractNestedMemberPropertyAccessor getParentPropertyAccessor() {
057                    return parentPropertyAccessor;
058            }
059    
060            protected String getBasePropertyName() {
061                    return basePropertyName;
062            }
063    
064            public Object getTarget() {
065                    if (parentPropertyAccessor != null && basePropertyName != null) {
066                            return parentPropertyAccessor.getPropertyValue(basePropertyName);
067                    }
068                    else {
069                            return null;
070                    }
071            }
072    
073            public Class getTargetClass() {
074                    if (parentPropertyAccessor != null) {
075                            return parentPropertyAccessor.getPropertyType(basePropertyName);
076                    }
077                    else {
078                            return super.getTargetClass();
079                    }
080            }
081    
082            public boolean isReadableProperty(String propertyPath) {
083                    if (PropertyAccessorUtils.isNestedProperty(propertyPath)) {
084                            String baseProperty = getBasePropertyName(propertyPath);
085                            String childPropertyPath = getChildPropertyPath(propertyPath);
086                            if (!super.isReadableProperty(baseProperty)) {
087                                    return false;
088                            }
089                            else {
090                                    return ((PropertyAccessor) childPropertyAccessors.get(baseProperty))
091                                                    .isReadableProperty(childPropertyPath);
092                            }
093                    }
094                    else {
095                            return super.isReadableProperty(propertyPath);
096                    }
097            }
098    
099            public boolean isWritableProperty(String propertyPath) {
100                    if (PropertyAccessorUtils.isNestedProperty(propertyPath)) {
101                            String baseProperty = getBasePropertyName(propertyPath);
102                            String childPropertyPath = getChildPropertyPath(propertyPath);
103                            return super.isReadableProperty(baseProperty)
104                                            && ((PropertyAccessor) childPropertyAccessors.get(baseProperty))
105                                                            .isWritableProperty(childPropertyPath);
106                    }
107                    else {
108                            return super.isWritableProperty(propertyPath);
109                    }
110            }
111    
112            public Class getPropertyType(String propertyPath) {
113                    if (PropertyAccessorUtils.isNestedProperty(propertyPath)) {
114                            String baseProperty = getBasePropertyName(propertyPath);
115                            String childPropertyPath = getChildPropertyPath(propertyPath);
116                            return ((PropertyAccessor) childPropertyAccessors.get(baseProperty)).getPropertyType(childPropertyPath);
117                    }
118                    else {
119                            return super.getPropertyType(propertyPath);
120                    }
121            }
122    
123            public Object getPropertyValue(String propertyPath) {
124                    if (PropertyAccessorUtils.isNestedProperty(propertyPath)) {
125                            String baseProperty = getBasePropertyName(propertyPath);
126                            String childPropertyPath = getChildPropertyPath(propertyPath);
127                            return ((PropertyAccessor) childPropertyAccessors.get(baseProperty)).getPropertyValue(childPropertyPath);
128                    }
129                    else if (isStrictNullHandlingEnabled() && getTarget() == null) {
130                            throw new NullValueInNestedPathException(getTargetClass(), propertyPath);
131                    }
132                    else {
133                            return super.getPropertyValue(propertyPath);
134                    }
135            }
136    
137            public void setPropertyValue(String propertyPath, Object value) {
138                    if (PropertyAccessorUtils.isNestedProperty(propertyPath)) {
139                            String baseProperty = getBasePropertyName(propertyPath);
140                            String childPropertyPath = getChildPropertyPath(propertyPath);
141                            ((PropertyAccessor) childPropertyAccessors.get(baseProperty)).setPropertyValue(childPropertyPath, value);
142                    }
143                    else if (isStrictNullHandlingEnabled() && getTarget() == null) {
144                            throw new NullValueInNestedPathException(getTargetClass(), propertyPath);
145                    }
146                    else {
147                            super.setPropertyValue(propertyPath, value);
148                    }
149            }
150    
151            protected String getBasePropertyName(String propertyPath) {
152                    int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
153                    return index == -1 ? propertyPath : propertyPath.substring(0, index);
154            }
155    
156            protected String getChildPropertyPath(String propertyPath) {
157                    int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
158                    if (index == -1) {
159                            return "";
160                    }
161                    return propertyPath.substring(index + 1);
162            }
163    
164            protected PropertyAccessor getChildPropertyAccessor(String propertyName) {
165                    return (PropertyAccessor) childPropertyAccessors.get(propertyName);
166            }
167    
168            protected abstract AbstractNestedMemberPropertyAccessor createChildPropertyAccessor(String propertyName);
169    
170            protected void clearChildPropertyAccessorCache() {
171                    childPropertyAccessors.clear();
172            }
173    
174            private class ChildPropertyAccessorCache extends CachingMapDecorator {
175    
176                    protected Object create(Object propertyName) {
177                            return createChildPropertyAccessor((String) propertyName);
178                    }
179            }
180    }