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.lang.reflect.Field;
019 import java.util.List;
020
021 import org.apache.ojb.broker.metadata.MetadataException;
022 import org.apache.ojb.broker.util.ClassHelper;
023 import org.apache.ojb.broker.core.proxy.ProxyHelper;
024
025 /**
026 * This {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField} implementation
027 * is the high-speed version of the access strategies.
028 * <br/>
029 * It does not cooperate with an AccessController,
030 * but accesses the fields directly. This implementation persistent
031 * attributes don't need getters and setters
032 * and don't have to be declared public or protected. Only the the
033 * metadata field names have to match the class fields.
034 *
035 * @version $Id: PersistentFieldDirectImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
036 */
037 public class PersistentFieldDirectImpl extends PersistentFieldBase
038 {
039 private static final long serialVersionUID = -5458024240998909205L;
040
041 private transient boolean isInitialized;
042 private transient List fieldsList;
043 private transient Field field;
044 private transient boolean nonNested;
045
046 public PersistentFieldDirectImpl()
047 {
048 }
049
050 public PersistentFieldDirectImpl(Class type, String fieldname)
051 {
052 super(type, fieldname);
053 }
054
055 public Class getType()
056 {
057 return getField().getType();
058 }
059
060 /**
061 * Returns the underlying field object.
062 * If parameter <tt>setAccessible</tt> is true the
063 * field access checking was suppressed.
064 */
065 protected Field getField()
066 {
067 // make sure class was initialized
068 if (!isInitialized)
069 {
070 /*
071 first we build a graph of fields for nested fields support,
072 but for best performance on non-nested fields we also keep
073 the latest field separate and set a 'is nested' flag.
074 */
075 fieldsList = getFieldGraph(makeAccessible());
076 field = (Field) fieldsList.get(fieldsList.size() - 1);
077 nonNested = fieldsList.size() == 1;
078 isInitialized = true;
079 }
080 return field;
081 }
082
083 private List getFieldsList()
084 {
085 // make sure class was initialized
086 if (!isInitialized) getField();
087 return fieldsList;
088 }
089
090 protected boolean isNestedField()
091 {
092 return !nonNested;
093 }
094
095 /**
096 * do not override this method, have a look at {@link #setValueFor(java.lang.reflect.Field, Object, Object)}
097 */
098 public void set(Object target, Object value) throws MetadataException
099 {
100 // if target null, we have nothing to do
101 if(target == null) return;
102 Object current = target;
103 if (isNestedField())
104 {
105 List fields = getFieldsList();
106 int size = fields.size() - 1;
107 Field field;
108 for (int i = 0; i < size; i++)
109 {
110 field = (Field) fields.get(i);
111 Object attribute;
112 try
113 {
114 attribute = getValueFrom(field, current);
115 }
116 catch (Exception e)
117 {
118 throw new MetadataException("Can't read field '" + field.getName() + "' of type " + field.getType().getName(), e);
119 }
120 if (attribute != null || value != null)
121 {
122 // if the intermediary nested object is null, we have to create
123 // a new instance to set the value
124 if (attribute == null)
125 {
126 try
127 {
128 attribute = ClassHelper.newInstance(field.getType());
129 }
130 catch (Exception e)
131 {
132 throw new MetadataException("Can't create nested object of type '"
133 + field.getType() + "' for field '"
134 + field.getName() + "'", e);
135 }
136 }
137 try
138 {
139 //field.set(current, attribute);
140 setValueFor(field, current, attribute);
141 }
142 //catch (IllegalAccessException e)
143 catch (Exception e)
144 {
145 throw new MetadataException("Can't set nested object of type '"
146 + field.getType() + "' for field '"
147 + field.getName() + "'", e);
148 }
149 }
150 else
151 {
152 return;
153 }
154 current = attribute;
155 }
156 }
157 setValueFor(getField(), current, value);
158 }
159
160 /**
161 * do not override this method, have a look at {@link #getValueFrom(java.lang.reflect.Field, Object)}
162 */
163 public Object get(Object target) throws MetadataException
164 {
165 Object result = target;
166 if (isNestedField())
167 {
168 List fields = getFieldsList();
169 for (int i = 0; i < fields.size(); i++)
170 {
171 if (result == null) break;
172 result = getValueFrom((Field) fields.get(i), result);
173 }
174 }
175 else
176 {
177 result = result != null ? getValueFrom(getField(), result) : null;
178 }
179 return result;
180 }
181
182
183
184 protected Object getValueFrom(Field field, Object target)
185 {
186 try
187 {
188 return field.get(ProxyHelper.getRealObject(target));
189 // TODO: don't make costly proxy test on field level use
190 // return field.get(target);
191 }
192 catch (IllegalAccessException e)
193 {
194 throw new MetadataException(
195 "IllegalAccess error reading field: " +
196 (field != null ? field.getName() : null) + " from object: "
197 + (target != null ? target.getClass().getName() : null), e);
198 }
199 catch (IllegalArgumentException e)
200 {
201 throw new MetadataException(
202 "IllegalArgument error reading field: " +
203 buildErrorGetMsg(target, field), e);
204 }
205 }
206
207 protected void setValueFor(Field field, Object target, final Object value)
208 {
209 try
210 {
211 /**
212 * MBAIRD
213 * we need to be able to set values to null. We can only set something to null if
214 * the type is not a primitive (assignable from Object).
215 */
216 // thanks to Tomasz Wysocki for this trick
217 if ((value != null) || !field.getType().isPrimitive())
218 {
219 field.set(ProxyHelper.getRealObject(target), value);
220 // TODO: don't make costly proxy test on field level use
221 // field.set(target, value);
222 }
223 }
224 catch (NullPointerException ignored)
225 {
226 getLog().info("Target object '" + (target != null ? target.getClass().getName() : null)
227 + "' for field '" + (field != null ? field.getName() : null)
228 + "' of type '" + (field != null ? field.getType().getName() : null)
229 + "' seems to be null. Can't write into null.", ignored);
230 }
231 catch (Exception e)
232 {
233 getLog().error("while set field: " + buildErrorSetMsg(target, value, field));
234 throw new MetadataException("IllegalAccess error setting field:" +
235 (field != null ? field.getName() : null) + " in object:" + target.getClass().getName(), e);
236 }
237 }
238
239 /**
240 * This implementation returns always 'true'.
241 */
242 protected boolean makeAccessible()
243 {
244 return true;
245 }
246
247 /**
248 * Always returns 'false'.
249 * @see org.apache.ojb.broker.metadata.fieldaccess.PersistentField#usesAccessorsAndMutators
250 */
251 public boolean usesAccessorsAndMutators()
252 {
253 return false;
254 }
255 }