Package org.exolab.castor.xml

Source Code of org.exolab.castor.xml.XMLMappingLoader$IdentityConvertor

/**
* 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.
*
* $Id: XMLMappingLoader.java,v 1.12 2004/03/08 10:39:21 kvisco Exp $
*/


package org.exolab.castor.xml;


import org.exolab.castor.mapping.ClassDescriptor;
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.CollectionHandler;
import org.exolab.castor.mapping.loader.CollectionHandlers;
import org.exolab.castor.mapping.loader.MappingLoader;
import org.exolab.castor.mapping.loader.Types;
import org.exolab.castor.mapping.loader.FieldDescriptorImpl;
import org.exolab.castor.mapping.loader.FieldHandlerImpl;
import org.exolab.castor.mapping.loader.TypeInfo;
import org.exolab.castor.mapping.xml.ClassMapping;
import org.exolab.castor.mapping.xml.FieldMapping;
import org.exolab.castor.mapping.xml.BindXml;
import org.exolab.castor.mapping.xml.MapTo;
import org.exolab.castor.mapping.xml.Property;
import org.exolab.castor.mapping.xml.types.BindXmlAutoNamingType;
import org.exolab.castor.mapping.xml.types.CollectionType;
import org.exolab.castor.util.Configuration;
import org.exolab.castor.util.LocalConfiguration;

import org.exolab.castor.xml.handlers.ContainerFieldHandler;
import org.exolab.castor.xml.handlers.EnumFieldHandler;
import org.exolab.castor.xml.util.ClassDescriptorResolverImpl;
import org.exolab.castor.xml.util.ContainerElement;
import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
import org.exolab.castor.xml.util.XMLClassDescriptorAdapter;
import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;
import org.exolab.castor.xml.validators.NameValidator;

import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Vector;
import java.util.Enumeration;

