1/* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.beans.decoder; 26 27import com.sun.beans.finder.MethodFinder; 28 29import java.beans.IndexedPropertyDescriptor; 30import java.beans.IntrospectionException; 31import java.beans.Introspector; 32import java.beans.PropertyDescriptor; 33 34import java.lang.reflect.Array; 35import java.lang.reflect.InvocationTargetException; 36import java.lang.reflect.Method; 37 38import sun.reflect.misc.MethodUtil; 39 40/** 41 * This class is intended to handle <property> element. 42 * This element simplifies access to the properties. 43 * If the {@code index} attribute is specified 44 * this element uses additional {@code int} parameter. 45 * If the {@code name} attribute is not specified 46 * this element uses method "get" as getter 47 * and method "set" as setter. 48 * This element defines getter if it contains no argument. 49 * It returns the value of the property in this case. 50 * For example:<pre> 51 * <property name="object" index="10"/></pre> 52 * is shortcut to<pre> 53 * <method name="getObject"> 54 * <int>10</int> 55 * </method></pre> 56 * which is equivalent to {@code getObject(10)} in Java code. 57 * This element defines setter if it contains one argument. 58 * It does not return the value of the property in this case. 59 * For example:<pre> 60 * <property><int>0</int></property></pre> 61 * is shortcut to<pre> 62 * <method name="set"> 63 * <int>0</int> 64 * </method></pre> 65 * which is equivalent to {@code set(0)} in Java code. 66 * <p>The following attributes are supported: 67 * <dl> 68 * <dt>name 69 * <dd>the property name 70 * <dt>index 71 * <dd>the property index 72 * <dt>id 73 * <dd>the identifier of the variable that is intended to store the result 74 * </dl> 75 * 76 * @since 1.7 77 * 78 * @author Sergey A. Malenkov 79 */ 80final class PropertyElementHandler extends AccessorElementHandler { 81 static final String GETTER = "get"; // NON-NLS: the getter prefix 82 static final String SETTER = "set"; // NON-NLS: the setter prefix 83 84 private Integer index; 85 86 /** 87 * Parses attributes of the element. 88 * The following attributes are supported: 89 * <dl> 90 * <dt>name 91 * <dd>the property name 92 * <dt>index 93 * <dd>the property index 94 * <dt>id 95 * <dd>the identifier of the variable that is intended to store the result 96 * </dl> 97 * 98 * @param name the attribute name 99 * @param value the attribute value 100 */ 101 @Override 102 public void addAttribute(String name, String value) { 103 if (name.equals("index")) { // NON-NLS: the attribute name 104 this.index = Integer.valueOf(value); 105 } else { 106 super.addAttribute(name, value); 107 } 108 } 109 110 /** 111 * Tests whether the value of this element can be used 112 * as an argument of the element that contained in this one. 113 * 114 * @return {@code true} if the value of this element should be used 115 * as an argument of the element that contained in this one, 116 * {@code false} otherwise 117 */ 118 @Override 119 protected boolean isArgument() { 120 return false; // non-static accessor cannot be used an argument 121 } 122 123 /** 124 * Returns the value of the property with specified {@code name}. 125 * 126 * @param name the name of the property 127 * @return the value of the specified property 128 */ 129 @Override 130 protected Object getValue(String name) { 131 try { 132 return getPropertyValue(getContextBean(), name, this.index); 133 } 134 catch (Exception exception) { 135 getOwner().handleException(exception); 136 } 137 return null; 138 } 139 140 /** 141 * Sets the new value for the property with specified {@code name}. 142 * 143 * @param name the name of the property 144 * @param value the new value for the specified property 145 */ 146 @Override 147 protected void setValue(String name, Object value) { 148 try { 149 setPropertyValue(getContextBean(), name, this.index, value); 150 } 151 catch (Exception exception) { 152 getOwner().handleException(exception); 153 } 154 } 155 156 /** 157 * Performs the search of the getter for the property 158 * with specified {@code name} in specified class 159 * and returns value of the property. 160 * 161 * @param bean the context bean that contains property 162 * @param name the name of the property 163 * @param index the index of the indexed property 164 * @return the value of the property 165 * @throws IllegalAccessException if the property is not accesible 166 * @throws IntrospectionException if the bean introspection is failed 167 * @throws InvocationTargetException if the getter cannot be invoked 168 * @throws NoSuchMethodException if the getter is not found 169 */ 170 private static Object getPropertyValue(Object bean, String name, Integer index) throws IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException { 171 Class<?> type = bean.getClass(); 172 if (index == null) { 173 return MethodUtil.invoke(findGetter(type, name), bean, new Object[] {}); 174 } else if (type.isArray() && (name == null)) { 175 return Array.get(bean, index); 176 } else { 177 return MethodUtil.invoke(findGetter(type, name, int.class), bean, new Object[] {index}); 178 } 179 } 180 181 /** 182 * Performs the search of the setter for the property 183 * with specified {@code name} in specified class 184 * and updates value of the property. 185 * 186 * @param bean the context bean that contains property 187 * @param name the name of the property 188 * @param index the index of the indexed property 189 * @param value the new value for the property 190 * @throws IllegalAccessException if the property is not accesible 191 * @throws IntrospectionException if the bean introspection is failed 192 * @throws InvocationTargetException if the setter cannot be invoked 193 * @throws NoSuchMethodException if the setter is not found 194 */ 195 private static void setPropertyValue(Object bean, String name, Integer index, Object value) throws IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException { 196 Class<?> type = bean.getClass(); 197 Class<?> param = (value != null) 198 ? value.getClass() 199 : null; 200 201 if (index == null) { 202 MethodUtil.invoke(findSetter(type, name, param), bean, new Object[] {value}); 203 } else if (type.isArray() && (name == null)) { 204 Array.set(bean, index, value); 205 } else { 206 MethodUtil.invoke(findSetter(type, name, int.class, param), bean, new Object[] {index, value}); 207 } 208 } 209 210 /** 211 * Performs the search of the getter for the property 212 * with specified {@code name} in specified class. 213 * 214 * @param type the class that contains method 215 * @param name the name of the property 216 * @param args the method arguments 217 * @return method object that represents found getter 218 * @throws IntrospectionException if the bean introspection is failed 219 * @throws NoSuchMethodException if method is not found 220 */ 221 private static Method findGetter(Class<?> type, String name, Class<?>...args) throws IntrospectionException, NoSuchMethodException { 222 if (name == null) { 223 return MethodFinder.findInstanceMethod(type, GETTER, args); 224 } 225 PropertyDescriptor pd = getProperty(type, name); 226 if (args.length == 0) { 227 Method method = pd.getReadMethod(); 228 if (method != null) { 229 return method; 230 } 231 } else if (pd instanceof IndexedPropertyDescriptor) { 232 IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd; 233 Method method = ipd.getIndexedReadMethod(); 234 if (method != null) { 235 return method; 236 } 237 } 238 throw new IntrospectionException("Could not find getter for the " + name + " property"); 239 } 240 241 /** 242 * Performs the search of the setter for the property 243 * with specified {@code name} in specified class. 244 * 245 * @param type the class that contains method 246 * @param name the name of the property 247 * @param args the method arguments 248 * @return method object that represents found setter 249 * @throws IntrospectionException if the bean introspection is failed 250 * @throws NoSuchMethodException if method is not found 251 */ 252 private static Method findSetter(Class<?> type, String name, Class<?>...args) throws IntrospectionException, NoSuchMethodException { 253 if (name == null) { 254 return MethodFinder.findInstanceMethod(type, SETTER, args); 255 } 256 PropertyDescriptor pd = getProperty(type, name); 257 if (args.length == 1) { 258 Method method = pd.getWriteMethod(); 259 if (method != null) { 260 return method; 261 } 262 } else if (pd instanceof IndexedPropertyDescriptor) { 263 IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd; 264 Method method = ipd.getIndexedWriteMethod(); 265 if (method != null) { 266 return method; 267 } 268 } 269 throw new IntrospectionException("Could not find setter for the " + name + " property"); 270 } 271 272 /** 273 * Performs the search of the descriptor for the property 274 * with specified {@code name} in specified class. 275 * 276 * @param type the class to introspect 277 * @param name the property name 278 * @return descriptor for the named property 279 * @throws IntrospectionException if property descriptor is not found 280 */ 281 private static PropertyDescriptor getProperty(Class<?> type, String name) throws IntrospectionException { 282 for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) { 283 if (name.equals(pd.getName())) { 284 return pd; 285 } 286 } 287 throw new IntrospectionException("Could not find the " + name + " property descriptor"); 288 } 289} 290