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   * Portions of this file developed by Keith Visco after Jan 19 2005 are
44   * Copyright (C) 2005 Keith Visco. All Rights Reserverd.
45   *
46   * $Id$
47   */
48  package org.exolab.castor.mapping.loader;
49  
50  import java.io.Serializable;
51  import java.lang.reflect.Array;
52  import java.lang.reflect.Constructor;
53  import java.lang.reflect.Field;
54  import java.lang.reflect.Method;
55  import java.lang.reflect.Modifier;
56  import java.util.ArrayList;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Enumeration;
61  import java.util.Map;
62  import java.util.Properties;
63  
64  
65  import org.castor.xml.InternalContext;
66  import org.castor.xml.AbstractInternalContext;
67  import org.castor.core.util.Messages;
68  import org.exolab.castor.mapping.ClassDescriptor;
69  import org.exolab.castor.mapping.ClonableFieldHandler;
70  import org.exolab.castor.mapping.ClonableFieldHandlerMarker;
71  import org.exolab.castor.mapping.CollectionHandler;
72  import org.exolab.castor.mapping.ConfigurableFieldHandler;
73  import org.exolab.castor.mapping.ExtendedFieldHandler;
74  import org.exolab.castor.mapping.FieldDescriptor;
75  import org.exolab.castor.mapping.FieldHandler;
76  import org.exolab.castor.mapping.GeneralizedFieldHandler;
77  import org.exolab.castor.mapping.MapItem;
78  import org.exolab.castor.mapping.MappingException;
79  import org.exolab.castor.mapping.ValidityException;
80  import org.exolab.castor.mapping.handlers.EnumFieldHandler;
81  import org.exolab.castor.mapping.handlers.TransientFieldHandler;
82  import org.exolab.castor.mapping.xml.ClassChoice;
83  import org.exolab.castor.mapping.xml.FieldHandlerDef;
84  import org.exolab.castor.mapping.xml.MappingRoot;
85  import org.exolab.castor.mapping.xml.ClassMapping;
86  import org.exolab.castor.mapping.xml.FieldMapping;
87  import org.exolab.castor.mapping.xml.Param;
88  
89  /**
90   * Assists in the construction of descriptors. Can be used as a mapping
91   * resolver to the engine. Engines will implement their own mapping
92   * scheme typically by extending this class.
93   *
94   * @author <a href="arkin@intalio.com">Assaf Arkin</a>
95   * @author <a href="keith AT kvisco DOT com">Keith Visco</a>
96   * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $
97   */
98  public abstract class AbstractMappingLoader extends AbstractMappingLoader2 {
99  
100     /** The prefix for the "add" method. */
101     private static final String ADD_METHOD_PREFIX = "add";
102 
103     /** The prefix for an enumeration method. */
104     private static final String ENUM_METHOD_PREFIX = "enum";
105 
106     /** The prefix for an enumeration method. */
107     private static final String ITER_METHOD_PREFIX = "iterate";
108 
109     /** The standard prefix for the getter method. */
110     private static final String GET_METHOD_PREFIX = "get";
111 
112     /** The prefix for the "is" method for booleans. */
113     private static final String IS_METHOD_PREFIX = "is";
114 
115     /** The standard prefix for the setter method. */
116     private static final String SET_METHOD_PREFIX = "set";
117 
118     /** The prefix for the "create" method. */
119     private static final String CREATE_METHOD_PREFIX = "create";
120 
121     /** The prefix for the "has" method. */
122     private static final String HAS_METHOD_PREFIX = "has";
123 
124     /** The prefix for the "delete" method. */
125     private static final String DELETE_METHOD_PREFIX = "delete";
126 
127     /** Empty array of class types used for reflection. */
128     protected static final Class<?>[] EMPTY_ARGS = new Class[0];
129 
130     /** The string argument for the valueOf method, used for introspection when searching for
131      *  type-safe enumeration style classes. */
132     protected static final Class<?>[] STRING_ARG = {String.class};
133 
134     /** Factory method name for type-safe enumerations. */
135     protected static final String VALUE_OF = "valueOf";
136 
137     /** Method name to get string value of a type-safe enumerations. */
138     protected static final String NAME = "name";
139 
140     /**
141      * The {@link AbstractInternalContext} is the centerpiece providing runtime configuration
142      * and state information.
143      */
144     private InternalContext _internalContext;
145 
146     /** Map of field handlers associated by their name. */
147     private final Map<String, FieldHandler> _fieldHandlers = new HashMap<String, FieldHandler>();
148 
149     /**
150      * Constructs a new mapping helper. This constructor is used by a derived class.
151      *
152      * @param loader The class loader to use, null for the default
153      */
154     protected AbstractMappingLoader(final ClassLoader loader) {
155         super(loader);
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     public final String getSourceType() {
162         return "CastorXmlMapping";
163     }
164 
165     /**
166      * Loads the mapping from the specified mapping object if not loaded previously.
167      *
168      * @param mapping The mapping information.
169      * @param param Arbitrary parameter that can be used by subclasses.
170      * @throws MappingException The mapping file is invalid.
171      */
172     public abstract void loadMapping(final MappingRoot mapping, final Object param)
173     throws MappingException;
174     
175     /**
176      * Load field handler definitions, check for duplicate definitions and 
177      * instantiate the appropriate FieldHandler implementations.
178      * 
179      * @param mapping Mapping to load field handler definitions from.
180      * @throws MappingException If mapping contains more then one field handler
181      *        definition with same name.
182      */
183     protected void createFieldHandlers(final MappingRoot mapping)
184     throws MappingException {
185         Enumeration<? extends FieldHandlerDef> enumeration = mapping.enumerateFieldHandlerDef();
186         while (enumeration.hasMoreElements()) {
187             FieldHandlerDef def = enumeration.nextElement();
188             
189             String name = def.getName();
190             
191             if (_fieldHandlers.containsKey(name)) {
192                 throw new MappingException(Messages.format("mapping.dupFieldHandler", name));
193             }
194             
195             
196             Class<?> clazz = resolveType(def.getClazz());
197             FieldHandler fieldHandler = null;
198             try {
199                 if (!FieldHandler.class.isAssignableFrom(clazz)) {
200                     throw new MappingException(Messages.format("mapping.classNotFieldHandler", 
201                             name, def.getClazz()));
202                 }
203                 fieldHandler = (FieldHandler) clazz.newInstance();
204                 _fieldHandlers.put(name, fieldHandler);
205             } catch (InstantiationException e) {
206                 throw new MappingException(e);
207             } catch (IllegalAccessException e) {
208                 throw new MappingException(e);
209             }
210             
211             // Add configuration data, if there is any
212             configureFieldHandler(def, fieldHandler);
213             
214          }
215     }
216 
217     /*
218      * Checks if the field handler is configurable, and adds the configuration
219      * data to the field handler if this is the case.
220      */
221     private void configureFieldHandler(final FieldHandlerDef def, final FieldHandler fieldHandler) 
222     throws MappingException {
223         
224         // Gather the configuration data (parameters).
225         Properties params = new Properties();            
226         Enumeration<? extends Param> enumerateParam = def.enumerateParam();
227         while (enumerateParam.hasMoreElements()) {
228             Param par = enumerateParam.nextElement();
229             params.put(par.getName(), par.getValue());
230         }
231         
232         // If there is configuration data, make sure that the field handler class
233         // supports it.
234         if (params.size() > 0) {
235             if (!ConfigurableFieldHandler.class.isAssignableFrom(fieldHandler.getClass())) {
236                 throw new MappingException(Messages.format("mapping.classNotConfigurableFieldHandler", 
237                         def.getName(), def.getClazz()));
238             }
239             
240             // Pass the configuration data to the field handler.
241             try {
242                 ((ConfigurableFieldHandler)fieldHandler).setConfiguration(params);
243             } catch (ValidityException e) {
244                 throw new MappingException(Messages.format("mapping.invalidFieldHandlerConfig",
245                         def.getName(), e.getMessage()), e);
246             }
247         }
248     }
249     
250     protected final void createClassDescriptors(final MappingRoot mapping)
251     throws MappingException {
252         // Load the mapping for all the classes. This is always returned
253         // in the same order as it appeared in the mapping file.
254         Enumeration<? extends ClassMapping> enumeration = mapping.enumerateClassMapping();
255 
256         List<ClassMapping> retryList = new ArrayList<ClassMapping>();
257         while (enumeration.hasMoreElements()) {
258             ClassMapping clsMap = enumeration.nextElement();
259             try {
260                 ClassDescriptor clsDesc = createClassDescriptor(clsMap);
261                 if (clsDesc != null) { 
262                     addDescriptor(clsDesc); 
263                 }
264             } catch (MappingException mx) {
265                 // save for later for possible out-of-order mapping files...
266                 retryList.add(clsMap);
267                 continue;
268             }
269         }
270 
271         // handle possible retries, for now we only loop once on the retries, but we
272         // should change this to keep looping until we have no more success rate.
273         for (ClassMapping classMapping : retryList) {
274             ClassDescriptor clsDesc = createClassDescriptor(classMapping);
275             if (clsDesc != null) { 
276                 addDescriptor(clsDesc); 
277             }
278         }
279 
280         // iterate over all class descriptors and resolve relations between them
281         for (ClassDescriptor classDescriptor : getDescriptors()) {
282             resolveRelations(classDescriptor);
283         }
284     }
285 
286     protected abstract ClassDescriptor createClassDescriptor(final ClassMapping clsMap)
287     throws MappingException;
288 
289     /**
290      * Gets the ClassDescriptor the given <code>classMapping</code> extends.
291      *
292      * @param clsMap The ClassMapping to find the required descriptor for.
293      * @param javaClass The name of the class that is checked (this is used for
294      *        generating the exception).
295      * @return The ClassDescriptor the given ClassMapping extends or
296      *         <code>null</code> if the given ClassMapping does not extend
297      *         any.
298      * @throws MappingException If the given ClassMapping extends another
299      *         ClassMapping but its descriptor could not be found.
300      */
301     protected final ClassDescriptor getExtended(final ClassMapping clsMap,
302                                                 final Class<?> javaClass) throws MappingException {
303         if (clsMap.getExtends() == null) { 
304             return null;
305         }
306 
307         ClassMapping mapping = (ClassMapping) clsMap.getExtends();
308         Class<?> type = resolveType(mapping.getName());
309         ClassDescriptor result = getDescriptor(type.getName());
310 
311         if (result == null) {
312             throw new MappingException(
313                     "mapping.extendsMissing", mapping, javaClass.getName());
314         }
315 
316         if (!result.getJavaClass().isAssignableFrom(javaClass)) {
317             throw new MappingException("mapping.classDoesNotExtend",
318                     javaClass.getName(), result.getJavaClass().getName());
319         }
320 
321         return result;
322     }
323 
324     /**
325      * Gets the ClassDescriptor the given <code>classMapping</code> depends
326      * on.
327      *
328      * @param clsMap The ClassMapping to find the required ClassDescriptor for.
329      * @param javaClass The name of the class that is checked (this is used for
330      *        generating the exception).
331      * @return The ClassDescriptor the given ClassMapping depends on or
332      *         <code>null</code> if the given ClassMapping does not depend on
333      *         any.
334      * @throws MappingException If the given ClassMapping depends on another
335      *         ClassMapping but its descriptor could not be found.
336      */
337     protected final ClassDescriptor getDepended(final ClassMapping clsMap,
338                                                 final Class<?> javaClass) throws MappingException {
339         if (clsMap.getDepends() == null) { 
340             return null; 
341         }
342 
343         ClassMapping mapping = (ClassMapping) clsMap.getDepends();
344         Class<?> type = resolveType(mapping.getName());
345         ClassDescriptor result = getDescriptor(type.getName());
346 
347         if (result == null) {
348             throw new MappingException(
349                     "Depends not found: " + mapping + " " + javaClass.getName());
350         }
351 
352         return result;
353     }
354 
355     /**
356      * Checks all given fields for name equality and throws a MappingException if at
357      * least two fields have the same name.
358      *
359      * @param fields The fields to be checked.
360      * @param cls Class that is checked (this is used for generating the exception).
361      * @throws MappingException If at least two fields have the same name.
362      */
363     protected final void checkFieldNameDuplicates(final FieldDescriptor[] fields,
364                                                   final Class<?> cls) throws MappingException {
365         for (int i = 0; i < fields.length - 1; i++) {
366             String fieldName = fields[i].getFieldName();
367             for (int j = i + 1; j < fields.length; j++) {
368                 if (fieldName.equals(fields[j].getFieldName())) {
369                     throw new MappingException("The field " + fieldName
370                             + " appears twice in the descriptor for " + cls.getName());
371                 }
372             }
373         }
374     }
375 
376     protected abstract void resolveRelations(final ClassDescriptor clsDesc);
377 
378     //--------------------------------------------------------------------------
379 
380     /**
381      * Returns the Java class for the named type. The type name can be one of the
382      * accepted short names (e.g. <tt>integer</tt>) or the full Java class name (e.g.
383      * <tt>java.lang.Integer</tt>). If the short name is used, the primitive type might
384      * be returned.
385      */
386     protected final Class<?> resolveType(final String typeName)
387     throws MappingException {
388         try {
389             return Types.typeFromName(getClassLoader(), typeName);
390         } catch (ClassNotFoundException ex) {
391             throw new MappingException("mapping.classNotFound", typeName);
392         }
393     }
394 
395     /**
396      * Create field descriptors. The class mapping information is used to create
397      * descriptors for all the fields in the class, except for container fields.
398      * Implementations may extend this method to create more suitable descriptors, or
399      * create descriptors only for a subset of the fields.
400      *
401      * @param clsMap The class to which the fields belong.
402      * @param javaClass The field mappings.
403      * @throws MappingException An exception indicating why mapping for the class cannot
404      *         be created.
405      */
406     protected final FieldDescriptorImpl[] createFieldDescriptors(final ClassMapping clsMap,
407                                                                      final Class<?> javaClass) throws MappingException {
408         FieldMapping[] fldMap = null;
409 
410         if (clsMap.getClassChoice() != null) {
411             fldMap = clsMap.getClassChoice().getFieldMapping();
412         }
413 
414         if ((fldMap == null) || (fldMap.length == 0)) {
415             return new FieldDescriptorImpl[0];
416         }
417 
418         FieldDescriptorImpl[] fields = new FieldDescriptorImpl[fldMap.length];
419         for (int i = 0; i < fldMap.length; i++) {
420             fields[i] = createFieldDesc(javaClass, fldMap[i]);
421 
422             // set identity flag
423             fields[i].setIdentity(fldMap[i].getIdentity());
424         }
425 
426         return fields;
427     }
428 
429     /**
430      * Gets the top-most (i.e. without any further 'extends') extends of the given
431      * <code>classMapping</code>.
432      *
433      * @param clsMap The ClassMapping to get the origin for.
434      * @return The top-most extends of the given ClassMapping or the ClassMapping itself
435      *         if it does not extend any other ClassMapping.
436      */
437     protected final ClassMapping getOrigin(final ClassMapping clsMap) {
438         ClassMapping result = clsMap;
439 
440         while (result.getExtends() != null) {
441             result = (ClassMapping) result.getExtends();
442         }
443 
444         return result;
445     }
446 
447     protected final FieldDescriptor[] divideFieldDescriptors(final FieldDescriptor[] fields,
448             final String[] ids, final FieldDescriptor[] identities) {
449         List<FieldDescriptor> fieldList = new ArrayList<FieldDescriptor>(fields.length);
450 
451         for (int i = 0; i < fields.length; i++) {
452             FieldDescriptor field = fields[i];
453             final int index = getIdColumnIndex(field, ids);
454             if (index == -1) {
455                 // copy non identity field from list of fields.
456                 fieldList.add(field);
457             } else {
458                 if (field instanceof FieldDescriptorImpl) {
459                     ((FieldDescriptorImpl) field).setRequired(true);
460                 }
461                 if (field.getHandler() instanceof FieldHandlerImpl) {
462                     ((FieldHandlerImpl) field.getHandler()).setRequired(true);
463                 }
464 
465                 identities[index] = field;
466             }
467         }
468 
469         // convert regularFieldList into array
470         FieldDescriptor[] result = new FieldDescriptor[fieldList.size()];
471         return fieldList.toArray(result);
472     }
473 
474     /**
475      * Finds the index in the given <code>idColumnNames</code> that has the same name as
476      * the given <code>field</code>.
477      *
478      * @param field The FieldDescriptor to find the column index for.
479      * @param ids The id columnNames available.
480      * @return The index of the id column name that matches the given field's name or
481      *         <code>-1</code> if no such id column name exists.
482      */
483     protected int getIdColumnIndex(FieldDescriptor field, String[] ids) {
484         for (int i = 0; i < ids.length; i++) {
485             if (field.getFieldName().equals(ids[i])) {
486                 return i;
487             }
488         }
489         return -1;
490     }
491 
492     /**
493      * Creates a single field descriptor. The field mapping is used to create a new stock
494      * {@link FieldDescriptor}. Implementations may extend this class to create a more
495      * suitable descriptor.
496      *
497      * @param javaClass The class to which the field belongs.
498      * @param fieldMap The field mapping information.
499      * @return The field descriptor.
500      * @throws MappingException The field or its accessor methods are not
501      *         found, not accessible, not of the specified type, etc.
502      */
503     protected FieldDescriptorImpl createFieldDesc(final Class<?> javaClass,
504                                                       final FieldMapping fieldMap) throws MappingException {
505         String fieldName = fieldMap.getName();
506 
507         // If the field type is supplied, grab it and use it to locate the field/accessor.
508         Class<?> fieldType = null;
509         if (fieldMap.getType() != null) {
510             fieldType = resolveType(fieldMap.getType());
511         }
512 
513         // If the field is declared as a collection, grab the collection type as
514         // well and use it to locate the field/accessor.
515         CollectionHandler collectionHandler = null;
516         if (fieldMap.getCollection() != null) {
517             String colTypeName = fieldMap.getCollection().toString();
518             Class<?> collectionType = CollectionHandlers.getCollectionType(colTypeName);
519             collectionHandler = CollectionHandlers.getHandler(collectionType);
520         }
521 
522         TypeInfo typeInfo = getTypeInfo(fieldType, collectionHandler, fieldMap);
523 
524         ExtendedFieldHandler exfHandler = null;
525         FieldHandler handler = null;
526 
527         // Check for user supplied FieldHandler
528         if (fieldMap.getHandler() != null) {
529             handler = getFieldHandler(fieldMap);
530 
531             // ExtendedFieldHandler?
532             if (handler instanceof ExtendedFieldHandler) {
533                 exfHandler = (ExtendedFieldHandler) handler;
534             }
535 
536             // Fix for CastorJDO from Steve Vaughan, CastorJDO requires FieldHandlerImpl
537             // or a ClassCastException will be thrown... [KV 20030131 - also make sure
538             // this new handler doesn't use it's own CollectionHandler otherwise it'll
539             // cause unwanted calls to the getValue method during unmarshalling]
540             collectionHandler = typeInfo.getCollectionHandler();
541             typeInfo.setCollectionHandler(null);
542             handler = new FieldHandlerImpl(handler, typeInfo);
543             typeInfo.setCollectionHandler(collectionHandler);
544             // End Castor JDO fix
545         }
546 
547         boolean generalized = (exfHandler instanceof GeneralizedFieldHandler);
548 
549         // If generalized we need to change the fieldType to whatever is specified in the
550         // GeneralizedFieldHandler so that the correct getter/setter methods can be found
551         if (generalized) {
552             fieldType = ((GeneralizedFieldHandler)exfHandler).getFieldType();
553         }
554 
555         if (generalized || (handler == null)) {
556             // Create TypeInfoRef to get new TypeInfo from call to createFieldHandler
557             FieldHandler custom = handler;
558             TypeInfoReference typeInfoRef = new TypeInfoReference();
559             typeInfoRef.typeInfo = typeInfo;
560             handler = createFieldHandler(javaClass, fieldType, fieldMap, typeInfoRef);
561             if (custom != null) {
562                 ((GeneralizedFieldHandler) exfHandler).setFieldHandler(handler);
563                 handler = custom;
564             } else {
565                 boolean isTypeSafeEnum = false;
566                 // Check for type-safe enum style classes
567                 if ((fieldType != null) && !isPrimitive(fieldType)) {
568                     if (!hasPublicDefaultConstructor(fieldType)) {
569                         Method method = getStaticValueOfMethod(fieldType);
570                         if (method != null) {
571                             handler = new EnumFieldHandler(fieldType, handler, method);
572                             typeInfo.setImmutable(true);
573                             isTypeSafeEnum = true;
574                         }
575                     }
576                 }
577                 // Reset proper TypeInfo
578                 if (!isTypeSafeEnum) { typeInfo = typeInfoRef.typeInfo; }
579             }
580         }
581 
582         FieldDescriptorImpl fieldDesc = new FieldDescriptorImpl(
583                 fieldName, typeInfo, handler, fieldMap.getTransient());
584 
585         fieldDesc.setRequired(fieldMap.getRequired());
586         
587         // set collection type as specified in mapping file
588         fieldDesc.setCollection(fieldMap.getCollection());
589         
590         // set various method informations 
591         fieldDesc.setComparator(fieldMap.getComparator());
592         fieldDesc.setCreateMethod(fieldMap.getCreateMethod());
593         fieldDesc.setGetMethod(fieldMap.getGetMethod());
594         fieldDesc.setSetMethod(fieldMap.getSetMethod());
595         
596         // set direct access (if defined)
597         fieldDesc.setDirect(fieldMap.getDirect());
598         
599         // extract values for 'laziness' from field mapping
600         fieldDesc.setLazy(fieldMap.isLazy());
601         
602         // If we're using an ExtendedFieldHandler we need to set the FieldDescriptor
603         if (exfHandler != null) {
604             ((FieldHandlerFriend) exfHandler).setFieldDescriptor(fieldDesc);
605         }
606 
607         return fieldDesc;
608     }
609 
610     private FieldHandler<?> getFieldHandler(final FieldMapping fieldMap) 
611     throws MappingException {
612         
613         // If there is a custom field handler present in the mapping, that one
614         // is returned.
615         FieldHandler<?> handler = _fieldHandlers.get(fieldMap.getHandler());
616         if (handler != null) {
617 
618             if (!(handler instanceof ClonableFieldHandler || handler instanceof ClonableFieldHandlerMarker)) {
619                 return handler;
620             }
621             
622             String methodName = "copyFieldHandler";
623             if (handler instanceof ClonableFieldHandler) {
624                methodName = "copyInstance";
625             }
626             
627             FieldHandler<?> clonedHandler = handler;
628             Class<?> classToClone = handler.getClass();
629             try {
630                Method method;
631                method = classToClone.getMethod(methodName, (Class[]) null);
632                clonedHandler = (FieldHandler<?>) method.invoke(handler, (Object[]) null);
633                return clonedHandler;
634             } catch (Exception e) {
635                String err = "The class '" + classToClone.getName() + "' must implement the ClonableFieldHandlerMarker interface.";
636                throw new MappingException(err, e);
637             }
638         }
639         
640         Class<?> handlerClass = null;
641         handlerClass = resolveType(fieldMap.getHandler());
642 
643         if (!FieldHandler.class.isAssignableFrom(handlerClass)) {
644             String err = "The class '" + fieldMap.getHandler() + "' must implement "
645                        + FieldHandler.class.getName();
646             throw new MappingException(err);
647         }
648 
649         // Get default constructor to invoke. We can't use the newInstance method
650         // unfortunately because FieldHandler overloads this method
651         try {
652             Constructor<?> constructor = handlerClass.getConstructor(new Class[0]);
653             return (FieldHandler<?>) constructor.newInstance(new Object[0]);
654         } catch (Exception ex) {
655             String err = "The class '" + handlerClass.getName()
656                        + "' must have a default public constructor.";
657             throw new MappingException(err);
658         }
659     }
660 
661     /**
662      * Does the given class has a public default constructor?
663      *
664      * @param type Class to check for a public default constructor.
665      * @return <code>true</code> if class has a public default constructor.
666      */
667     private boolean hasPublicDefaultConstructor(final Class<?> type) {
668         try {
669             Constructor<?> cons = type.getConstructor(EMPTY_ARGS);
670             return Modifier.isPublic(cons.getModifiers());
671         } catch (NoSuchMethodException ex) {
672             return false;
673         }
674     }
675 
676     /**
677      * Get static valueOf(String) factory method of given class.
678      *
679      * @param type Class to check for a static valueOf(String) factory method.
680      * @return Static valueOf(String) factory method or <code>null</code> if none could
681      *         be found.
682      */
683     private Method getStaticValueOfMethod(final Class<?> type) {
684         try {
685             Method method = type.getMethod(VALUE_OF, STRING_ARG);
686             Class<?> returnType = method.getReturnType();
687             if (returnType == null) {
688                 return null;
689             }
690             if (!type.isAssignableFrom(returnType)) { 
691                 return null; 
692             }
693             if (!Modifier.isStatic(method.getModifiers())) { 
694                 return null; 
695             }
696             return method;
697         } catch (NoSuchMethodException ex) {
698             return null;
699         }
700     }
701 
702     /**
703      * Creates the FieldHandler for the given FieldMapping.
704      *
705      * @param javaClass the class type of the parent of the field.
706      * @param fldType the Java class type for the field.
707      * @param fldMap the field mapping.
708      * @return the newly created FieldHandler.
709      */
710     protected final FieldHandler createFieldHandler(Class<?> javaClass, Class<?> fldType,
711                                                     final FieldMapping fldMap,
712                                                     final TypeInfoReference typeInfoRef) throws MappingException {
713         // Prevent introspection of transient fields
714         if (fldMap.getTransient()) {
715             return new TransientFieldHandler();
716         }
717 
718         Class<?> collectionType = null;
719         CollectionHandler collectionHandler = null;
720         boolean colRequireGetSet = true;
721 
722         String fieldName = fldMap.getName();
723 
724         // If the field is declared as a collection, grab the collection type as
725         // well and use it to locate the field/accessor.
726         if (fldMap.getCollection() != null) {
727             String colTypeName = fldMap.getCollection().toString();
728             collectionType = CollectionHandlers.getCollectionType(colTypeName);
729             collectionHandler = CollectionHandlers.getHandler(collectionType);
730             colRequireGetSet = CollectionHandlers.isGetSetCollection(collectionType);
731             if (collectionType == Object[].class) {
732                 if (fldType == null) {
733                     String msg = "'type' is a required attribute for field that are "
734                                + "array collections: " + fieldName;
735                     throw new MappingException(msg);
736                 }
737                 Object obj = Array.newInstance(fldType, 0);
738                 collectionType = obj.getClass();
739             }
740         }
741 
742 
743         FieldHandlerImpl  handler            = null;
744 
745         // If get/set methods not specified, use field names to determine them.
746         if (fldMap.getDirect()) {
747             // No accessor, map field directly.
748             Field field = findField(javaClass, fieldName, (collectionType == null ? fldType : collectionType));
749             if (field == null) {
750                 throw new MappingException(
751                         "mapping.fieldNotAccessible", fieldName, javaClass.getName());
752             }
753             if (fldType == null) {
754                 fldType = field.getType();
755             }
756 
757             typeInfoRef.typeInfo = getTypeInfo(fldType, collectionHandler, fldMap);
758 
759             handler = new FieldHandlerImpl(field, typeInfoRef.typeInfo);
760         } else if ((fldMap.getGetMethod() == null) && (fldMap.getSetMethod() == null)) {
761             // If both methods (get/set) are not specified, determine them automatically
762             if (fieldName == null) {
763                 throw new MappingException(
764                         "mapping.missingFieldName", javaClass.getName());
765             }
766 
767             List<Method> getSequence = new ArrayList<Method>();
768             List<Method> setSequence = new ArrayList<Method>();
769             Method getMethod = null;
770             Method setMethod = null;
771 
772             // Get method normally starts with "get", but may start with "is"
773             // if it's a boolean.
774             try {
775                 // Handle nested fields
776                 while (true) {
777                     int point = fieldName.indexOf('.');
778                     if (point < 0) { break; }
779 
780                     String parentField = fieldName.substring(0, point);
781 
782                     // Getter method for parent field
783                     String methodName = GET_METHOD_PREFIX + capitalize(parentField);
784                     Method method = javaClass.getMethod(methodName, (Class[]) null);
785                     if (isAbstractOrStatic(method)) {
786                         throw new MappingException("mapping.accessorNotAccessible",
787                                 methodName, javaClass.getName());
788                     }
789                     getSequence.add(method);
790 
791                     Class<?> nextClass = method.getReturnType();
792 
793                     // Setter method for parent field
794                     try {
795                         methodName = SET_METHOD_PREFIX + capitalize(parentField);
796                         Class<?>[] types = new Class[] {nextClass};
797                         method = javaClass.getMethod(methodName, types);
798                         if (isAbstractOrStatic(method)) { method = null; }
799                     } catch (Exception ex) {
800                         method = null;
801                     }
802                     setSequence.add(method);
803 
804                     javaClass = nextClass;
805                     fieldName = fieldName.substring(point + 1);
806                 }
807 
808                 // Find getter method for actual field
809                 String methodName = GET_METHOD_PREFIX + capitalize( fieldName );
810                 Class<?> returnType = (collectionType == null) ? fldType : collectionType;
811                 getMethod = findAccessor(javaClass, methodName, returnType, true);
812 
813                 // If getMethod is null, check for boolean type method prefix
814                 if (getMethod == null) {
815                     if ((fldType == Boolean.class) || (fldType == Boolean.TYPE)) {
816                         methodName = IS_METHOD_PREFIX + capitalize(fieldName);
817                         getMethod = findAccessor(javaClass, methodName, returnType, true);
818                     }
819                 }
820             } catch (MappingException ex) {
821                 throw ex;
822             } catch (Exception ex) {
823                 // LOG.warn("Unexpected exception", ex);
824             }
825 
826             if (getMethod == null) {
827                 String getAccessor = GET_METHOD_PREFIX + capitalize(fieldName);
828                 String isAccessor = IS_METHOD_PREFIX + capitalize(fieldName);
829                 throw new MappingException("mapping.accessorNotFound",
830                         getAccessor + "/" + isAccessor,
831                         (collectionType == null ? fldType : collectionType),
832                         javaClass.getName());
833             }
834 
835             if ((fldType == null) && (collectionType == null)) {
836                 fldType = getMethod.getReturnType();
837             }
838 
839             // We try to locate a set method anyway but complain only if we need one
840             String methodName = SET_METHOD_PREFIX + capitalize(fieldName);
841             setMethod = findAccessor(javaClass, methodName,
842                     (collectionType == null ? fldType : collectionType), false);
843 
844             // If we have a collection that need both set and get but we don't have a
845             // set method, we fail
846             if ((setMethod == null) && (collectionType != null) && colRequireGetSet) {
847                 throw new MappingException("mapping.accessorNotFound", methodName,
848                         (collectionType == null ? fldType : collectionType), javaClass.getName());
849             }
850 
851             typeInfoRef.typeInfo = getTypeInfo(fldType, collectionHandler, fldMap);
852 
853             fieldName = fldMap.getName();
854             if (fieldName == null) {
855                 if (getMethod == null) {
856                     fieldName = setMethod.getName();
857                 } else {
858                     fieldName = getMethod.getName();
859                 }
860             }
861 
862             // Convert method call sequence for nested fields to arrays
863             Method[] getArray = null;
864             Method[] setArray = null;
865             if (getSequence.size() > 0) {
866                 getArray = new Method[getSequence.size()];
867                 getArray = getSequence.toArray(getArray);
868                 setArray = new Method[setSequence.size()];
869                 setArray = setSequence.toArray(setArray);
870             }
871 
872             // Create handler
873             handler = new FieldHandlerImpl(fieldName, getArray, setArray,
874                     getMethod, setMethod, typeInfoRef.typeInfo);
875 
876             if (setMethod != null) {
877                 if (setMethod.getName().startsWith(ADD_METHOD_PREFIX)) {
878                     handler.setAddMethod(setMethod);
879                 }
880             }
881         } else {
882             Method getMethod = null;
883             Method setMethod = null;
884 
885             // First look up the get accessors
886             if (fldMap.getGetMethod() != null) {
887                 Class<?> rtype = fldType;
888                 if (collectionType != null) {
889                     String methodName = fldMap.getGetMethod();
890                     if (methodName.startsWith(ENUM_METHOD_PREFIX)) {
891                         // An enumeration method must really return a enumeration.
892                         rtype = Enumeration.class;
893                     } else if (methodName.startsWith(ITER_METHOD_PREFIX)) {
894                         // An iterator method must really return a iterator.
895                         rtype = Iterator.class;
896                     } else {
897                         rtype = collectionType;
898                     }
899                 }
900 
901                 getMethod = findAccessor(javaClass, fldMap.getGetMethod(), rtype, true);
902                 if (getMethod == null) {
903                     throw new MappingException("mapping.accessorNotFound",
904                             fldMap.getGetMethod(), rtype, javaClass.getName());
905                 }
906 
907                 if ((fldType == null) && (collectionType == null)) {
908                     fldType = getMethod.getReturnType();
909                 }
910             }
911 
912             // Second look up the set/add accessor
913             if (fldMap.getSetMethod() != null) {
914                 String methodName = fldMap.getSetMethod();
915                 Class<?> type = fldType;
916                 if (collectionType != null) {
917                     if (!methodName.startsWith(ADD_METHOD_PREFIX)) {
918                         type = collectionType;
919                     }
920                 }
921 
922                 // Set via constructor?
923                 if (methodName.startsWith("%")) {
924                     // Validate index value
925                     int index = 0;
926 
927                     String temp = methodName.substring(1);
928                     try {
929                         index = Integer.parseInt(temp);
930                     } catch(NumberFormatException ex) {
931                         throw new MappingException("mapping.invalidParameterIndex", temp);
932                     }
933 
934                     if ((index < 1) || (index > 9)) {
935                         throw new MappingException("mapping.invalidParameterIndex", temp);
936                     }
937                 } else {
938                     setMethod = findAccessor(javaClass, methodName, type , false);
939                     if (setMethod == null) {
940                         throw new MappingException("mapping.accessorNotFound",
941                                 methodName, type, javaClass.getName());
942                     }
943 
944                     if (fldType == null) {
945                         fldType = setMethod.getParameterTypes()[0];
946                     }
947                 }
948             }
949 
950             typeInfoRef.typeInfo = getTypeInfo(fldType, collectionHandler, fldMap);
951 
952             fieldName = fldMap.getName();
953             if (fieldName == null) {
954                 if (getMethod == null) {
955                     fieldName = setMethod.getName();
956                 } else {
957                     fieldName = getMethod.getName();
958                 }
959             }
960 
961             // Create handler
962             handler = new FieldHandlerImpl(fieldName, null, null,
963                     getMethod, setMethod, typeInfoRef.typeInfo);
964 
965             if (setMethod != null) {
966                 if (setMethod.getName().startsWith(ADD_METHOD_PREFIX)) {
967                     handler.setAddMethod(setMethod);
968                 }
969             }
970         }
971 
972         // If there is a create method, add it to the field handler
973         String methodName = fldMap.getCreateMethod();
974         if (methodName != null) {
975             try {
976                 Method method = javaClass.getMethod(methodName, (Class[]) null);
977                 handler.setCreateMethod(method);
978             } catch (Exception ex) {
979                 throw new MappingException("mapping.createMethodNotFound",
980                         methodName, javaClass.getName());
981             }
982         } else if ((fieldName != null) && !Types.isSimpleType(fldType)) {
983             try {
984                 methodName = CREATE_METHOD_PREFIX + capitalize(fieldName);
985                 Method method = javaClass.getMethod(methodName, (Class[]) null);
986                 handler.setCreateMethod(method);
987             } catch (Exception ex) {
988                 // LOG.warn ("Unexpected exception", ex);
989             }
990         }
991 
992         // If there is an has/delete method, add them to field handler
993         if (fieldName != null) {
994             try {
995                 methodName = fldMap.getHasMethod();
996                 if (methodName == null) {
997                     methodName = HAS_METHOD_PREFIX + capitalize(fieldName);
998                 }
999                 Method hasMethod = javaClass.getMethod(methodName, (Class[]) null);
1000 
1001                 if ((hasMethod.getModifiers() & Modifier.STATIC) != 0) {
1002                     hasMethod = null;
1003                 }
1004 
1005                 Method deleteMethod = null;
1006                 try {
1007                     methodName = DELETE_METHOD_PREFIX + capitalize(fieldName);
1008                     deleteMethod = javaClass.getMethod(methodName, (Class[]) null );
1009                     if ((deleteMethod.getModifiers() & Modifier.STATIC) != 0) {
1010                         deleteMethod = null;
1011                     }
1012                 } catch (Exception ex) {
1013                     // Purposely Ignore exception we're just seeing if the method exists
1014                 }
1015 
1016                 handler.setHasDeleteMethod(hasMethod, deleteMethod);
1017             } catch (Exception ex) {
1018                 // LOG.warn("Unexpected exception", ex);
1019             }
1020         }
1021 
1022         return handler;
1023     }
1024 
1025     private static boolean isAbstract(final Class<?> cls) {
1026         return ((cls.getModifiers() & Modifier.ABSTRACT) != 0);
1027     }
1028 
1029     private static boolean isAbstractOrStatic(final Method method) {
1030         return ((method.getModifiers() & Modifier.ABSTRACT) != 0)
1031             || ((method.getModifiers() & Modifier.STATIC) != 0);
1032     }
1033 
1034     protected TypeInfo getTypeInfo(final Class fieldType,
1035                                    final CollectionHandler colHandler,
1036                                    final FieldMapping fieldMap) throws MappingException {
1037         return new TypeInfo(Types.typeFromPrimitive(fieldType), null, null,
1038                             fieldMap.getRequired(), null, colHandler, false);
1039     }
1040 
1041     /**
1042      * Returns the named field. Uses reflection to return the named field and check the
1043      * field type, if specified.
1044      *
1045      * @param javaClass The class to which the field belongs.
1046      * @param fieldName The name of the field.
1047      * @param fieldType The type of the field if known, or null.
1048      * @return The field, null if not found.
1049      * @throws MappingException The field is not accessible or is not of the
1050      *         specified type.
1051      */
1052     private final Field findField(final Class<?> javaClass, final String fieldName,
1053                                   Class<?> fieldType) throws MappingException {
1054         try {
1055             // Look up the field based on its name, make sure it's only modifier
1056             // is public. If a type was specified, match the field type.
1057             Field field = javaClass.getField(fieldName);
1058             if ((field.getModifiers() != Modifier.PUBLIC)
1059                     && (field.getModifiers() != (Modifier.PUBLIC | Modifier.VOLATILE))) {
1060                 throw new MappingException(
1061                         "mapping.fieldNotAccessible", fieldName, javaClass.getName());
1062             }
1063 
1064             if (fieldType == null) {
1065                 fieldType = Types.typeFromPrimitive(field.getType());
1066             } else {
1067                 Class<?> ft1 = Types.typeFromPrimitive(fieldType);
1068                 Class<?> ft2 = Types.typeFromPrimitive(field.getType());
1069                 if ((ft1 != ft2) && (fieldType != Serializable.class)) {
1070                     throw new MappingException(
1071                             "mapping.fieldTypeMismatch", field, fieldType.getName());
1072                 }
1073             }
1074             return field;
1075         } catch (NoSuchFieldException ex) {
1076             return null;
1077         } catch (SecurityException ex) {
1078             return null;
1079         }
1080     }
1081 
1082     /**
1083      * Returns the named accessor. Uses reflection to return the named accessor and
1084      * check the return value or parameter type, if specified.
1085      *
1086      * @param javaClass The class to which the field belongs.
1087      * @param methodName The name of the accessor method.
1088      * @param fieldType The type of the field if known, or null.
1089      * @param getMethod True if get method, false if set method.
1090      * @return The method, null if not found.
1091      * @throws MappingException The method is not accessible or is not of the
1092      *         specified type.
1093      */
1094     public static final Method findAccessor(final Class<?> javaClass,
1095                                             final String methodName, Class<?> fieldType,
1096                                             final boolean getMethod) throws MappingException {
1097         try {
1098             Method method = null;
1099 
1100             if (getMethod) {
1101                 // Get method: look for the named method or prepend get to the method
1102                 // name. Look up the field and potentially check the return type.
1103                 method = javaClass.getMethod(methodName, new Class[0]);
1104 
1105                 // The MapItem is used to handle the contents of maps. Since the MapItem
1106                 // has to use Object for its methods we cannot (but also don't have to)
1107                 // check for correct types.
1108                 if (javaClass == MapItem.class) {
1109                     if (methodName.equals("getKey")) { return method; }
1110                     if (methodName.equals("getValue")) { return method; }
1111                 }
1112 
1113                 if (fieldType == null) {
1114                     fieldType = Types.typeFromPrimitive(method.getReturnType());
1115                 } else {
1116                     fieldType = Types.typeFromPrimitive(fieldType);
1117                     Class<?> returnType = Types.typeFromPrimitive(method.getReturnType());
1118 
1119                     //-- First check against whether the declared type is
1120                     //-- an interface or abstract class. We also check
1121                     //-- type as Serializable for CMP 1.1 compatibility.
1122                     if (fieldType.isInterface()
1123                             || ((fieldType.getModifiers() & Modifier.ABSTRACT) != 0)
1124                             || (fieldType == java.io.Serializable.class)) {
1125 
1126                         if (!fieldType.isAssignableFrom(returnType)) {
1127                             throw new MappingException(
1128                                     "mapping.accessorReturnTypeMismatch",
1129                                     method, fieldType.getName());
1130                         }
1131                     } else {
1132                         if (!returnType.isAssignableFrom(fieldType)) {
1133                             throw new MappingException(
1134                                     "mapping.accessorReturnTypeMismatch",
1135                                     method, fieldType.getName());
1136                         }
1137                     }
1138                 }
1139             } else {
1140                 // Set method: look for the named method or prepend set to the method
1141                 // name. If the field type is know, look up a suitable method. If the
1142                 // field type is unknown, lookup the first method with that name and
1143                 // one parameter.
1144                 Class<?> fieldTypePrimitive = null;
1145                 if (fieldType != null) {
1146                     fieldTypePrimitive = Types.typeFromPrimitive(fieldType);
1147                     try {
1148                         method = javaClass.getMethod(methodName, new Class[] {fieldType});
1149                     } catch (Exception ex) {
1150                         try {
1151                             method = javaClass.getMethod(
1152                                     methodName, new Class[] {fieldTypePrimitive});
1153                         } catch (Exception ex2) {
1154                             // LOG.warn("Unexpected exception", ex2);
1155                         }
1156                     }
1157 
1158                     /* Replace above try catch block with the following one to resolve
1159                      * CASTOR-1141 for jdo part. After this change you can use this method
1160                      * also in FieldMolder and remove its findAccessor() method to omit
1161                      * code duplication. Having said that this introduces a problem
1162                      * with xmlctf that have to resolved first.
1163                     // first check for setter with reference type (e.g. setXxx(Integer))
1164                     try {
1165                         method = javaClass.getMethod(methodName, new Class[] {fieldTypePrimitive});
1166                     } catch (Exception ex) {
1167                         // if setter for reference type could not be found
1168                         // try to find one for primitive type (e.g. setXxx(int))
1169                         try {
1170                             method = javaClass.getMethod(methodName, new Class[] {fieldType});
1171                         } catch (Exception ex2) {
1172                             // LOG.warn("Unexpected exception", ex2);
1173                         }
1174                     }
1175                     */
1176                 }
1177 
1178                 if (method == null) {
1179                     Method[] methods = javaClass.getMethods();
1180                     for (int i = 0; i < methods.length; ++i) {
1181                         if (methods[i].getName().equals(methodName)) {
1182                             Class<?>[] paramTypes = methods[i].getParameterTypes();
1183                             if (paramTypes.length != 1) { 
1184                                 continue; 
1185                             }
1186 
1187                             Class<?> paramType = Types.typeFromPrimitive(paramTypes[0]);
1188 
1189                             if (fieldType == null) {
1190                                 method = methods[i];
1191                                 break;
1192                             } else if (paramType.isAssignableFrom(fieldTypePrimitive)) {
1193                                 method = methods[i];
1194                                 break;
1195                             } else if (fieldType.isInterface() || isAbstract(fieldType)) {
1196                                 if (fieldTypePrimitive.isAssignableFrom(paramType)) {
1197                                     method = methods[i];
1198                                     break;
1199                                 }
1200                             }
1201                         }
1202                     }
1203 
1204                     if (method == null) { 
1205                         return null; 
1206                     }
1207                 }
1208             }
1209 
1210             // Make sure method is public and not static.
1211             // (note: Class.getMethod() returns only public methods).
1212             if ((method.getModifiers() & Modifier.STATIC) != 0) {
1213                 throw new MappingException(
1214                         "mapping.accessorNotAccessible", methodName, javaClass.getName());
1215             }
1216             return method;
1217         } catch (MappingException ex) {
1218             throw ex;
1219         } catch (Exception ex) {
1220             return null;
1221         }
1222     }
1223 
1224     private static final String capitalize(final String name) {
1225         char first = name.charAt(0);
1226         if (Character.isUpperCase(first)) { 
1227             return name; 
1228         }
1229         return Character.toUpperCase(first) + name.substring(1);
1230     }
1231 
1232     /**
1233      * Returns a list of column names that are part of the identity.
1234      *
1235      * @param ids Known identity names.
1236      * @param clsMap The class mapping.
1237      * @return List of identity column names.
1238      */
1239     public static final String[] getIdentityColumnNames(final String[] ids,
1240             final ClassMapping clsMap) {
1241 
1242         String[] idNames = ids;
1243 
1244         if ((ids == null) || (ids.length == 0)) {
1245             ClassChoice classChoice = clsMap.getClassChoice();
1246             if (classChoice == null) { 
1247                 classChoice = new ClassChoice(); 
1248             }
1249 
1250             FieldMapping[] fieldMappings = classChoice.getFieldMapping();
1251 
1252             List<String> idNamesList = new ArrayList<String>();
1253             for (FieldMapping fieldMapping : fieldMappings) {
1254                 if (fieldMapping.getIdentity() == true) {
1255                     idNamesList.add(fieldMapping.getName());
1256                 }
1257             }
1258 
1259             if (!idNamesList.isEmpty()) {
1260                 idNames = new String[idNamesList.size()];
1261                 idNames = idNamesList.toArray(idNames);
1262             }
1263         }
1264 
1265         return idNames;
1266     }
1267 
1268     /**
1269      * Returns true if the given class should be treated as a primitive
1270      * type
1271      * @return true if the given class should be treated as a primitive
1272      * type
1273      */
1274     protected static final boolean isPrimitive(final Class<?> type) {
1275         if (type.isPrimitive()) { return true; }
1276         if ((type == Boolean.class) || (type == Character.class)) { return true; }
1277         return (type.getSuperclass() == Number.class);
1278     }
1279 
1280     /**
1281      * A class used to by the createFieldHandler method in order to
1282      * save the reference of the TypeInfo that was used.
1283      */
1284     public class TypeInfoReference {
1285         public TypeInfo typeInfo = null;
1286     }
1287 
1288     public void setInternalContext(final InternalContext internalContext) {
1289         _internalContext = internalContext;
1290     }
1291     
1292     public InternalContext getInternalContext() {
1293         return _internalContext;
1294     }
1295 
1296 }