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    }