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 java.lang.reflect.Array;
019    import java.lang.reflect.Field;
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Member;
022    import java.lang.reflect.Method;
023    import java.util.Collection;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    
028    import org.springframework.beans.BeansException;
029    import org.springframework.beans.InvalidPropertyException;
030    import org.springframework.beans.NotReadablePropertyException;
031    import org.springframework.beans.NotWritablePropertyException;
032    import org.springframework.beans.NullValueInNestedPathException;
033    import org.springframework.beans.TypeMismatchException;
034    import org.springframework.core.JdkVersion;
035    import org.springframework.core.MethodParameter;
036    import org.springframework.richclient.util.ReflectionUtils;
037    
038    /**
039     * This class implements actual access to properties for
040     * <tt>AbstractNestedMemberPropertyAccessor</tt>.
041     *
042     * @author Arne Limburg
043     */
044    public class DefaultMemberPropertyAccessor extends AbstractNestedMemberPropertyAccessor {
045    
046            private Object target;
047    
048            private boolean fixedTargetClass;
049    
050            public DefaultMemberPropertyAccessor(Class targetClass) {
051                    this(targetClass, null, false, false);
052            }
053    
054            public DefaultMemberPropertyAccessor(Object target) {
055                    this(target, false, true);
056            }
057    
058            public DefaultMemberPropertyAccessor(Object target, boolean fieldAccessEnabled, boolean strictNullHandlingEnabled) {
059                    super(target.getClass(), fieldAccessEnabled, strictNullHandlingEnabled);
060                    setTarget(target);
061            }
062    
063            public DefaultMemberPropertyAccessor(Class targetClass, Object target, boolean fieldAccessEnabled, boolean strictNullHandlingEnabled) {
064                    super(targetClass, fieldAccessEnabled, strictNullHandlingEnabled);
065                    fixedTargetClass = true;
066                    setTarget(target);
067            }
068    
069            protected DefaultMemberPropertyAccessor(AbstractNestedMemberPropertyAccessor parent, String baseProperty) {
070                    super(parent, baseProperty);
071            }
072    
073            public Object getTarget() {
074                    if (target != null) {
075                            return target;
076                    }
077                    else {
078                            return super.getTarget();
079                    }
080            }
081    
082            public void setTarget(Object target) {
083                    if (getParentPropertyAccessor() != null) {
084                            throw new IllegalStateException("explicite setting of target is not allowed for child property accessors");
085                    }
086                    this.target = target;
087                    if (!fixedTargetClass && target != null && target.getClass() != getTargetClass()) {
088                            setTargetClass(target.getClass());
089                            clearChildPropertyAccessorCache();
090                    }
091            }
092    
093            public Object getIndexedPropertyValue(String propertyName) throws BeansException {
094                    if (getPropertyType(propertyName) == null) {
095                            throw new NotReadablePropertyException(getTargetClass(), propertyName,
096                                            "property type could not be determined");
097                    }
098                    String rootPropertyName = getRootPropertyName(propertyName);
099                    Member readAccessor = getReadPropertyAccessor(rootPropertyName);
100                    if (readAccessor == null) {
101                            throw new NotReadablePropertyException(getTargetClass(), propertyName,
102                                            "Neither non-static field nor get-method exists for indexed property");
103                    }
104                    Object rootProperty = getPropertyValue(rootPropertyName);
105                    if (rootProperty == null) {
106                            if (isStrictNullHandlingEnabled()) {
107                                    throw new NullValueInNestedPathException(getTargetClass(), propertyName);
108                            }
109                            else if (isWritableProperty(rootPropertyName)) {
110                                    return null;
111                            }
112                            else {
113                                    throw new NotReadablePropertyException(getTargetClass(), propertyName);
114                            }
115                    }
116                    Object[] indices;
117                    try {
118                            indices = getIndices(propertyName);
119                    }
120                    catch (Exception e) {
121                            // could not convert indices
122                            throw createNotReadablePropertyException(propertyName, e);
123                    }
124                    return getPropertyValue(rootProperty, indices);
125            }
126    
127            public Object getSimplePropertyValue(String propertyName) throws BeansException {
128                    Member readAccessor = getReadPropertyAccessor(propertyName);
129                    if (readAccessor == null) {
130                            throw new NotReadablePropertyException(getTargetClass(), propertyName,
131                                            "Neither non-static field nor get-method does exist");
132                    }
133                    Object target = getTarget();
134                    if (target == null) {
135                            return null;
136                    }
137                    try {
138                            ReflectionUtils.makeAccessible(readAccessor);
139                            if (readAccessor instanceof Field) {
140                                    return ((Field) readAccessor).get(target);
141                            }
142                            else {// readAccessor instanceof Method
143                                    return ((Method) readAccessor).invoke(target, null);
144                            }
145                    }
146                    catch (IllegalAccessException e) {
147                            throw new InvalidPropertyException(getTargetClass(), propertyName, "Property is not accessible", e);
148                    }
149                    catch (InvocationTargetException e) {
150                            ReflectionUtils.handleInvocationTargetException(e);
151                            throw new IllegalStateException(
152                                            "An unexpected state occured during getSimplePropertyValue(String). This may be a bug.");
153                    }
154            }
155    
156            private Object getPropertyValue(Object assemblage, Object[] indices) {
157                    return getPropertyValue(assemblage, indices, 0);
158            }
159    
160            private Object getPropertyValue(Object assemblage, Object[] indices, int parameterIndex) {
161                    if (assemblage == null) {
162                            if (isStrictNullHandlingEnabled()) {
163                                    throw new NullValueInNestedPathException(getTargetClass(), "");
164                            }
165                            else {
166                                    return null;
167                            }
168                    }
169                    Object value = null;
170                    if (assemblage.getClass().isArray()) {
171                            value = getArrayValue(assemblage, (Integer) indices[parameterIndex]);
172                    }
173                    else if (assemblage instanceof List) {
174                            value = getListValue((List) assemblage, (Integer) indices[parameterIndex]);
175                    }
176                    else if (assemblage instanceof Map) {
177                            value = getMapValue((Map) assemblage, indices[parameterIndex]);
178                    }
179                    else if (assemblage instanceof Collection) {
180                            value = getCollectionValue((Collection) assemblage, (Integer) indices[parameterIndex]);
181                    }
182                    else {
183                            throw new IllegalStateException(
184                                            "getPropertyValue(Object, Object[], int) called with neither array nor collection nor map");
185                    }
186                    if (parameterIndex == indices.length - 1) {
187                            return value;
188                    }
189                    if (value == null) {
190                            if (isStrictNullHandlingEnabled()) {
191                                    throw new InvalidPropertyException(getTargetClass(), "", "");
192                            }
193                            else {
194                                    return null;
195                            }
196                    }
197                    return getPropertyValue(value, indices, parameterIndex + 1);
198            }
199    
200            private Object getArrayValue(Object array, Integer index) {
201                    if (Array.getLength(array) > index.intValue()) {
202                            return Array.get(array, index.intValue());
203                    }
204                    else if (isStrictNullHandlingEnabled()) {
205                            throw new InvalidPropertyException(getTargetClass(), "", "");
206                    }
207                    else {
208                            return null;
209                    }
210            }
211    
212            private Object getListValue(List list, Integer index) {
213                    if (list.size() > index.intValue()) {
214                            return list.get(index.intValue());
215                    }
216                    else if (isStrictNullHandlingEnabled()) {
217                            throw new InvalidPropertyException(getTargetClass(), "", "");
218                    }
219                    else {
220                            return null;
221                    }
222            }
223    
224            private Object getMapValue(Map map, Object key) {
225                    if (map.containsKey(key)) {
226                            return map.get(key);
227                    }
228                    else {
229                            if (!JdkVersion.isAtLeastJava15()) {
230                                    // we don't know the type of the keys, so we fall back to
231                                    // comparing toString()
232                                    for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
233                                            Map.Entry entry = (Map.Entry) i.next();
234                                            if (entry.getKey() == key
235                                                            || (entry.getKey() != null && key != null && entry.getKey().toString().equals(
236                                                                            key.toString()))) {
237                                                    return entry.getValue();
238                                            }
239                                    }
240                            }
241                            return null;
242                    }
243            }
244    
245            private Object getCollectionValue(Collection collection, Integer index) {
246                    if (collection.size() > index.intValue()) {
247                            Iterator iterator = collection.iterator();
248                            for (int i = 0; i < index.intValue(); i++) {
249                                    iterator.next();
250                            }
251                            return iterator.next();
252                    }
253                    else if (isStrictNullHandlingEnabled()) {
254                            throw new InvalidPropertyException(getTargetClass(), "", "");
255                    }
256                    else {
257                            return null;
258                    }
259            }
260    
261            public void setIndexedPropertyValue(String propertyName, Object value) throws BeansException {
262                    String parentPropertyName = getParentPropertyName(propertyName);
263                    Object parentValue;
264                    try {
265                            parentValue = getPropertyValue(parentPropertyName);
266                    }
267                    catch (NotReadablePropertyException e) {
268                            throw new NotWritablePropertyException(getTargetClass(), propertyName, "parent property is not readable", e);
269                    }
270                    if (parentValue == null) {
271                            if (isWritableProperty(parentPropertyName)) {
272                                    throw new NullValueInNestedPathException(getTargetClass(), propertyName);
273                            }
274                            else {
275                                    throw new NotWritablePropertyException(getTargetClass(), propertyName);
276                            }
277                    }
278                    Object[] indices;
279                    try {
280                            indices = getIndices(propertyName);
281                    }
282                    catch (Exception e) {
283                            throw new NotWritablePropertyException(getTargetClass(), propertyName, "wrong index type", e);
284                    }
285                    Object index = indices[indices.length - 1];
286                    Object newParentValue = setAssemblageValue(getPropertyType(parentPropertyName), parentValue, index, value);
287                    if (newParentValue != parentValue) {
288                            setPropertyValue(parentPropertyName, newParentValue);
289                    }
290            }
291    
292            public void setSimplePropertyValue(String propertyName, Object value) throws BeansException {
293                    Member writeAccessor = getWritePropertyAccessor(propertyName);
294                    if (writeAccessor == null) {
295                            throw new NotWritablePropertyException(getTargetClass(), propertyName,
296                                            "Neither non-static, non-final field nor set-method does exist");
297                    }
298                    Object target = getTarget();
299                    if (target == null) {
300                            throw new NullValueInNestedPathException(getTargetClass(), propertyName);
301                    }
302                    try {
303                            ReflectionUtils.makeAccessible(writeAccessor);
304                            if (writeAccessor instanceof Field) {
305                                    ((Field) writeAccessor).set(target, value);
306                            }
307                            else {// writeAccessor instanceof Method
308                                    ((Method) writeAccessor).invoke(target, new Object[] { value });
309                            }
310                    }
311                    catch (IllegalAccessException e) {
312                            throw new InvalidPropertyException(getTargetClass(), propertyName, "Property is not accessible", e);
313                    }
314                    catch (InvocationTargetException e) {
315                            ReflectionUtils.handleInvocationTargetException(e);
316                            throw new IllegalStateException(
317                                            "An unexpected state occured during setPropertyValue(String, Object). This may be a bug.");
318                    }
319            }
320    
321            protected AbstractNestedMemberPropertyAccessor createChildPropertyAccessor(String propertyName) {
322                    return new DefaultMemberPropertyAccessor(this, propertyName);
323            }
324    
325            public Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
326                            throws TypeMismatchException {
327                    // TODO Auto-generated method stub
328                    return null;
329            }
330    }