/**
* An XML implementation of mapping helper. Creates XML class
* descriptors from the mapping file.
*
* @author <a href="kvisco@intalio.com">Keith Visco</a>
* @author <a href="arkin@intalio.com">Assaf Arkin</a>
* @version $Revision: 1.12 $ $Date: 2004/03/08 10:39:21 $
*/
public class XMLMappingLoader
    extends MappingLoader
{


    /**
     * 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";


    /**
     * naming conventions
    **/
    private XMLNaming _naming = null;

    /**
     * Introspector
    **/
    private NodeType _primitiveNodeType = null;


    /**
     * Creates a new XMLMappingLoader
     */
    public XMLMappingLoader( ClassLoader loader, PrintWriter logWriter )
        throws MappingException
    {
        super( loader, logWriter );
       
        LocalConfiguration config = LocalConfiguration.getInstance();
       
        _primitiveNodeType = config.getPrimitiveNodeType();
        _naming            = config.getXMLNaming();

    } //-- XMLMappingLoader


    protected void resolveRelations( ClassDescriptor clsDesc )
        throws MappingException
    {
        FieldDescriptor[] fields;

        fields = clsDesc.getFields();
        for ( int i = 0 ; i < fields.length ; ++i ) {
            if (fields[i].getClassDescriptor() != null) continue;
            ClassDescriptor   relDesc;
           
            relDesc = getDescriptor( fields[ i ].getFieldType() );
            if ( relDesc == NoDescriptor ) {
                // XXX Error message should come here
            }
            else if ( relDesc != null &&
                      relDesc instanceof XMLClassDescriptor &&
                      fields[ i ] instanceof XMLFieldDescriptorImpl )
            {
            ( (XMLFieldDescriptorImpl) fields[ i ] ).setClassDescriptor( (XMLClassDescriptor) relDesc );

            //-- removed kvisco 20010814
                // ( (XMLFieldDescriptorImpl) fields[ i ] ).setNodeType( NodeType.Element );
            }
        }
        if ( clsDesc instanceof XMLClassDescriptorImpl )
            ( (XMLClassDescriptorImpl) clsDesc ).sortDescriptors();
    }


    protected ClassDescriptor createDescriptor( ClassMapping clsMap )
        throws MappingException
    {
        ClassDescriptor clsDesc;
        String          xmlName;

        // See if we have a compiled descriptor.
        clsDesc = loadClassDescriptor( clsMap.getName() );
        if ( clsDesc != null && clsDesc instanceof XMLClassDescriptor )
            return clsDesc;

        // Use super class to create class descriptor. Field descriptors will be
        // generated only for supported fields, see createFieldDesc later on.
        clsDesc = super.createDescriptor( clsMap );
        MapTo mapTo = clsMap.getMapTo();
        if (( mapTo == null) || (mapTo.getXml() == null)) {
            String clsName = clsDesc.getJavaClass().getName();
            int idx = clsName.lastIndexOf('.');
            if (idx >= 0) {
                clsName = clsName.substring(idx+1);
            }
            xmlName = _naming.toXMLName( clsName );
        }
        else {
            xmlName = clsMap.getMapTo().getXml();

        }

        XMLClassDescriptorImpl xmlClassDesc
            = new XMLClassDescriptorAdapter( clsDesc, xmlName, _primitiveNodeType );

        if (clsMap.getAutoComplete()) {

            XMLClassDescriptor referenceDesc = null;
           
            Class type = xmlClassDesc.getJavaClass();
           
            //-- check compiled descriptors
            ClassDescriptorResolverImpl cdr
                = new ClassDescriptorResolverImpl();
            cdr.setIntrospection(false);
            referenceDesc = cdr.resolve(type);
            if (referenceDesc == null) {
                Introspector introspector = new Introspector();
                try {
                    referenceDesc = introspector.generateClassDescriptor(type);
                    if (clsMap.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 (clsMap.getIdentityCount() > 0)
                identity = clsMap.getIdentity(0);

           
            FieldDescriptor[] fields = xmlClassDesc.getFields();

            // Attributes
            XMLFieldDescriptor[] introFields = referenceDesc.getAttributeDescriptors();
            for (int i = 0; i<introFields.length; ++i) {
                if (!isMatchFieldName(fields, 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(fields, 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(fields, field.getFieldName()))
                    // If there is no field with this name, we can add
                    xmlClassDesc.addFieldDescriptor(field);


        } //-- End of auto-complete


        //-- copy ns-uri + ns-prefix
        if (mapTo != null) {
            xmlClassDesc.setNameSpacePrefix(mapTo.getNsPrefix());
            xmlClassDesc.setNameSpaceURI(mapTo.getNsUri());
        }
        return xmlClassDesc;
    } //-- createDescriptor


    /**
     * 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 FieldDescriptor createFieldDesc( Class javaClass, FieldMapping fieldMap )
        throws MappingException
    {
       
        FieldDescriptor        fieldDesc;
        CollectionType         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 == CollectionType.HASHTABLE) ||
                (colType == CollectionType.MAP))
            {
                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 = _primitiveNodeType;
            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 = _naming.toXMLName( fieldDesc.getFieldName() );
            match = xmlName + ' ' + fieldDesc.getFieldName();
        }

        xmlDesc = new XMLFieldDescriptorImpl( fieldDesc, xmlName, nodeType, _primitiveNodeType );

        //-- 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);

        xmlDesc.setContainer(fieldMap.getContainer());

        if (xml != null) {
           
            //-- has class descriptor for type specified
            if (xml.getClassMapping() != null) {
                ClassDescriptor cd = createDescriptor(xml.getClassMapping());
                xmlDesc.setClassDescriptor((XMLClassDescriptor)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(NameValidator.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.setProperty(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 (CollectionHandlers.hasHandler(type)) {
                String typeName = CollectionHandlers.getCollectionName(type);
                colType = CollectionType.valueOf(typeName);
            }
        }
       
        //-- isMapped item
        if (colType != null) {   
            if ((colType == CollectionType.HASHTABLE) ||
                (colType == CollectionType.MAP))
            {
                //-- 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
                                FieldHandler handler = xmlDesc.getHandler();
                                handler = new EnumFieldHandler(fieldType, handler);
                                xmlDesc.setHandler(handler);
                                xmlDesc.setImmutable(true);
                            }
                        }
                    }
                }
                catch(NoSuchMethodException nsmx) {
                    //-- Do nothing
                }
            }
        }
       
        //-- constructor argument?
        String setter = fieldMap.getSetMethod();
        if (setter != null) {
            if (setter.startsWith("%")) {
                int index = 0;
                setter = setter.substring(1);
                index = Integer.parseInt(setter);
                if ((index < 1) || (index > 9)) {
                    throw new MappingException("mapper.invalidParameterIndex", setter);
                }
                //-- adjust index to base zero
                --index;
                xmlDesc.setConstructorArgumentIndex(index);
            }
        }

        return xmlDesc;
    }


    protected TypeInfo getTypeInfo( Class fieldType, CollectionHandler colHandler, FieldMapping fieldMap )
        throws MappingException
    {
        return new TypeInfo( fieldType, null, 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(),
                                         _primitiveNodeType);
        //-- 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 ContainerElementFieldDescriptor(fieldDesc, _primitiveNodeType);
    } //-- createWrapperDescriptor
    
    /**
     * Returns true if the given class should be treated as a primitive
     * type
     * @return true if the given class should be treated as a primitive
     * type
    **/
    private static boolean isPrimitive(Class type) {

        if (type.isPrimitive()) return true;

        if ((type == Boolean.class) || (type == Character.class))
            return true;

        return (type.getSuperclass() == Number.class);

    } //-- isPrimitive

    /**
     * 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( Object object, String param )
            throws ClassCastException
        {
            return object;
        }
    } //-- class: IdentityConvertor

    /**
     * An extended XMLFieldDescriptor that allows us to change
     * the fieldType, needed for container element support
     */
    class ContainerElementFieldDescriptor extends XMLFieldDescriptorImpl {
       
        ContainerElementFieldDescriptor(XMLFieldDescriptorImpl fieldDesc, NodeType primitiveNodeType)
            throws MappingException
        {
            super(fieldDesc, fieldDesc.getXMLName(), fieldDesc.getNodeType(),
                primitiveNodeType);
        }
       
        public Class getFieldType() {
            return org.exolab.castor.xml.util.ContainerElement.class;
        }
    } //-- class: ContainerElementFieldDescriptor

   
} //-- class: XMLMappingLoader



TOP

Related Classes of org.exolab.castor.xml.XMLMappingLoader$IdentityConvertor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.