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