001 package org.apache.ojb.broker.metadata.fieldaccess;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://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
018 import java.beans.BeanInfo;
019 import java.beans.IntrospectionException;
020 import java.beans.Introspector;
021 import java.beans.PropertyDescriptor;
022 import java.lang.reflect.Method;
023 import java.util.ArrayList;
024 import java.util.List;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.apache.ojb.broker.core.proxy.ProxyHelper;
028 import org.apache.ojb.broker.metadata.MetadataException;
029 import org.apache.ojb.broker.util.ClassHelper;
030 import org.apache.ojb.broker.util.logging.Logger;
031
032 /**
033 * A {@link PersistentField} implementation using
034 * JavaBeans compliant calls only to access persistent attributes.
035 * No Reflection is needed. But for each attribute xxx there must be
036 * public getXxx() and setXxx() methods. In metadata the field name must be
037 * the bean compliant 'xxx'.
038 *
039 * @version $Id: PersistentFieldIntrospectorImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
040 */
041 public class PersistentFieldIntrospectorImpl extends PersistentFieldBase
042 {
043 private static final long serialVersionUID = 8805309492150404444L;
044 private Class type;
045 private transient List propertyGraph;
046
047 public PersistentFieldIntrospectorImpl()
048 {
049 super();
050 }
051
052 public PersistentFieldIntrospectorImpl(Class aClass, String aPropertyName)
053 {
054 super(aClass, aPropertyName);
055 }
056
057 public Class getType()
058 {
059 if (type == null)
060 {
061 type = getPropertyDescriptor().getPropertyType();
062 }
063 return type;
064 }
065
066 public void set(Object target, Object value) throws MetadataException
067 {
068 if(target == null) return;
069 List propertyDescriptors = getPropertyGraph();
070 int size = propertyDescriptors.size() - 1;
071 PropertyDescriptor pd;
072 for (int i = 0; i < size; i++)
073 {
074 Object attribute;
075 pd = (PropertyDescriptor) propertyDescriptors.get(i);
076 attribute = getValueFrom(pd, target);
077 if (attribute != null || value != null)
078 {
079 if (attribute == null)
080 {
081 try
082 {
083 attribute = ClassHelper.newInstance(pd.getPropertyType());
084 }
085 catch (Exception e)
086 {
087 throw new MetadataException("Can't instantiate nested object of type '"
088 + pd.getPropertyType() + "' for field '"
089 + pd.getName() + "'", e);
090 }
091 }
092 setValueFor(pd, target, attribute);
093 }
094 else
095 {
096 return;
097 }
098 target = attribute;
099 }
100 pd = (PropertyDescriptor) propertyDescriptors.get(size);
101 setValueFor(pd, target, value);
102 }
103
104 public Object get(Object target) throws MetadataException
105 {
106 List propertyDescriptors = getPropertyGraph();
107 for (int i = 0; i < propertyDescriptors.size(); i++)
108 {
109 PropertyDescriptor pd = (PropertyDescriptor) propertyDescriptors.get(i);
110 target = getValueFrom(pd, target);
111 if (target == null) break;
112 }
113 return target;
114 }
115
116 private Object getValueFrom(PropertyDescriptor pd, Object target)
117 {
118 if (target == null) return null;
119 Method m = pd.getReadMethod();
120 if (m != null)
121 {
122 try
123 {
124 return m.invoke(ProxyHelper.getRealObject(target), null);
125 }
126 catch (Throwable e)
127 {
128 logProblem(pd, target, null, "Can't read value from given object");
129 throw new MetadataException("Error invoking method:" + m.getName() + " in object " + target.getClass().getName(), e);
130 }
131 }
132 else
133 {
134 throw new MetadataException("Can't get ReadMethod for property:" + pd.getName() + " in object " + target.getClass().getName());
135 }
136 }
137
138 private void setValueFor(PropertyDescriptor pd, Object target, Object value)
139 {
140 Method m = pd.getWriteMethod();
141 Object[] args = {value};
142 if (m != null)
143 {
144 try
145 {
146 /**
147 * MBAIRD: it is safe to call getParameterTypes()[0] because this is
148 * the "set" method and it needs to take one parameter only.
149 * we need to be able to set values to null. We can only set something to null if
150 * the type is not a primitive (assignable from Object).
151 */
152 if ((value != null) || !m.getParameterTypes()[0].isPrimitive())
153 {
154 m.invoke(ProxyHelper.getRealObject(target), args);
155 }
156 }
157 catch (Throwable e)
158 {
159 logProblem(pd, target, value, "Can't set value on given object.");
160 throw new MetadataException("Error invoking method:" + m.getName() + " in object:" + target.getClass().getName(), e);
161 }
162 }
163 else
164 {
165 throw new MetadataException("Can't get WriteMethod for property:" + pd.getName() + " in object:" + target.getClass().getName());
166 }
167 }
168
169 private List getPropertyGraph()
170 {
171 if (propertyGraph == null)
172 {
173 propertyGraph = buildPropertyGraph();
174 }
175 return propertyGraph;
176 }
177
178 private List buildPropertyGraph()
179 {
180 List result = new ArrayList();
181 String[] fields = StringUtils.split(getName(), PATH_TOKEN);
182 PropertyDescriptor pd = null;
183 for (int i = 0; i < fields.length; i++)
184 {
185 String fieldName = fields[i];
186 if (pd == null)
187 {
188 pd = findPropertyDescriptor(getDeclaringClass(), fieldName);
189 }
190 else
191 {
192 pd = findPropertyDescriptor(pd.getPropertyType(), fieldName);
193 }
194 result.add(pd);
195 }
196 return result;
197 }
198
199 /**
200 * Get the PropertyDescriptor for aClass and aPropertyName
201 */
202 protected static PropertyDescriptor findPropertyDescriptor(Class aClass, String aPropertyName)
203 {
204 BeanInfo info;
205 PropertyDescriptor[] pd;
206 PropertyDescriptor descriptor = null;
207
208 try
209 {
210 info = Introspector.getBeanInfo(aClass);
211 pd = info.getPropertyDescriptors();
212 for (int i = 0; i < pd.length; i++)
213 {
214 if (pd[i].getName().equals(aPropertyName))
215 {
216 descriptor = pd[i];
217 break;
218 }
219 }
220 if (descriptor == null)
221 {
222 /*
223 * Daren Drummond: Throw here so we are consistent
224 * with PersistentFieldDefaultImpl.
225 */
226 throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName());
227 }
228 return descriptor;
229 }
230 catch (IntrospectionException ex)
231 {
232 /*
233 * Daren Drummond: Throw here so we are consistent
234 * with PersistentFieldDefaultImpl.
235 */
236 throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName(), ex);
237 }
238 }
239
240 /**
241 * Returns the PropertyDescriptor.
242 *
243 * @return java.beans.PropertyDescriptor
244 */
245 protected PropertyDescriptor getPropertyDescriptor()
246 {
247 return (PropertyDescriptor) getPropertyGraph().get(getPropertyGraph().size() - 1);
248 }
249
250 /**
251 * This implementation returns always 'false'.
252 */
253 public boolean makeAccessible()
254 {
255 return false;
256 }
257
258 /**
259 * Always returns 'false'.
260 *
261 * @see PersistentField#usesAccessorsAndMutators
262 */
263 public boolean usesAccessorsAndMutators()
264 {
265 return true;
266 }
267
268 /**
269 * Let's give the user some hints as to what could be wrong.
270 */
271 protected void logProblem(PropertyDescriptor pd, Object anObject, Object aValue, String msg)
272 {
273 Logger logger = getLog();
274 logger.error("Error in [PersistentFieldPropertyImpl], " + msg);
275 logger.error("Declaring class [" + getDeclaringClass().getName() + "]");
276 logger.error("Property Name [" + getName() + "]");
277 logger.error("Property Type [" + pd.getPropertyType().getName() + "]");
278
279 if (anObject != null)
280 {
281 logger.error("anObject was class [" + anObject.getClass().getName() + "]");
282 }
283 else
284 {
285 logger.error("anObject was null");
286 }
287 if (aValue != null)
288 {
289 logger.error("aValue was class [" + aValue.getClass().getName() + "]");
290 }
291 else
292 {
293 logger.error("aValue was null");
294 }
295 }
296 }