/* * 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 (C) 2005 Keith Visco. All Rights Reserverd. * * $Id$ */ package org.exolab.castor.builder.factory; import org.exolab.castor.builder.BuilderConfiguration; import org.exolab.castor.builder.info.ClassInfo; import org.exolab.castor.builder.info.CollectionInfo; import org.exolab.castor.builder.info.FieldInfo; import org.exolab.castor.builder.info.NodeType; import org.exolab.castor.builder.info.nature.XMLInfoNature; import org.exolab.castor.builder.types.XSType; import org.exolab.castor.mapping.xml.BindXml; import org.exolab.castor.mapping.xml.ClassChoice; 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.types.BindXmlNodeType; import org.exolab.javasource.JClass; import org.exolab.javasource.JType; /** * A factory for creating mapping files. * * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a> * @version $Revision$ $Date: 2006-04-13 07:37:49 -0600 (Thu, 13 Apr 2006) $ */ public final class MappingFileSourceFactory { /** * Creates a new MappingFileSourceFactory with the given configuration. * * @param config the BuilderConfiguration instance */ public MappingFileSourceFactory(final BuilderConfiguration config) { if (config == null) { String err = "The argument 'config' must not be null."; throw new IllegalArgumentException(err); } } //-- MappingFileSourceFactory /** * Creates the class mapping for the given ClassInfo. * * @param classInfo the XML Schema element declaration * @return the ClassMapping representing the ClassInfo */ public ClassMapping createMapping(final ClassInfo classInfo) { JClass jClass = classInfo.getJClass(); String className = jClass.getName(); ClassMapping classMapping = new ClassMapping(); classMapping.setName(className); //-- Set namespace prefix MapTo mapTo = new MapTo(); classMapping.setMapTo(mapTo); XMLInfoNature xmlNature = new XMLInfoNature(classInfo); String nsPrefix = xmlNature.getNamespacePrefix(); if ((nsPrefix != null) && (nsPrefix.length() > 0)) { mapTo.setNsPrefix(nsPrefix); } //-- Set namespace URI String nsURI = xmlNature.getNamespaceURI(); if ((nsURI != null) && (nsURI.length() > 0)) { mapTo.setNsUri(nsURI); } //-- set XML Name mapTo.setXml(xmlNature.getNodeName()); //-- set Element Definition flag mapTo.setElementDefinition(xmlNature.isElementDefinition()); //-- set grouping compositor if (xmlNature.isChoice()) { // TODO need a way to specify choice in Mapping file } boolean isAbstract = classInfo.isAbstract(); if (!isAbstract) { isAbstract = jClass.getModifiers().isAbstract(); } classInfo.setAbstract(isAbstract); //-- To prevent compiler warnings...make sure //-- we don't declare temp variables if field count is 0; if (classInfo.getFieldCount() == 0) { return classMapping; } //-- handle content if (classInfo.allowContent()) { createFieldMapping(classMapping, classInfo.getTextField(), null); } ClassInfo base = classInfo.getBaseClass(); if (base != null) { classMapping.setExtends(base.getJClass().getName()); } FieldInfo[] atts = classInfo.getAttributeFields(); //-----------------------------/ //- Create attribute mappings -/ //-----------------------------/ for (int i = 0; i < atts.length; i++) { FieldInfo member = atts[i]; //-- skip transient members if (member.isTransient()) { continue; } //-- skip inherited fields if (base != null && base.getAttributeField(xmlNature.getNodeName()) != null) { continue; } createFieldMapping(classMapping, member, nsURI); } //---------------------------/ //- Create element mappings -/ //---------------------------/ FieldInfo[] elements = classInfo.getElementFields(); for (int i = 0; i < elements.length; i++) { FieldInfo member = elements[i]; //-- skip transient members if (member.isTransient()) { continue; } //-- skip inherited fields if (base != null && base.getElementField(xmlNature.getNodeName()) != null) { continue; } createFieldMapping(classMapping, member, nsURI); } return classMapping; } //-- createClassMapping //-------------------/ //- Private Methods -/ //-------------------/ /** * Creates a FieldMapping for a given member and adds it to the given * ClassMapping. * @param classMapping the ClassMapping we are adding a new FieldMapping to * @param member member for which we are creating a FieldMapping * @param nsURI name space URI */ private void createFieldMapping(final ClassMapping classMapping, final FieldInfo member, final String nsURI) { XMLInfoNature xmlNature = new XMLInfoNature(member); XSType xsType = xmlNature.getSchemaType(); boolean any = false; NodeType nodeType = xmlNature.getNodeType(); boolean isAttribute = (nodeType == NodeType.ATTRIBUTE); boolean isText = (nodeType == NodeType.TEXT); //-- a hack, I know, I will change later (kv) if (member.getName().equals("_anyObject")) { any = true; } //Attributes can handle COLLECTION type for NMTOKENS or IDREFS for instance if (xsType.isCollection()) { xsType = new XMLInfoNature(((CollectionInfo) member).getContent()).getSchemaType(); } //-- create class choice on demand ClassChoice classChoice = classMapping.getClassChoice(); if (classChoice == null) { classChoice = new ClassChoice(); classMapping.setClassChoice(classChoice); } //-- create field mapping FieldMapping fieldMap = new FieldMapping(); classChoice.addFieldMapping(fieldMap); String fieldName = member.getName(); if (fieldName.charAt(0) == '_') { fieldName = fieldName.substring(1); } fieldMap.setName(fieldName); String className = getClassName(xsType.getJType()); if (className.equals("byte[]")) { className = "bytes"; } fieldMap.setType(className); BindXml bindXml = new BindXml(); fieldMap.setBindXml(bindXml); String nodeName = xmlNature.getNodeName(); if ((nodeName != null) && (!isText)) { bindXml.setName(nodeName); } if (isAttribute) { bindXml.setNode(BindXmlNodeType.ATTRIBUTE); } else if (isText) { bindXml.setNode(BindXmlNodeType.TEXT); } else { bindXml.setNode(BindXmlNodeType.ELEMENT); } switch (xsType.getType()) { case XSType.IDREF_TYPE : bindXml.setReference(true); break; case XSType.ID_TYPE : classMapping.addIdentity(member.getName()); break; case XSType.QNAME_TYPE : bindXml.setType("QName"); default: break; } //-- set any user-specified field handler fieldMap.setHandler(member.getXMLFieldHandler()); //-- container if (member.isContainer()) { fieldMap.setContainer(true); } // Handle namespaces. FieldInfo namespace has higher priority than ClassInfo namespace. // TODO Need to add better namespace support to bind-xml element, // it's not very good at the moment // nsURI = member.getNamespaceURI(); // if (nsURI != null) { // jsc.add("desc.setNameSpaceURI(\""); // jsc.append(nsURI); // jsc.append("\");"); // } // // if (any && member.getNamespaceURI() == null) { // nsURI = null; // } // required if (xmlNature.isRequired()) { fieldMap.setRequired(true); } // nillable if (member.isNillable()) { // TODO Mapping file needs nillable support! } // if any it can match all the names if (any) { bindXml.setMatches("*"); } // Add Validation Code // TODO mapping file has no validation support so users need to use xsi:schemaLocation // in their XML instances and enable schema validation on the parser } /** * Returns the classname for the given JType. The class name may be the * fully qualified classname or the mapping loader "short" name. * * @param jType * the JType for which to return the class name * @return the classname for the given XSType */ private static String getClassName(final JType jType) { // TODO Look up short names from: org.exolab.castor.mapping.loader.Types if (jType.isPrimitive()) { return jType.getName(); } return jType.toString(); } }