SchemaBinder.java :  » XML » zeus » org » enhydra » zeus » binder » Java Open Source

Java Open Source » XML » zeus 
zeus » org » enhydra » zeus » binder » SchemaBinder.java
/*
 * Enhydra Java Application Server Project
 * 
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License on
 * the Enhydra web site ( http://www.enhydra.org/ ).
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
 * the License for the specific terms governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Enhydra Application Server is Lutris
 * Technologies, Inc. The Enhydra Application Server and portions created
 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
 * All Rights Reserved.
 */
package org.enhydra.zeus.binder;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

// Zeus imports
import org.enhydra.zeus.Binder;
import org.enhydra.zeus.Binding;
import org.enhydra.zeus.Source;
import org.enhydra.zeus.ZeusDefaults;
import org.enhydra.zeus.binding.AtomicProperty;
import org.enhydra.zeus.binding.ContainerProperty;
import org.enhydra.zeus.util.NamingUtils;
import org.enhydra.zeus.util.SchemaUtils;

// JDOM imports
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;

/**
 * <p>
 *  <code>SchemaBinder</code> implements the <code>{@link Binder}</code>
 *    interface and allows generation of Zeus
 *    <code>{@link Binding}</code>s from an XML Schema.
 * </p>
 *
 * @author Brett McLaughlin
 * @version 1.0
 */
public class SchemaBinder extends BaseBinder {

    /** The JDOM <code>Namespace</code> object for the schema namespace */
    private Namespace schemaNamespace;

    /**
     * <p>
     *  This constructor takes in a <code>{@link Source}</code>
     *    to read an XML Schema from and allow generation of the 
     *    <code>{@link Binding}</code>s from it.
     * </p>
     *
     * @param source <code>Source</code> to read input from.
     */
    public SchemaBinder(Source source) {
        super(source);

        // Set up default XML Schema namespace
        schemaNamespace = 
            Namespace.getNamespace(ZeusDefaults.SCHEMA_NAMESPACE_URI);
    }

    /**
     * <p>
     *  This returns the current namespace URI being used for XML Schema.
     * </p>
     *
     * @return <code>String</code> - XML Schema namespace URI being used.
     */
    public String getSchemaNamespaceURI() {
        return schemaNamespace.getURI();
    }

    /**
     * <p>
     *  This will set the URI to use for XML Schema. This is useful for 
     *    documents that have an older schema namespace URI. Note that this does
     *    <i>not</i> imply that the <code>SchemaBinder</code> can deal with
     *    older schema features and syntax; this is purely for incorrect or old
     *    namespace URIs.
     * </p>
     *
     * @param schemaNamespaceURI the URI to use for XML Schema
     */
    public void setSchemaNamespaceURI(String schemaNamespaceURI) {
        schemaNamespace = Namespace.getNamespace(schemaNamespaceURI);
    }

    /**
     * <p>
     *  This is integral portion of the <code>Binder</code>. It
     *    is responsible for returning a Zeus representation of
     *    the set of constraints that this binding represents,
     *    resulting from the supplied XML Schema.
     * </p>
     *
     * @return <code>List</code> - the resultant
     *         <code>Bindings</code> from conversion of
     *         constraints.
     * @throws <code>IOException</code> when errors in reading
     *         input occur.
     */
    public List getBindings() throws IOException {
        Document schema = source.getDocument();
        Element root = schema.getRootElement();
    
        return convertToBindings(root);
    }
    
    /**
     * <p>
     *  This takes a single <code>Element</code> (in JDOM form), and
     *    converts it to a <code>Binding</code>.
     * </p>
     *
     * @param element <code>Element</code> to convert to a Zeus
     *        <code>Binding</code>.
     * @return <code>List</code> - the <code>Binding</code>s from
     *         the supplied <code>Element</code>.
     */
    private List convertToBindings(Element element) {

        // Create the list to work with
        List bindings = new LinkedList();

        // Get all the elements named "element"
        List elements = element.getChildren("element", schemaNamespace);
        for (Iterator i = elements.iterator(); i.hasNext(); ) {
            Element nextElement = (Element)i.next();
            Attribute elementName = nextElement.getAttribute("name");
            if (elementName == null) {
                // All root-level elements should be named types
                throw new IllegalArgumentException("All root-level elements " +
                    "should be named types (with a 'name' attribute).");
            }

            // Deal with the element definition
            bindings.add(convertDefinitionToBinding(nextElement));
        }

        /**
         * Currently, anything other than root-level elements are ignored.
         *   Eventually, complexTypes defined here should be handled at this
         *   point.
         */
        
        // Deal with the element itself
        
        return bindings;
    }

