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 }