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 }