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