View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * $Id$
44   */
45  
46  package org.exolab.castor.mapping.loader;
47  
48  import java.lang.reflect.Array;
49  import java.lang.reflect.Field;
50  import java.lang.reflect.Method;
51  import java.lang.reflect.Modifier;
52  import java.lang.reflect.InvocationTargetException;
53  import java.util.Enumeration;
54  import java.util.Iterator;
55  
56  import org.castor.core.util.Messages;
57  import org.exolab.castor.core.exceptions.CastorIllegalStateException;
58  import org.exolab.castor.mapping.AbstractFieldHandler;
59  import org.exolab.castor.mapping.ExtendedFieldHandler;
60  import org.exolab.castor.mapping.FieldDescriptor;
61  import org.exolab.castor.mapping.FieldHandler;
62  import org.exolab.castor.mapping.GeneralizedFieldHandler;
63  import org.exolab.castor.mapping.TypeConvertor;
64  import org.exolab.castor.mapping.CollectionHandler;
65  import org.exolab.castor.mapping.MappingException;
66  import org.exolab.castor.mapping.MappingRuntimeException;
67  import org.exolab.castor.util.IteratorEnumeration;
68  
69  /**
70   * A field handler that knows how to get/set the values of a field directly or
71   * through the get/set methods. Uses reflection.
72   * <p>
73   * Note: the field Java type is obtained from {@link TypeInfo#getFieldType()},
74   * but if the field is a collection, the actual field/accessor type is obtained
75   * from {@link TypeInfo#getCollectionHandler} and the object to create (with
76   * {@link #newInstance(Object)}) is the former field type.
77   * 
78   * @author <a href="arkin@intalio.com">Assaf Arkin</a>
79   * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr
80   *          2006) $
81   */
82  public final class FieldHandlerImpl<T> extends AbstractFieldHandler<T> {
83     /**
84      * The prefix for an "add" method
85      **/
86     private static final String ADD_PREFIX = "add";
87  
88     /**
89      * The prefix for an "enum" method
90      */
91     private static final String ENUM_PREFIX = "enum";
92  
93     /**
94      * The prefix for an "iter" method
95      */
96     private static final String ITER_PREFIX = "iter";
97  
98     /**
99      * The underlying field handler used by this handler.
100     */
101    private final FieldHandler<T> _handler;
102 
103    /**
104     * The Java field described and accessed through this descriptor.
105     */
106    private final Field _field;
107 
108    /**
109     * The sequence of methods used to obtain the nested field. May be null.
110     */
111    private Method[] _getSequence;
112 
113    /**
114     * The sequence of methods used to create the nested object. May be null.
115     */
116    private Method[] _setSequence;
117 
118    /**
119     * The method used to "incrementally" set the value of this field. This is
120     * only used if the field is a collection
121     */
122    private Method _addMethod;
123 
124    /**
125     * The method used to enumerate entries of a container.
126     */
127    private Method _enumMethod;
128 
129    /**
130     * The method used to iterate over a container.
131     */
132    private Method _iterMethod;
133 
134    /**
135     * The method used to obtain the value of this field. May be null.
136     */
137    private Method _getMethod;
138 
139    /**
140     * The method used to set the value of this field. May be null.
141     */
142    private Method _setMethod;
143 
144    /**
145     * The method used to check if the value of this field exists. May be null.
146     */
147    private Method _hasMethod;
148 
149    /**
150     * The method used to delete the value of this field. May be null.
151     */
152    private Method _deleteMethod;
153 
154    /**
155     * The method used to create a new instance of the field.
156     */
157    private Method _createMethod;
158 
159    /**
160     * The Java field name.
161     */
162    private final String _fieldName;
163 
164    /**
165     * The Java field type.
166     */
167    private final Class _fieldType;
168 
169    /**
170     * True if this field is an immutable type.
171     */
172    private final boolean _immutable;
173 
174    /**
175     * The default value for primitive fields. Will be set if the field is null.
176     */
177    private final Object _default;
178 
179    /**
180     * Convertor to apply when setting the value of the field. Converts from the
181     * value to the field type. Null if no convertor is required.
182     */
183    private TypeConvertor _convertTo = null;
184 
185    /**
186     * Convertor to apply when reading the value of the field. Converts from the
187     * field type to the return value. Null if no convertor is required.
188     */
189    private TypeConvertor _convertFrom = null;
190 
191    /**
192     * The collection handler for multi valued fields.
193     */
194    private final CollectionHandler<T> _colHandler;
195 
196    /**
197     * Construct a new field handler for the specified field. The field must be
198     * public, and may not be static or transient. The field name is determined
199     * from the Java field, the type from the type information.
200     * 
201     * @param handler
202     * @param typeInfo
203     *           Type information
204     */
205    public FieldHandlerImpl(FieldHandler<T> handler, TypeInfo typeInfo) {
206       _handler = handler;
207       _field = null;
208       _fieldName = handler.toString();
209       _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
210       _immutable = typeInfo.isImmutable();
211       _default = typeInfo.getDefaultValue();
212       _convertTo = typeInfo.getConvertorTo();
213       _convertFrom = typeInfo.getConvertorFrom();
214       _colHandler = typeInfo.getCollectionHandler();
215    }
216 
217    /**
218     * Construct a new field handler for the specified field. The field must be
219     * public, and may not be static or transient. The field name is determined
220     * from the Java field, the type from the type information.
221     * 
222     * @param field
223     *           The field being described
224     * @param typeInfo
225     *           Type information
226     * @throws MappingException
227     *            If the field is not public, is static or transient
228     */
229    public FieldHandlerImpl(Field field, TypeInfo typeInfo) throws MappingException {
230       if (field.getModifiers() != Modifier.PUBLIC
231             && field.getModifiers() != (Modifier.PUBLIC | Modifier.VOLATILE))
232          throw new MappingException("mapping.fieldNotAccessible", field.getName(), field.getDeclaringClass()
233                .getName());
234       _handler = null;
235       _field = field;
236       _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
237       _fieldName = field.getName() + "(" + field.getType().getName() + ")";
238       _immutable = typeInfo.isImmutable();
239       // If the field is of a primitive type or if it is required
240       // we use the default value
241       if (_field.getType().isPrimitive())
242          _default = typeInfo.getDefaultValue();
243       else
244          _default = null;
245       _convertTo = typeInfo.getConvertorTo();
246       _convertFrom = typeInfo.getConvertorFrom();
247       _colHandler = typeInfo.getCollectionHandler();
248    }
249 
250    /**
251     * Construct a new field handler for the specified field that is accessed
252     * through the accessor methods (get/set). The accessor methods must be
253     * public and not static. The field name is required for descriptive
254     * purposes. The field type must match the return value of the get method and
255     * the single parameter of the set method. Either get or set methods are
256     * optional.
257     * 
258     * @param fieldName
259     *           The field being described
260     * @param getMethod
261     *           The method used to retrieve the field value, must accept no
262     *           parameters and have a return type castable to the field type
263     * @param setMethod
264     *           The method used to set the field value, must accept a single
265     *           parameter that is castable to the field type
266     * @param typeInfo
267     *           Type information
268     * @throws MappingException
269     *            If the get or set method are not public, are static, or do not
270     *            specify the proper types
271     * 
272     */
273    public FieldHandlerImpl(String fieldName, Method[] getSequence, Method[] setSequence, Method getMethod,
274          Method setMethod, TypeInfo typeInfo) throws MappingException {
275       _handler = null;
276       _field = null;
277       if (fieldName == null)
278          throw new IllegalArgumentException("Argument 'fieldName' is null");
279 
280       // Originally commented out by Oleg....not sure why?
281       // if ( getMethod == null && setMethod == null )
282       // throw new IllegalArgumentException(
283       // "Both arguments 'getMethod' and 'setMethod' are null" );
284 
285       _getSequence = getSequence;
286       _setSequence = setSequence;
287 
288       if (setMethod != null) {
289          // -- might be an "add" method
290          if (setMethod.getName().startsWith(ADD_PREFIX)) {
291             Class<?> pType = setMethod.getParameterTypes()[0];
292             if (pType != typeInfo.getFieldType())
293                setAddMethod(setMethod);
294             else
295                setWriteMethod(setMethod);
296          }
297 
298          // for(Iterator iter = setMethods.iterator(); iter.hasNext(); ) {
299          // Method method = (Method) iter.next();
300          //
301          // if (method.getName().startsWith(ADD_PREFIX)) {
302          // Class paraType = method.getParameterTypes()[0];
303          //
304          // if (paraType != typeInfo.getFieldType()) {
305          // addMethods.add(method);
306          // iter.remove();
307          // }
308          // }
309          // }
310          else
311             setWriteMethod(setMethod);
312       }
313 
314       if (getMethod != null) {
315          // getMethod might be an enumeration or iteration.
316          if (getMethod.getName().startsWith(ENUM_PREFIX)) {
317             Class<?> rType = getMethod.getReturnType();
318 
319             // Check if getMethod really returns an enumeration.
320             if (Enumeration.class.isAssignableFrom(rType))
321                setEnumMethod(getMethod);
322             else
323                // If getMethod does not return an enumeration, treat it as a
324                // normal getMethod.
325                setReadMethod(getMethod);
326          } else if (getMethod.getName().startsWith(ITER_PREFIX)) {
327             Class<?> rType = getMethod.getReturnType();
328 
329             // Check if getMethod really returns an iterator.
330             if (Iterator.class.isAssignableFrom(rType))
331                setIterMethod(getMethod);
332             else
333                // If getMethod does not return an iterator, treat it as a normal
334                // getMethod.
335                setReadMethod(getMethod);
336          } else
337             setReadMethod(getMethod);
338       }
339 
340       _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
341       _fieldName = fieldName + "(" + _fieldType.getName() + ")";
342       _immutable = typeInfo.isImmutable();
343 
344       // If the field is of a primitive type or if it is required
345       // we use the default value
346       if (setMethod != null && setMethod.getParameterTypes()[0].isPrimitive())
347          _default = typeInfo.getDefaultValue();
348       else
349          _default = null;
350       _convertTo = typeInfo.getConvertorTo();
351       _convertFrom = typeInfo.getConvertorFrom();
352       _colHandler = typeInfo.getCollectionHandler();
353    }
354 
355    public TypeConvertor getConvertFrom() {
356       return _convertFrom;
357    }
358 
359    public TypeConvertor getConvertTo() {
360       return _convertTo;
361    }
362 
363    /**
364     * {@inheritDoc}
365     * 
366     * @see org.exolab.castor.mapping.AbstractFieldHandler#getValue(java.lang.Object)
367     */
368    @SuppressWarnings("unchecked")
369    public T getValue(Object object) {
370       T value;
371 
372       try {
373          // If field is accessed directly, get its value. If not, we need to
374          // call
375          // its get method. It's possible to not have a way to access the field.
376 
377          if (_handler != null) {
378             value = _handler.getValue(object);
379          } else if (_field != null) {
380             value = (T) _field.get(object);
381          } else if (_enumMethod != null) {
382             // If there is an enumeration method supplied, return the
383             // enumeration.
384             value = (T) _enumMethod.invoke(object, (Object[]) null);
385          } else if (_iterMethod != null) {
386             // If there is an iterator method supplied, wrap it in an
387             // enumeration.
388             value = (T) new IteratorEnumeration((Iterator<T>) _iterMethod.invoke(object, (Object[]) null));
389          } else if (_getMethod != null) {
390             if (_getSequence != null) {
391                for (int i = 0; i < _getSequence.length; i++) {
392                   object = _getSequence[i].invoke(object, (Object[]) null);
393                   if (object == null) {
394                      break;
395                   }
396                }
397             }
398 
399             // Some of the objects in the sequence might be null, then the value
400             // is null.
401             // If field has 'has' method, false means field is null and do not
402             // attempt to
403             // call getValue. Otherwise, ????
404             if (object == null
405                   || (_hasMethod != null && !((Boolean) _hasMethod.invoke(object, (Object[]) null))
406                         .booleanValue())) {
407                value = null;
408             } else {
409                value = (T) _getMethod.invoke(object, (Object[]) null);
410             }
411          } else {
412             value = null;
413          }
414       } catch (IllegalAccessException except) {
415          throw new CastorIllegalStateException(Messages.format("mapping.schemaChangeNoAccess", toString()),
416                except);
417       } catch (InvocationTargetException except) {
418          throw new CastorIllegalStateException(Messages.format("mapping.schemaChangeInvocation", toString(),
419                except), except);
420       }
421 
422       // -- If a collection, return an enumeration of it's values.
423       // -- Only use collection handler, if there is no convertor or enum
424       // method.
425       if (_colHandler != null && _enumMethod == null && _iterMethod == null && _convertFrom == null) {
426          if (value == null) {
427             return (T) new CollectionHandlers.EmptyEnumerator<T>();
428          }
429          return (T) _colHandler.elements(value);
430       }
431 
432       // If there is a convertor, apply it
433       if (_convertFrom == null || value == null) {
434          return value;
435       }
436 
437       try {
438          return (T) _convertFrom.convert(value);
439       } catch (ClassCastException except) {
440          String errorMessage = Messages.format("mapping.wrongConvertor", value.getClass().getName());
441          throw new IllegalArgumentException(errorMessage, except);
442       }
443    }
444 
445    /**
446     * {@inheritDoc}
447     * 
448     * @see org.exolab.castor.mapping.AbstractFieldHandler#setValue(java.lang.Object,
449     *      java.lang.Object)
450     */
451    @SuppressWarnings("unchecked")
452    public void setValue(Object object, T value) {
453       if (_colHandler == null || _addMethod != null) {
454 
455          // If there is a convertor, apply conversion here.
456          if (value != null && _convertTo != null) {
457             try {
458                value = (T) _convertTo.convert(value);
459             } catch (ClassCastException except) {
460                String errorMessage = Messages.format("mapping.wrongConvertor", value.getClass().getName());
461                throw new IllegalArgumentException(errorMessage, except);
462             }
463          } else {
464             // -- unwrap MapItem if necessary
465             // if (_colHandler != null) {
466             // if ((value instanceof MapItem) && (_fieldType != MapItem.class))
467             // {
468             // value = ((MapItem)value).getValue();
469             // }
470             // }
471          }
472 
473          try {
474             if (_handler != null) {
475                _handler.setValue(object, value);
476             } else if (_field != null) {
477                _field.set(object, value == null ? _default : value);
478             } else {
479 
480                // -- either add or set
481                Method setter = selectWriteMethod(value);
482 
483                if (setter != null) {
484                   if (_getSequence != null) {
485                      for (int i = 0; i < _getSequence.length; i++) {
486                         Object last;
487 
488                         last = object;
489                         object = _getSequence[i].invoke(object, (Object[]) null);
490                         if (object == null) {
491                            // if the value is not null, we must instantiate
492                            // the object in the sequence
493                            if (value == null || _setSequence[i] == null) {
494                               break;
495                            }
496                            object = Types.newInstance(_getSequence[i].getReturnType());
497                            _setSequence[i].invoke(last, new Object[] { object });
498                         }
499                      }
500                   }
501                   if (object != null) {
502                      if (value == null && _deleteMethod != null) {
503                         _deleteMethod.invoke(object, (Object[]) null);
504                      } else {
505                         setter.invoke(object, new Object[] { value == null ? _default : value });
506                      }
507                   }
508                }
509             }
510             // If the field has no set method, ignore it.
511             // If this is a problem, identity it someplace else.
512          } catch (IllegalArgumentException except) {
513             // Graceful way of dealing with unwrapping exception
514             if (value == null) {
515                String errorMessage = Messages.format("mapping.typeConversionNull", toString());
516                throw new IllegalArgumentException(errorMessage);
517             }
518             String errorMessage = Messages.format("mapping.typeConversion", toString(), value.getClass()
519                   .getName());
520             throw new IllegalArgumentException(errorMessage, except);
521          } catch (IllegalAccessException except) {
522             // This should never happen
523             String errorMessage = Messages.format("mapping.schemaChangeNoAccess", toString());
524             throw new CastorIllegalStateException(errorMessage, except);
525          } catch (InvocationTargetException except) {
526             // This should never happen
527             throw new MappingRuntimeException(except.getTargetException());
528          }
529       } else if (value != null) {
530          Object collect;
531          try {
532             // Get the field value (the collection), add the value to it,
533             // possibly yielding a new collection (in the case of an array),
534             // and set that collection back into the field.
535             if (_handler != null) {
536                collect = _handler.getValue(object);
537                collect = _colHandler.add(collect, value);
538                if (collect != null)
539                   _handler.setValue(object, (T) collect);
540             } else if (_field != null) {
541                collect = _field.get(object);
542                if (collect == null) {
543                   // The type of the collection.
544                   Class type = _field.getType();
545                   // -- Handle Arrays, we need to declare the array with
546                   // -- the correct type. The other cases are handled in
547                   // -- the J1CollectionHandler during the
548                   // -- add(collect,value) call
549                   if (type.isArray()) {
550                      Class componentType = type.getComponentType();
551                      Class valueType = value.getClass();
552                      if (componentType.isPrimitive()
553                            || ((!valueType.isArray()) && (valueType != componentType))) {
554                         try {
555                            collect = Array.newInstance(componentType, 0);
556                         } catch (Exception e) {
557                            String err = "Unable to instantiate an array of '" + componentType + "' : " + e;
558                            throw new CastorIllegalStateException(err, e);
559                         }
560                      }
561                   }
562                }
563                collect = _colHandler.add(collect, value);
564                if (collect != null)
565                   _field.set(object, collect);
566 
567             } else if (_getMethod != null) {
568                if (_getSequence != null)
569                   for (int i = 0; i < _getSequence.length; i++)
570                      object = _getSequence[i].invoke(object, (Object[]) null);
571                collect = _getMethod.invoke(object, (Object[]) null);
572 
573                // If we deal with a collection who is an array of primitive
574                // and that has not been instantiated, we have to handle the
575                // instantiation here rather than in J1CollectionHandler,
576                // because we have acces to the Field object here.
577                boolean setCollection = false;
578                if (collect == null) {
579                   // The return type of the get method should be the type of the
580                   // collection.
581                   Class type = _getMethod.getReturnType();
582 
583                   // -- Handle Arrays, we need to declare the array with
584                   // -- the correct type. The other cases are handled in
585                   // -- the J1CollectionHandler during the
586                   // -- add(collect,value) call
587                   if (type.isArray()) {
588                      Class componentType = type.getComponentType();
589                      Class valueType = value.getClass();
590                      if (componentType.isPrimitive()
591                            || ((!valueType.isArray()) && (valueType != componentType))) {
592                         try {
593                            collect = Array.newInstance(componentType, 0);
594                         } catch (Exception e) {
595                            String err = "Unable to instantiate an array of '" + componentType + "' : " + e;
596                            throw new CastorIllegalStateException(err, e);
597                         }
598                      }
599                   }
600                   setCollection = true;
601                } else {
602                   setCollection = collect.getClass().isArray();
603                }
604 
605                Object tmp = _colHandler.add(collect, value);
606 
607                // -- make sure we do not overwrite collect unless
608                // -- the new collection is not null
609                if (tmp != null)
610                   collect = tmp;
611 
612                if (setCollection && (_setMethod != null))
613                   _setMethod.invoke(object, new Object[] { collect });
614             }
615          } catch (IllegalAccessException except) {
616             // This should never happen
617             throw new IllegalStateException(Messages.format("mapping.schemaChangeNoAccess", toString()));
618          } catch (InvocationTargetException except) {
619             // This should never happen
620             throw new MappingRuntimeException(except.getTargetException());
621          }
622       }
623    }
624 
625    public void resetValue(Object object) {
626       if (_colHandler == null) {
627 
628          try {
629             if (_handler != null)
630                _handler.resetValue(object);
631             else if (_field != null)
632                _field.set(object, _default);
633             else if (_setMethod != null) {
634                if (_getSequence != null)
635                   for (int i = 0; i < _getSequence.length; i++) {
636                      object = _getSequence[i].invoke(object, (Object[]) null);
637                      if (object == null)
638                         break;
639                   }
640                if (object != null) {
641                   if (_deleteMethod != null)
642                      _deleteMethod.invoke(object, (Object[]) null);
643                   else
644                      _setMethod.invoke(object, new Object[] { _default });
645                }
646             }
647             // If the field has no set method, ignore it.
648             // If this is a problem, identity it someplace else.
649          } catch (IllegalArgumentException except) {
650             // Graceful way of dealing with unwrapping exception
651             throw new IllegalArgumentException(Messages.format("mapping.typeConversionNull", toString()));
652          } catch (IllegalAccessException except) {
653             // This should never happen
654             throw new IllegalStateException(Messages.format("mapping.schemaChangeNoAccess", toString()));
655          } catch (InvocationTargetException except) {
656             // This should never happen
657             throw new MappingRuntimeException(except.getTargetException());
658          }
659 
660       } else {
661          Object collect;
662 
663          try {
664             // Get the field value (the collection), add the value to it,
665             // possibly yielding a new collection (in the case of an array),
666             // and set that collection back into the field.
667             if (_handler != null) {
668                _handler.resetValue(object);
669             } else if (_field != null) {
670                collect = _field.get(object);
671                collect = _colHandler.clear(collect);
672                if (collect != null)
673                   _field.set(object, collect);
674             } else if (_getMethod != null) {
675                if (_getSequence != null)
676                   for (int i = 0; i < _getSequence.length; i++)
677                      object = _getSequence[i].invoke(object, (Object[]) null);
678                collect = _getMethod.invoke(object, (Object[]) null);
679                collect = _colHandler.clear(collect);
680                if (collect != null && _setMethod != null)
681                   _setMethod.invoke(object, new Object[] { collect });
682             }
683          } catch (IllegalAccessException except) {
684             // This should never happen
685             throw new IllegalStateException(Messages.format("mapping.schemaChangeNoAccess", toString()));
686          } catch (InvocationTargetException except) {
687             // This should never happen
688             throw new MappingRuntimeException(except.getTargetException());
689          }
690 
691       }
692    }
693 
694    /**
695     * Creates a new instance of the object described by this field.
696     * 
697     * @param parent
698     *           The object for which the field is created
699     * @return A new instance of the field's value
700     * @throws IllegalStateException
701     *            This field is a simple type and cannot be instantiated
702     */
703    public T newInstance(Object parent) throws IllegalStateException {
704       return newInstance(parent, null);
705    }
706 
707    /**
708     * Creates a new instance of the object described by this field.
709     * 
710     * @param parent
711     *           The object for which the field is created
712     * @param args
713     *           the set of constructor arguments
714     * @return A new instance of the field's value
715     * @throws IllegalStateException
716     *            This field is a simple type and cannot be instantiated
717     */
718    @SuppressWarnings("unchecked")
719    public T newInstance(Object parent, Object[] args) throws IllegalStateException {
720       if (_fieldType.isInterface() && _createMethod == null)
721          return null;
722 
723       if ((_immutable) && ((args == null) || (args.length == 0)))
724          throw new IllegalStateException(Messages.format("mapping.classNotConstructable", _fieldType));
725 
726       if (_handler != null) {
727          if (_handler instanceof ExtendedFieldHandler)
728             return (T) ((ExtendedFieldHandler<T>) _handler).newInstance(parent, args);
729          return _handler.newInstance(parent);
730       }
731       // If we have a create method and parent object, call the create method.
732       if (_createMethod != null && parent != null) {
733          try {
734             return (T) _createMethod.invoke(parent, args);
735          } catch (IllegalAccessException except) {
736             // This should never happen
737             throw new IllegalStateException(Messages.format("mapping.schemaChangeNoAccess", toString()));
738          } catch (InvocationTargetException except) {
739             // This should never happen
740             throw new MappingRuntimeException(except.getTargetException());
741          }
742       }
743       return (T) Types.newInstance(_fieldType, args);
744    } // -- newInstance
745 
746    /**
747     * Mutator method used by {@link AbstractMappingLoader}.
748     */
749    void setRequired(final boolean required) {
750    }
751 
752    /**
753     * Sets the TypeConvertor used during calls to getValue
754     * 
755     * @param convertor
756     *           the TypeConvertor to use during calls to getValue
757     **/
758    public void setConvertFrom(TypeConvertor convertor) {
759       _convertFrom = convertor;
760    } // -- setConvertFrom
761 
762    /**
763     * Sets the TypeConvertor used during calls to setValue
764     * 
765     * @param convertor
766     *           the TypeConvertor to use during calls to setValue
767     **/
768    public void setConvertTo(TypeConvertor convertor) {
769       _convertTo = convertor;
770    } // -- setConvertTo
771 
772    /**
773     * Mutator method used by {@link AbstractMappingLoader} and
774     * {@link org.exolab.castor.xml.Introspector}. Please understand how this
775     * method is used before you start playing with it! :-)
776     */
777    public void setCreateMethod(Method method) throws MappingException {
778       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
779          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
780                .getName());
781       if (method.getParameterTypes().length != 0)
782          throw new MappingException("mapping.createMethodNoParam", method, method.getDeclaringClass()
783                .getName());
784       _createMethod = method;
785    }
786 
787    /**
788     * Mutator method used by {@link AbstractMappingLoader} and
789     * {@link org.exolab.castor.xml.Introspector}. Please understand how this
790     * method is used before you start playing with it! :-)
791     */
792    public void setHasDeleteMethod(Method hasMethod, Method deleteMethod) throws MappingException {
793       if (hasMethod != null) {
794          if ((hasMethod.getModifiers() & Modifier.PUBLIC) == 0
795                || (hasMethod.getModifiers() & Modifier.STATIC) != 0)
796             throw new MappingException("mapping.accessorNotAccessible", hasMethod, hasMethod
797                   .getDeclaringClass().getName());
798          if (hasMethod.getParameterTypes().length != 0)
799             throw new MappingException("mapping.createMethodNoParam", hasMethod, hasMethod
800                   .getDeclaringClass().getName());
801          _hasMethod = hasMethod;
802       }
803 
804       if (deleteMethod != null) {
805          if ((deleteMethod.getModifiers() & Modifier.PUBLIC) == 0
806                || (deleteMethod.getModifiers() & Modifier.STATIC) != 0)
807             throw new MappingException("mapping.accessorNotAccessible", deleteMethod, deleteMethod
808                   .getDeclaringClass().getName());
809          if (deleteMethod.getParameterTypes().length != 0)
810             throw new MappingException("mapping.createMethodNoParam", deleteMethod, deleteMethod
811                   .getDeclaringClass().getName());
812          _deleteMethod = deleteMethod;
813       }
814    }
815 
816    /**
817     * Mutator method used by {@link org.exolab.castor.xml.Introspector}. Please
818     * understand how this method is used before you start playing with it! :-)
819     */
820    public void setReadMethod(Method method) throws MappingException {
821       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
822          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
823                .getName());
824       if (method.getParameterTypes().length != 0)
825          throw new MappingException("mapping.readMethodHasParam", method, method.getDeclaringClass()
826                .getName());
827       _getMethod = method;
828    }
829 
830    /**
831     * Mutator method used by {@link org.exolab.castor.xml.Introspector}. Please
832     * understand how this method is used before you start playing with it! :-)
833     */
834    public void setWriteMethod(Method method) throws MappingException {
835       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
836          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
837                .getName());
838       if (method.getParameterTypes().length != 1)
839          throw new MappingException("mapping.writeMethodNoParam", method, method.getDeclaringClass()
840                .getName());
841       _setMethod = method;
842    }
843 
844    /**
845     * Mutator method used by {@link org.exolab.castor.xml.Introspector}. Please
846     * understand how this method is used before you start playing with it! :-)
847     */
848    public void setAddMethod(Method method) throws MappingException {
849       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
850          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
851                .getName());
852       if (method.getParameterTypes().length != 1)
853          throw new MappingException("mapping.writeMethodNoParam", method, method.getDeclaringClass()
854                .getName());
855       _addMethod = method;
856 
857       // -- make sure add method is not the same as the set method
858       if (_addMethod == _setMethod)
859          _setMethod = null;
860 
861    } // -- setAddMethod
862 
863    /**
864     * Sets the enumeration method.
865     */
866    public void setEnumMethod(Method method) throws MappingException {
867       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
868          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
869                .getName());
870       if (method.getParameterTypes().length != 0)
871          throw new MappingException("mapping.readMethodHasParam", method, method.getDeclaringClass()
872                .getName());
873 
874       _enumMethod = method;
875    }
876 
877    /**
878     * Sets the iteration method.
879     */
880    public void setIterMethod(Method method) throws MappingException {
881       if ((method.getModifiers() & Modifier.PUBLIC) == 0 || (method.getModifiers() & Modifier.STATIC) != 0)
882          throw new MappingException("mapping.accessorNotAccessible", method, method.getDeclaringClass()
883                .getName());
884       if (method.getParameterTypes().length != 0)
885          throw new MappingException("mapping.readMethodHasParam", method, method.getDeclaringClass()
886                .getName());
887 
888       _iterMethod = method;
889    }
890 
891    /**
892     * Selects the appropriate "write" method based on the value. This is used
893     * when there is an "add" method and a "set" method.
894     * 
895     * @return the selected write method
896     **/
897    private Method selectWriteMethod(Object value) {
898       if (_setMethod != null) {
899 
900          if (_addMethod == null)
901             return _setMethod;
902 
903          if (value == null) {
904             if (_default != null)
905                value = _default;
906             else
907                return _setMethod;
908          }
909 
910          // -- check value's class type
911          Class paramType = _setMethod.getParameterTypes()[0];
912 
913          if (paramType.isAssignableFrom(value.getClass()))
914             return _setMethod;
915       }
916 
917       return _addMethod;
918 
919    } // -- selectWriteMethod
920 
921    /**
922     * Return true if the field is a collection.
923     */
924    public boolean isCollection() {
925       return (_colHandler != null);
926    }
927 
928    public String toString() {
929       return _fieldName;
930    }
931 
932    /**
933     * Sets the FieldDescriptor that this FieldHander is responsibile for. By
934     * setting the FieldDescriptor, it allows the implementation of the
935     * FieldHandler methods to obtain information about the field itself. This
936     * allows a particular implementation to become more generic and reusable.
937     * 
938     * @param fieldDesc
939     *           the FieldDescriptor to set
940     */
941    public void setFieldDescriptor(FieldDescriptor fieldDesc) {
942       super.setFieldDescriptor(fieldDesc);
943       if (_handler != null) {
944          if (_handler instanceof GeneralizedFieldHandler) {
945             ((GeneralizedFieldHandler) _handler).setFieldDescriptor(fieldDesc);
946          }
947       }
948    }
949 
950 }