001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.beanutils; 019 020import java.io.Serializable; 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.util.HashMap; 024 025/** 026 * <p>Minimal implementation of the <code>DynaClass</code> interface. Can be 027 * used as a convenience base class for more sophisticated implementations.</p> * 028 * <p><strong>IMPLEMENTATION NOTE</strong> - The <code>DynaBean</code> 029 * implementation class supplied to our constructor MUST have a one-argument 030 * constructor of its own that accepts a <code>DynaClass</code>. This is 031 * used to associate the DynaBean instance with this DynaClass.</p> 032 * 033 */ 034public class BasicDynaClass implements DynaClass, Serializable { 035 036 private static final long serialVersionUID = 1L; 037 038 /** 039 * The method signature of the constructor we will use to create 040 * new DynaBean instances. 041 */ 042 protected static Class<?>[] constructorTypes = { DynaClass.class }; 043 044 /** 045 * The constructor of the <code>dynaBeanClass</code> that we will use 046 * for creating new instances. 047 */ 048 protected transient Constructor<?> constructor; 049 050 /** 051 * The argument values to be passed to the constructore we will use 052 * to create new DynaBean instances. 053 */ 054 protected Object[] constructorValues = { this }; 055 056 /** 057 * The <code>DynaBean</code> implementation class we will use for 058 * creating new instances. 059 */ 060 protected Class<?> dynaBeanClass = BasicDynaBean.class; 061 062 /** 063 * The "name" of this DynaBean class. 064 */ 065 protected String name = this.getClass().getName(); 066 067 /** 068 * The set of dynamic properties that are part of this DynaClass. 069 */ 070 protected DynaProperty[] properties = {}; 071 072 /** 073 * The set of dynamic properties that are part of this DynaClass, 074 * keyed by the property name. Individual descriptor instances will 075 * be the same instances as those in the <code>properties</code> list. 076 */ 077 protected HashMap<String, DynaProperty> propertiesMap = new HashMap<>(); 078 079 /** 080 * Construct a new BasicDynaClass with default parameters. 081 */ 082 public BasicDynaClass() { 083 this(null, null, null); 084 } 085 086 /** 087 * Construct a new BasicDynaClass with the specified parameters. 088 * 089 * @param name Name of this DynaBean class 090 * @param dynaBeanClass The implementation class for new instances 091 */ 092 public BasicDynaClass(final String name, final Class<?> dynaBeanClass) { 093 this(name, dynaBeanClass, null); 094 } 095 096 /** 097 * Construct a new BasicDynaClass with the specified parameters. 098 * 099 * @param name Name of this DynaBean class 100 * @param dynaBeanClass The implementation class for new intances 101 * @param properties Property descriptors for the supported properties 102 */ 103 public BasicDynaClass(final String name, Class<?> dynaBeanClass, 104 final DynaProperty[] properties) { 105 if (name != null) { 106 this.name = name; 107 } 108 if (dynaBeanClass == null) { 109 dynaBeanClass = BasicDynaBean.class; 110 } 111 setDynaBeanClass(dynaBeanClass); 112 if (properties != null) { 113 setProperties(properties); 114 } 115 } 116 117 /** 118 * Return the Class object we will use to create new instances in the 119 * <code>newInstance()</code> method. This Class <strong>MUST</strong> 120 * implement the <code>DynaBean</code> interface. 121 * 122 * @return The class of the {@link DynaBean} 123 */ 124 public Class<?> getDynaBeanClass() { 125 return this.dynaBeanClass; 126 } 127 128 /** 129 * <p>Return an array of <code>ProperyDescriptors</code> for the properties 130 * currently defined in this DynaClass. If no properties are defined, a 131 * zero-length array will be returned.</p> 132 * 133 * <p><strong>FIXME</strong> - Should we really be implementing 134 * <code>getBeanInfo()</code> instead, which returns property descriptors 135 * and a bunch of other stuff?</p> 136 * 137 * @return the set of properties for this DynaClass 138 */ 139 @Override 140 public DynaProperty[] getDynaProperties() { 141 return properties; 142 } 143 144 /** 145 * Return a property descriptor for the specified property, if it exists; 146 * otherwise, return <code>null</code>. 147 * 148 * @param name Name of the dynamic property for which a descriptor 149 * is requested 150 * @return The descriptor for the specified property 151 * @throws IllegalArgumentException if no property name is specified 152 */ 153 @Override 154 public DynaProperty getDynaProperty(final String name) { 155 if (name == null) { 156 throw new IllegalArgumentException 157 ("No property name specified"); 158 } 159 return propertiesMap.get(name); 160 } 161 162 /** 163 * Return the name of this DynaClass (analogous to the 164 * <code>getName()</code> method of <code>java.lang.Class</code>), which 165 * allows the same <code>DynaClass</code> implementation class to support 166 * different dynamic classes, with different sets of properties. 167 * 168 * @return the name of the DynaClass 169 */ 170 @Override 171 public String getName() { 172 return this.name; 173 } 174 175 /** 176 * Instantiate and return a new DynaBean instance, associated 177 * with this DynaClass. 178 * 179 * @return A new <code>DynaBean</code> instance 180 * @throws IllegalAccessException if the Class or the appropriate 181 * constructor is not accessible 182 * @throws InstantiationException if this Class represents an abstract 183 * class, an array class, a primitive type, or void; or if instantiation 184 * fails for some other reason 185 */ 186 @Override 187 public DynaBean newInstance() 188 throws IllegalAccessException, InstantiationException { 189 try { 190 // Refind the constructor after a deserialization (if needed) 191 if (constructor == null) { 192 setDynaBeanClass(this.dynaBeanClass); 193 } 194 // Invoke the constructor to create a new bean instance 195 return (DynaBean) constructor.newInstance(constructorValues); 196 } catch (final InvocationTargetException e) { 197 throw new InstantiationException 198 (e.getTargetException().getMessage()); 199 } 200 } 201 202 /** 203 * Set the Class object we will use to create new instances in the 204 * <code>newInstance()</code> method. This Class <strong>MUST</strong> 205 * implement the <code>DynaBean</code> interface. 206 * 207 * @param dynaBeanClass The new Class object 208 * @throws IllegalArgumentException if the specified Class does not 209 * implement the <code>DynaBean</code> interface 210 */ 211 protected void setDynaBeanClass(final Class<?> dynaBeanClass) { 212 // Validate the argument type specified 213 if (dynaBeanClass.isInterface()) { 214 throw new IllegalArgumentException 215 ("Class " + dynaBeanClass.getName() + 216 " is an interface, not a class"); 217 } 218 if (!DynaBean.class.isAssignableFrom(dynaBeanClass)) { 219 throw new IllegalArgumentException 220 ("Class " + dynaBeanClass.getName() + 221 " does not implement DynaBean"); 222 } 223 // Identify the Constructor we will use in newInstance() 224 try { 225 this.constructor = dynaBeanClass.getConstructor(constructorTypes); 226 } catch (final NoSuchMethodException e) { 227 throw new IllegalArgumentException 228 ("Class " + dynaBeanClass.getName() + 229 " does not have an appropriate constructor"); 230 } 231 this.dynaBeanClass = dynaBeanClass; 232 } 233 234 /** 235 * Set the list of dynamic properties supported by this DynaClass. 236 * 237 * @param properties List of dynamic properties to be supported 238 */ 239 protected void setProperties(final DynaProperty[] properties) { 240 this.properties = properties; 241 propertiesMap.clear(); 242 for (final DynaProperty propertie : properties) { 243 propertiesMap.put(propertie.getName(), propertie); 244 } 245 } 246 247}