/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 1999-2004(C) Intalio, Inc. All Rights Reserved. * * This file was originally developed by Keith Visco during the * course of employment at Intalio Inc. * All portions of this file developed by Keith Visco after Jan 19 2005 are * Copyright (C) 2005 Keith Visco. All Rights Reserved. * * $Id$ */ package org.exolab.castor.xml; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.mapping.BindingType; import org.exolab.castor.mapping.AbstractFieldHandler; import org.exolab.castor.mapping.ClassDescriptor; import org.exolab.castor.mapping.CollectionHandler; import org.exolab.castor.mapping.FieldDescriptor; import org.exolab.castor.mapping.FieldHandler; import org.exolab.castor.mapping.MapItem; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.mapping.TypeConvertor; import org.exolab.castor.mapping.loader.FieldDescriptorImpl; import org.exolab.castor.mapping.loader.AbstractMappingLoader; import org.exolab.castor.mapping.loader.CollectionHandlers; import org.exolab.castor.mapping.loader.FieldHandlerImpl; import org.exolab.castor.mapping.loader.TypeInfo; import org.exolab.castor.mapping.loader.Types; import org.exolab.castor.mapping.xml.BindXml; import org.exolab.castor.mapping.xml.ClassMapping; import org.exolab.castor.mapping.xml.FieldMapping; import org.exolab.castor.mapping.xml.MapTo; import org.exolab.castor.mapping.xml.MappingRoot; import org.exolab.castor.mapping.xml.Property; import org.exolab.castor.mapping.xml.types.BindXmlAutoNamingType; import org.exolab.castor.mapping.xml.types.FieldMappingCollectionType; import org.exolab.castor.xml.handlers.ContainerFieldHandler; import org.exolab.castor.xml.handlers.ToStringFieldHandler; import org.exolab.castor.xml.util.ContainerElement; import org.exolab.castor.xml.util.XMLClassDescriptorAdapter; import org.exolab.castor.xml.util.XMLClassDescriptorImpl; import org.exolab.castor.xml.util.XMLContainerElementFieldDescriptor; import org.exolab.castor.xml.util.XMLFieldDescriptorImpl; import org.exolab.castor.xml.validators.IdRefValidator; import org.exolab.castor.xml.validators.NameValidator; /** * An XML implementation of mapping helper. Creates XML class * descriptors from the mapping file. * * @author <a href="keith AT kvisco DOT com">Keith Visco</a> * @author <a href="arkin@intalio.com">Assaf Arkin</a> * @version $Revision$ $Date: 2006-02-23 01:37:50 -0700 (Thu, 23 Feb 2006) $ */ public final class XMLMappingLoader extends AbstractMappingLoader { /** * {@link Log} instance to be used. */ private static final Log LOG = LogFactory.getLog(XMLMappingLoader.class); //----------------------------------------------------------------------------------- /** The default xml prefix used on certain attributes such as xml:lang, xml:base, * etc. */ private static final String XML_PREFIX = "xml:"; /** Empty array of class types used for reflection. */ private static final Class[] EMPTY_ARGS = new Class[0]; /** The NCName Schema type. */ private static final String NCNAME = "NCName"; /** The string argument for the valueOf method, used for introspection. */ private static final Class[] STRING_ARG = {String.class}; /** Factory method name for type-safe enumerations. This is primarily for allowing * users to map classes that were created by Castor's SourceGenerator. */ private static final String VALUE_OF = "valueOf"; /** * Creates a new XMLMappingLoader. * Joachim 2007-08-19: called via ClassLoader from XMLMappingLoaderFactory.getMappingLoader() * must not be modified!!! * @param loader the class loader to use */ public XMLMappingLoader(final ClassLoader loader) { super(loader); } /** * {@inheritDoc} */ public BindingType getBindingType() { return BindingType.XML; } /** * {@inheritDoc} */ public void loadMapping(final MappingRoot mapping, final Object param) throws MappingException { if (loadMapping()) { createFieldHandlers(mapping); createClassDescriptors(mapping); } } //----------------------------------------------------------------------------------- /** * To create the class descriptor for the given class mapping. * Throws IllegalStateException if the class has no valid internal context. * @param classMapping the class mapping information to process * @return the {@link ClassDescriptor} created for the class mapping * @throws MappingException ... */ protected ClassDescriptor createClassDescriptor(final ClassMapping classMapping) throws MappingException { // execution makes no sense without a context or without a resolver... if ((getInternalContext() == null) || (getInternalContext().getXMLClassDescriptorResolver() == null)) { String message = "Internal context or class descriptor resolver within are not valid"; LOG.warn(message); throw new IllegalStateException(message); } // Create the class descriptor. XMLClassDescriptorAdapter xmlClassDesc = new XMLClassDescriptorAdapter(); // Introspection and package level stuff needs to be disabled !! getInternalContext().getXMLClassDescriptorResolver().setUseIntrospection(false); getInternalContext().getXMLClassDescriptorResolver().setLoadPackageMappings(false); try { if (classMapping.getAutoComplete()) { if ((classMapping.getMapTo() == null) && ((classMapping.getClassChoice() == null) || (classMapping.getClassChoice().getFieldMappingCount() == 0)) && (classMapping.getIdentityCount() == 0)) { // If we make it here we simply try to load a compiled mapping try { ClassDescriptor clsDesc = getInternalContext() .getXMLClassDescriptorResolver().resolve(classMapping.getName()); if (clsDesc != null) { return clsDesc; } } catch (ResolverException e) { if (LOG.isDebugEnabled()) { String message = new StringBuffer().append("Ignoring exception: ").append(e) .append(" at resolving: ").append(classMapping.getName()).toString(); LOG.debug(message); } } } } // Obtain the Java class. Class javaClass = resolveType(classMapping.getName()); if (classMapping.getVerifyConstructable()) { if (!Types.isConstructable(javaClass, true)) { throw new MappingException( "mapping.classNotConstructable", javaClass.getName()); } } xmlClassDesc.setJavaClass(javaClass); // Obtain XML name. String xmlName; MapTo mapTo = classMapping.getMapTo(); if ((mapTo != null) && (mapTo.getXml() != null)) { xmlName = mapTo.getXml(); } else { String clsName = getInternalContext().getJavaNaming().getClassName(javaClass); xmlName = getInternalContext().getXMLNaming().toXMLName(clsName); } xmlClassDesc.setXMLName(xmlName); // If this class extends another class, we need to obtain the extended // class and make sure this class indeed extends it. ClassDescriptor extDesc = getExtended(classMapping, javaClass); xmlClassDesc.setExtends((XMLClassDescriptor) extDesc); // Create all field descriptors. FieldDescriptorImpl[] allFields = createFieldDescriptors(classMapping, javaClass); // Make sure there are no two fields with the same name. checkFieldNameDuplicates(allFields, javaClass); // Identify identity and normal fields. Note that order must be preserved. List fieldList = new ArrayList(allFields.length); List idList = new ArrayList(); if (extDesc == null) { // Sort fields into 2 lists based on identity definition of field. for (int i = 0; i < allFields.length; i++) { if (!allFields[i].isIdentity()) { fieldList.add(allFields[i]); } else { idList.add(allFields[i]); } } if (idList.size() == 0) { // Found no identities based on identity definition of field. // Try to find identities based on identity definition on class. String[] idNames = classMapping.getIdentity(); FieldDescriptor identity; for (int i = 0; i < idNames.length; i++) { identity = findIdentityByName(fieldList, idNames[i], javaClass); if (identity != null) { idList.add(identity); } else { throw new MappingException("mapping.identityMissing", idNames[i], javaClass.getName()); } } } } else { // Add all fields of extending class to field list. for (int i = 0; i < allFields.length; i++) { fieldList.add(allFields[i]); } // Add identity of extended class to identity list. if (extDesc.getIdentity() != null) { idList.add(extDesc.getIdentity()); } // Search redefined identities in extending class. FieldDescriptor identity; for (int i = 0; i < idList.size(); i++) { String idname = ((FieldDescriptor) idList.get(i)).getFieldName(); identity = findIdentityByName(fieldList, idname, javaClass); if (identity != null) { idList.set(i, identity); } } } FieldDescriptor xmlId = null; if (idList.size() != 0) { xmlId = (FieldDescriptor) idList.get(0); } if (xmlId != null) { xmlClassDesc.setIdentity((XMLFieldDescriptorImpl) xmlId); } for (int i = 0; i < fieldList.size(); i++) { FieldDescriptor fieldDesc = (FieldDescriptor) fieldList.get(i); if (fieldDesc != null) { xmlClassDesc.addFieldDescriptor((XMLFieldDescriptorImpl) fieldDesc); } } if (classMapping.getAutoComplete()) { XMLClassDescriptor referenceDesc = null; Class type = xmlClassDesc.getJavaClass(); //-- check compiled descriptors if ((getInternalContext() == null) || (getInternalContext().getXMLClassDescriptorResolver() == null)) { String message = "Internal context or class descriptor resolver within are not valid"; LOG.warn(message); throw new IllegalStateException(message); } try { referenceDesc = (XMLClassDescriptor) getInternalContext().getXMLClassDescriptorResolver().resolve(type); } catch (ResolverException rx) { throw new MappingException(rx); } if (referenceDesc == null) { Introspector introspector = getInternalContext().getIntrospector(); try { referenceDesc = introspector.generateClassDescriptor(type); if (classMapping.getExtends() != null) { //-- clear parent from introspected descriptor since //-- a mapping was provided in the mapping file ((XMLClassDescriptorImpl) referenceDesc).setExtends(null); } } catch (MarshalException mx) { String error = "unable to introspect class '" + type.getName() + "' for auto-complete: "; throw new MappingException(error + mx.getMessage()); } } //-- check for identity String identity = ""; if (classMapping.getIdentityCount() > 0) { identity = classMapping.getIdentity(0); } FieldDescriptor[] xmlFields2 = xmlClassDesc.getFields(); // Attributes XMLFieldDescriptor[] introFields = referenceDesc.getAttributeDescriptors(); for (int i = 0; i < introFields.length; ++i) { if (!isMatchFieldName(xmlFields2, introFields[i].getFieldName())) { // If there is no field with this name, we can add it if (introFields[i].getFieldName().equals(identity)) { xmlClassDesc.setIdentity(introFields[i]); } else { xmlClassDesc.addFieldDescriptor(introFields[i]); } } } // Elements introFields = referenceDesc.getElementDescriptors(); for (int i = 0; i < introFields.length; ++i) { if (!isMatchFieldName(xmlFields2, introFields[i].getFieldName())) { // If there is no field with this name, we can add it if (introFields[i].getFieldName().equals(identity)) { xmlClassDesc.setIdentity(introFields[i]); } else { xmlClassDesc.addFieldDescriptor(introFields[i]); } } } // Content XMLFieldDescriptor field = referenceDesc.getContentDescriptor(); if (field != null) { if (!isMatchFieldName(xmlFields2, field.getFieldName())) { // If there is no field with this name, we can add xmlClassDesc.addFieldDescriptor(field); } } } // Copy ns-uri + ns-prefix + element-definition if (mapTo != null) { xmlClassDesc.setNameSpacePrefix(mapTo.getNsPrefix()); xmlClassDesc.setNameSpaceURI(mapTo.getNsUri()); xmlClassDesc.setElementDefinition(mapTo.getElementDefinition()); } } finally { getInternalContext().getXMLClassDescriptorResolver().setUseIntrospection(true); getInternalContext().getXMLClassDescriptorResolver().setLoadPackageMappings(true); } return xmlClassDesc; } protected final FieldDescriptor findIdentityByName( final List fldList, final String idName, final Class javaClass) { for (int i = 0; i < fldList.size(); i++) { FieldDescriptor field = (FieldDescriptor) fldList.get(i); if (idName.equals(field.getFieldName())) { fldList.remove(i); return field; } } return null; } protected final void resolveRelations(ClassDescriptor clsDesc) { FieldDescriptor[] fields; fields = clsDesc.getFields(); for (int i = 0 ; i < fields.length ; ++i ) { if (fields[i].getClassDescriptor() != null) continue; ClassDescriptor relDesc; Class fieldType = fields[i].getFieldType(); if (fieldType != null) { relDesc = getDescriptor(fieldType.getName()); if (relDesc != null && relDesc instanceof XMLClassDescriptor && fields[ i ] instanceof XMLFieldDescriptorImpl) { ((XMLFieldDescriptorImpl) fields[i]).setClassDescriptor(relDesc); } } } if ( clsDesc instanceof XMLClassDescriptorImpl ) ( (XMLClassDescriptorImpl) clsDesc ).sortDescriptors(); } //----------------------------------------------------------------------------------- /** * Match if a field named <code>fieldName</code> is in fields */ private boolean isMatchFieldName(FieldDescriptor[] fields, String fieldName) { for (int i=0; i< fields.length; ++i) if (fields[i].getFieldName().equals(fieldName)) return true; return false; } //-- method: isMatchFieldName protected FieldDescriptorImpl createFieldDesc( Class javaClass, FieldMapping fieldMap ) throws MappingException { FieldDescriptor fieldDesc; FieldMappingCollectionType colType = fieldMap.getCollection(); String xmlName = null; NodeType nodeType = null; String match = null; XMLFieldDescriptorImpl xmlDesc; boolean isReference = false; boolean isXMLTransient = false; //-- handle special case for HashMap/Hashtable if ((fieldMap.getType() == null) && (colType != null)) { if ((colType == FieldMappingCollectionType.HASHTABLE) || (colType == FieldMappingCollectionType.MAP) || (colType == FieldMappingCollectionType.SORTEDMAP)) { fieldMap.setType(MapItem.class.getName()); } } // Create an XML field descriptor fieldDesc = super.createFieldDesc( javaClass, fieldMap ); BindXml xml = fieldMap.getBindXml(); boolean deriveNameByClass = false; if (xml != null) { //-- xml name xmlName = xml.getName(); //-- node type if ( xml.getNode() != null ) nodeType = NodeType.getNodeType( xml.getNode().toString() ); //-- matches match = xml.getMatches(); //-- reference isReference = xml.getReference(); //-- XML transient isXMLTransient = xml.getTransient(); //-- autonaming BindXmlAutoNamingType autoName = xml.getAutoNaming(); if (autoName != null) { deriveNameByClass = (autoName == BindXmlAutoNamingType.DERIVEBYCLASS); } } //-- transient //-- XXXX -> if it's transient we probably shouldn't do all //-- XXXX -> the unecessary work isXMLTransient = isXMLTransient || fieldDesc.isTransient(); //-- //-- handle QName for xmlName String namespace = null; if ((xmlName != null) && (xmlName.length() > 0)){ if (xmlName.charAt(0) == '{') { int idx = xmlName.indexOf('}'); if (idx < 0) { throw new MappingException("Invalid QName: " + xmlName); } namespace = xmlName.substring(1, idx); xmlName = xmlName.substring(idx+1); } else if (xmlName.startsWith(XML_PREFIX)) { namespace = Namespaces.XML_NAMESPACE; xmlName = xmlName.substring(4); } } if (nodeType == null) { if (isPrimitive(javaClass)) nodeType = getInternalContext().getPrimitiveNodeType(); else nodeType = NodeType.Element; } //-- Create XML name if necessary. Note if name is to be derived //-- by class..we just make sure we set the name to null... //-- the Marshaller does this during runtime. This allows //-- Collections to be handled properly. if ((!deriveNameByClass) && ((xmlName == null) && (match == null))) { xmlName = getInternalContext().getXMLNaming().toXMLName( fieldDesc.getFieldName() ); match = xmlName + ' ' + fieldDesc.getFieldName(); } xmlDesc = new XMLFieldDescriptorImpl( fieldDesc, xmlName, nodeType, getInternalContext().getPrimitiveNodeType() ); if (xmlDesc.getHandler() != null && xmlDesc.getHandler() instanceof AbstractFieldHandler) { AbstractFieldHandler handler = (AbstractFieldHandler) xmlDesc.getHandler(); handler.setFieldDescriptor(xmlDesc); } //-- transient? xmlDesc.setTransient(isXMLTransient); //--set a default fieldValidator xmlDesc.setValidator(new FieldValidator()); //-- enable use parent namespace if explicit one doesn't exist xmlDesc.setUseParentsNamespace(true); //-- If deriveNameByClass we need to reset the name to //-- null because XMLFieldDescriptorImpl tries to be smart //-- and automatically creates the name. if (deriveNameByClass) { xmlDesc.setXMLName(null); } //-- namespace if (namespace != null) { xmlDesc.setNameSpaceURI(namespace); } //-- matches if (match != null) { xmlDesc.setMatches(match); //-- special fix for xml-name since XMLFieldDescriptorImpl //-- will create a default name based off the field name if (xmlName == null) xmlDesc.setXMLName(null); } //-- reference xmlDesc.setReference(isReference); if (isReference) { if (colType == null) { FieldValidator fieldValidator = new FieldValidator(); fieldValidator.setValidator(new IdRefValidator()); xmlDesc.setValidator(fieldValidator); } else { // TODO handle other cases } } xmlDesc.setContainer(fieldMap.getContainer()); xmlDesc.setNillable(fieldMap.isNillable()); if (xml != null) { //-- has class descriptor for type specified if (xml.getClassMapping() != null) { ClassDescriptor cd = createClassDescriptor(xml.getClassMapping()); xmlDesc.setClassDescriptor(cd); } //-- has location path? if (xml.getLocation() != null) { xmlDesc.setLocationPath(xml.getLocation()); } //is the value type needs specific handling //such as QName or NCName support? String xmlType = xml.getType(); xmlDesc.setSchemaType(xmlType); xmlDesc.setQNamePrefix(xml.getQNamePrefix()); TypeValidator validator = null; if (NCNAME.equals(xmlType)) { validator = new NameValidator(XMLConstants.NAME_TYPE_NCNAME); xmlDesc.setValidator(new FieldValidator(validator)); } //-- special properties? Property[] props = xml.getProperty(); if ((props != null) && (props.length > 0)) { for (int pIdx = 0; pIdx < props.length; pIdx++) { Property prop = props[pIdx]; xmlDesc.setXMLProperty(prop.getName(), prop.getValue()); } } } //-- Get collection type if (colType == null) { //-- just in case user forgot to use collection="..." //-- in the mapping file Class type = fieldDesc.getFieldType(); if (type != null && CollectionHandlers.hasHandler(type)) { String typeName = CollectionHandlers.getCollectionName(type); colType = FieldMappingCollectionType.valueOf(typeName); } } //-- isMapped item if (colType != null) { if ((colType == FieldMappingCollectionType.HASHTABLE) || (colType == FieldMappingCollectionType.MAP) || (colType == FieldMappingCollectionType.SORTEDMAP)) { //-- Make sure user is not using an addMethod //-- before setting the mapped field to true. String methodName = fieldMap.getSetMethod(); if (methodName != null) { if (!methodName.startsWith("add")) { xmlDesc.setMapped(true); } } else xmlDesc.setMapped(true); } //-- special NodeType.Namespace handling //-- prevent FieldHandlerImpl from using CollectionHandler //-- during calls to #getValue if ((nodeType == NodeType.Namespace) || (xmlDesc.isMapped())) { Object handler = xmlDesc.getHandler(); if (handler instanceof FieldHandlerImpl) { FieldHandlerImpl handlerImpl = (FieldHandlerImpl)handler; handlerImpl.setConvertFrom(new IdentityConvertor()); } } //-- wrap collection in element? if (nodeType == NodeType.Element) { if (fieldMap.hasContainer() && (!fieldMap.getContainer())) { xmlDesc = wrapCollection(xmlDesc); } } } //-- is Type-Safe Enumeration? //-- This is not very clean, we should have a way //-- to specify something is a type-safe enumeration //-- without having to guess. else if ((!isReference) && (!isXMLTransient)) { Class fieldType = xmlDesc.getFieldType(); if (!isPrimitive(fieldType)) { //-- make sure no default constructor Constructor cons = null; try { cons = fieldType.getConstructor(EMPTY_ARGS); if (!Modifier.isPublic(cons.getModifiers())) { cons = null; } } catch(NoSuchMethodException nsmx) { //-- Do nothing } try { if (cons == null) { //-- make sure a valueOf factory method //-- exists and no user specified handler exists Method method = fieldType.getMethod(VALUE_OF, STRING_ARG); Class returnType = method.getReturnType(); if ((returnType != null) && fieldType.isAssignableFrom(returnType)) { if (fieldMap.getHandler() == null) { //-- Use EnumFieldHandler //-- mapping loader now supports a basic EnumFieldHandler //-- for xml we simply need to make sure the toString() //-- method is called during getValue() //FieldHandler handler = xmlDesc.getHandler(); //handler = new EnumFieldHandler(fieldType, handler); FieldHandler handler = new ToStringFieldHandler(fieldType, xmlDesc.getHandler()); xmlDesc.setHandler(handler); xmlDesc.setImmutable(true); } } } } catch (NoSuchMethodException nsmx) { //-- Do nothing } } } //-- constructor argument? String setter = fieldMap.getSetMethod(); if (setter != null && setter.startsWith("%")) { String parameterNumberAsString = setter.substring(1).trim(); int index = Integer.parseInt(parameterNumberAsString); if (index < 1) { throw new MappingException("mapper.invalidParameterIndex", parameterNumberAsString); } //-- adjust index to base zero xmlDesc.setConstructorArgumentIndex(--index); } return xmlDesc; } /** * Sets whether or not to look for and load package specific * mapping files (".castor.xml" files). * * @param loadPackageMappings a boolean that enables or * disables the loading of package specific mapping files */ public void setLoadPackageMappings(final boolean loadPackageMappings) { if ((getInternalContext() == null) || (getInternalContext().getXMLClassDescriptorResolver() == null)) { String message = "Internal context or class descriptor resolver within are not valid"; LOG.warn(message); throw new IllegalStateException(message); } getInternalContext() .getXMLClassDescriptorResolver() .setLoadPackageMappings(loadPackageMappings); } //-- setLoadPackageMappings protected TypeInfo getTypeInfo( Class fieldType, CollectionHandler colHandler, FieldMapping fieldMap ) throws MappingException { return new TypeInfo(fieldType, null, null, fieldMap.getRequired(), null, colHandler, false); } /** * This method allows a collection to be treated as a first class * object (and not a container) so that it has an element representation * in the marshalled XML. */ private XMLFieldDescriptorImpl wrapCollection (XMLFieldDescriptorImpl fieldDesc) throws MappingException { //-- If we have a field 'c' that is a collection and //-- we want to wrap that field in an element <e>, we //-- need to create a field descriptor for //-- an object that represents the element <e> and //-- acts as a go-between from the parent of 'c' //-- denoted as P(c) and 'c' itself // // object model: P(c) -> c // xml : <p><e><c></e><p> //-- Make new class descriptor for the field that //-- will represent the container element <e> Class type = ContainerElement.class; XMLClassDescriptorImpl classDesc = new XMLClassDescriptorImpl(type); //-- make copy of fieldDesc and add it to our new class descriptor XMLFieldDescriptorImpl newFieldDesc = new XMLFieldDescriptorImpl(fieldDesc, fieldDesc.getXMLName(), fieldDesc.getNodeType(), getInternalContext().getPrimitiveNodeType()); //-- nullify xmlName so that auto-naming will be enabled, //-- we can't do this in the constructor because //-- XMLFieldDescriptorImpl will create a default one. newFieldDesc.setXMLName(null); newFieldDesc.setMatches("*"); //-- add the field descriptor to our new class descriptor classDesc.addFieldDescriptor(newFieldDesc); //-- reassociate the orignal class descriptor (for 'c') // of fieldDesc with our new classDesc fieldDesc.setClassDescriptor(classDesc); //-- wrap the field handler in a special container field //-- handler that will actually do the delgation work FieldHandler handler = new ContainerFieldHandler(fieldDesc.getHandler()); newFieldDesc.setHandler(handler); fieldDesc.setHandler(handler); //-- Change fieldType of original field descriptor and //-- return new descriptor return new XMLContainerElementFieldDescriptor(fieldDesc, getInternalContext().getPrimitiveNodeType()); } //-- createWrapperDescriptor /** * A special TypeConvertor that simply returns the object * given. This is used for preventing the FieldHandlerImpl * from using a CollectionHandler when getValue is called. **/ class IdentityConvertor implements TypeConvertor { public Object convert(final Object object) { return object; } } //-- class: IdentityConvertor } //-- class: XMLMappingLoader