    /**
     * <p>
     *   This will take an element that defines a structure (already determined
     *     by the time that this method is invoked) and convert the definition
     *     to a <code>{@link Binding}</code>.
     * </p>
     *
     * @param element the element to convert
     * @return <code>Binding</code> - the converted Zeus <code>Binding</code>.
     */
    private Binding convertDefinitionToBinding(Element element) {

        // Create base container
        String xmlName = element.getAttribute("name").getValue();
        String xmlType = xmlName;

        ContainerProperty container =
            new ContainerProperty(xmlName, xmlType);

        // See if this is a simple type or complex type
        Attribute type = element.getAttribute("type");
        if (type != null) {
            // Simple type: deal with allowed values
            String schemaType = type.getValue();
            // XXX: I am sure I broke this! (Sean)
            AtomicProperty property =
                new AtomicProperty("value", schemaType);
            container.addProperty(property);
        } else {
            // This should be a complex type
            Element complexType = 
                element.getChild("complexType", schemaNamespace);
            if (complexType == null) {
                // XXX: This should be a Zeus-specific exception
                throw new IllegalArgumentException("Illegal schema: " +
                    "No complexType defined for this element.");
            }

            // Determine if this extends a base type
            Attribute baseType = complexType.getAttribute("baseType");
            if (baseType != null) {
                // Set up the base type as a property
                String schemaType = baseType.getValue();

                // XXX: I am sure I broke this! (Sean)
                AtomicProperty property =
                    new AtomicProperty("value", schemaType);
                container.addProperty(property);
            }

            // If there is a "sequence" element, it doesn't affect anything
            Element parent = complexType.getChild("sequence", schemaNamespace);
            if (parent == null) {
                parent = complexType;
            }

            // Deal with the attributes
            List attributes = parent.getChildren("attribute", schemaNamespace);
            for (Iterator i = attributes.iterator(); i.hasNext(); ) {
                Element theAttribute = (Element)i.next();
                Attribute propertyName = theAttribute.getAttribute("name");
                if (propertyName == null) {
                    // XXX: This should be a Zeus-specific exception
                    throw new IllegalArgumentException("Attributes must have " +
                        "a name (specified by the 'name' attribute).");
                }

                Attribute schemaType = theAttribute.getAttribute("type");
                if (schemaType == null) {
                    // XXX: This should be a Zeus-specific exception
                    throw new IllegalArgumentException("Attributes must have " +
                        "a type (specified by the 'type' attribute).");
                }

                // XXX: I am sure I broke this! (Sean)
                AtomicProperty property =
                    new AtomicProperty(propertyName.getValue(),
                                       "",
                                       "string",
                                       ZeusDefaults.SCHEMA_NAMESPACE_URI);
                container.addProperty(property);
            }

            // Deal with the elements
            List elements = parent.getChildren("element", schemaNamespace);
            for (Iterator i = elements.iterator(); i.hasNext(); ) {
                Element theElement = (Element)i.next();

                // If an element ref, add as a property; otherwise recurse
                Attribute ref = theElement.getAttribute("ref");
                if (ref == null) {
                } else {
                    // Since this is a reference, add the property
                    String propertyName = ref.getValue();
                    String propertyType = 
                        NamingUtils.getJavaClassName(propertyName);

                    // XXX: I am sure I broke this! (Sean)
                    AtomicProperty property
                        = new AtomicProperty(propertyName,
                                             propertyType);

                    // Determine if a list is needed
                    Attribute maxOccurs = theElement.getAttribute("maxOccurs");
                    if (maxOccurs != null) {
                        String value = maxOccurs.getValue();

                        // The value should be either 'unbounded' or an int
                        if (value.equals("unbounded")) {
                            property.setIsCollection(true);
                        } else {
                            try {
                                int maxOccursInt = Integer.parseInt(value);
                                if (maxOccursInt > 1) {
                                    property.setIsCollection(true);
                                }
                            } catch (NumberFormatException ignored) {
                                // Not a collection, so don't worry about it
                            }
                        }
                    }

                    container.addProperty(property);
                }
            }
        }

        return container;
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.