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

Java Open Source » XML » zeus 
zeus » org » enhydra » zeus » binder » DTDBinder.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.BitSet;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

// Zeus imports
import org.enhydra.zeus.Binder;
import org.enhydra.zeus.Binding;
import org.enhydra.zeus.ZeusDefaults;
import org.enhydra.zeus.binding.Property;
import org.enhydra.zeus.binding.AtomicProperty;
import org.enhydra.zeus.binding.Container;
import org.enhydra.zeus.binding.ContainerProperty;
import org.enhydra.zeus.source.DTDSource;
import org.enhydra.zeus.util.CapitalizationUtils;
import org.enhydra.zeus.util.DTDUtils;

// DTDParser imports
import com.wutka.dtd.DTD;
import com.wutka.dtd.DTDAny;
import com.wutka.dtd.DTDAttribute;
import com.wutka.dtd.DTDCardinal;
import com.wutka.dtd.DTDContainer;
import com.wutka.dtd.DTDDecl;
import com.wutka.dtd.DTDElement;
import com.wutka.dtd.DTDEmpty;
import com.wutka.dtd.DTDEnumeration;
import com.wutka.dtd.DTDItem;
import com.wutka.dtd.DTDMixed;
import com.wutka.dtd.DTDName;
import com.wutka.dtd.DTDPCData;

/**
 * <p>
 *  <code>DTDBinder</code> implements the <code>{@link Binder}</code>
 *    interface and allows generation of Zeus
 *    <code>{@link Binding}</code>s from a DTD.
 * </p>
 *
 * @author Brett McLaughlin
 * @author Steve Witten
 * @author Maciej Zawadzki
 * @author Sean Ogle
 */
public class DTDBinder extends BaseBinder {

    /** The DTD being processed */
    protected DTD dtd;
    
    /** The elements by name */
    protected Hashtable elements;
    
    /** The root element of the DTD */
    protected String rootElementName;

    /**
     * <p>
     *  This constructor takes in a <code>{@link DTDSource}</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.
     * @param rootElementName the name of the DTD's root element
     */
    public DTDBinder(DTDSource source, String rootElementName) {
        super(source);
        this.rootElementName = rootElementName;
        elements = new Hashtable();
    }
    
    /**
     * <p>
     *  This constructor takes in a <code>{@link DTDSource}</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 DTDBinder(DTDSource source) {
        this(source, null);
    }
    
    /**
     * <p>
     *  This retrieves the name of the root element for this binder.
     * </p>
     *
     * @param <code>String</code> - this binder DTD's root element name.
     */
    public String getRootElementName() {
        return rootElementName;
    }

    /**
     * <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 DTD.
     * </p>
     *
     * @return <code>List</code> - the resultant
     *         <code>Binding</code>s from conversion of
     *         constraints.
     * @throws <code>IOException</code> when errors in reading
     *         input occur.     
     */
    public List getBindings() throws IOException {
        // Get the DTD
        dtd = ((DTDSource)source).getDTD();
        
        // Create a list for the bindings
        List bindings = new LinkedList();
        
        // Get all of the elements in the DTD
        Vector dtdElements = dtd.getItemsByType(DTDElement.class);
        
        // First, process element names and types
        for (Iterator i = dtdElements.iterator(); i.hasNext(); ) {
            DTDElement dtdElement = (DTDElement)i.next();
            buildNamesAndTypes(dtdElement);
        }
        
        // Second pass covers bindings
        for (Iterator i = dtdElements.iterator(); i.hasNext(); ) {
            DTDElement dtdElement = (DTDElement)i.next();
            Binding binding = convertToBinding(dtdElement);
            if (rootElementName != null) {
                if (dtdElement.getName().equals(rootElementName)) {
                    binding.setIsXMLRootElement(true);
                }
            } else if (dtdElement == dtd.rootElement) {
                binding.setIsXMLRootElement(true);
            }
            bindings.add(binding); 
        }
    
        return bindings;
    }
    
    /**
     * <p>
     *  This will build up a listing of the element's name and type for
     *    use in type lookups later.
     * </p>
     *
     * @param dtdElement <code>DTDElement</code> to build from.
     */
    private void buildNamesAndTypes(DTDElement dtdElement) {
        // The xmlName is the same as in the DTD
        String xmlName = dtdElement.getName();

        String xmlType = xmlName;
        
        // if collapsing, type is a string for simple elements...
        if ((isCollapsingSimpleElements) &&
            (DTDUtils.isSimpleElement(dtdElement, isIgnoringIDAttributes))) {
            
            xmlType = "string";
        }
        
        elements.put(xmlName, xmlType);
    }
    
    /**
     * <p>
     *  This will take a single DTD element (in the form of a
     *    <code>DTDElement</code>) and convert it to a Zeus
     *    <code>Binding</code> object.
     * </p>
     *
     * @param dtdElement <code>DTDElement</code> to convert to
     *        Zeus <code>Binding</code>.
     * @return <code>Binding</code> - the converted Zeus <code>Binding</code>.
     */
    private Binding convertToBinding(DTDElement dtdElement) {
        // The xmlName is the same as in the DTD
        String xmlName = dtdElement.getName();
        String xmlType = (String)elements.get(xmlName);
        
        // This property is an element.
        BitSet modifiers = new BitSet();
        modifiers.set(Property.SOURCE_ELEMENT);

        // Build a new container to hold the element's contents.
        ContainerProperty container = 
            new ContainerProperty(xmlName, xmlType);
        container.setModifier(modifiers);
            
        // Deal with the content of the DTDElement
        handleContent(container, dtdElement.getContent());
        
        // Deal with the attributes of the DTDElement
        handleAttributes(container, dtdElement);
    
        return container;
    }
    
    /**
     * <p>
     *  This will take an element from a DTD (in <code>DTDElement</code>
     *    form) and create Zeus <code>{@link AtomicProperty}</code> objects
     *    from its attributes, adding them to the supplied
     *    <code>{@link Container}</code>.
     * </p>
     *
     * @param container <code>Container</code> to add properties to.
     * @param dtdElement <code>DTDElement</code> to read attributes of.
     */
    private void handleAttributes(Container container, DTDElement dtdElement) {
        Hashtable elementAttributes = dtdElement.attributes;
        Enumeration e = elementAttributes.elements();
        while (e.hasMoreElements()) {
            DTDAttribute attribute = (DTDAttribute)e.nextElement();
            /**
             * Create the attribute; all DTD attributes are 
             *   simply Java <code>String</code>s, since
             *   there is no typing in DTDs.
             */
            DTDDecl decl = attribute.getDecl();
            
            // This will have private access and is from an XML attribute.
            BitSet modifiers = new BitSet();
            modifiers.set(Property.ACCESS_PRIVATE);
            modifiers.set(Property.SOURCE_ATTLIST);
            
            // should the decl be stored for future reference?
            //   maybe in the code generator?
            // we need to handle #REQUIRED, #IMPLIED, and #VALUE too...
            if ((decl != null) && (decl.equals(DTDDecl.FIXED))) {
                modifiers.set(Property.MUTABILITY_FINAL);
                modifiers.set(Property.STORAGE_STATIC);
            }

            /**
             * Set up the XML name and type information for an attribute. The
             *   type is always an XML string, and the namespace is the same
             *   as that of the XML Schema namespace, for default type
             *   processing.
             */
            String xmlName = attribute.getName();
            String xmlType = "string";

            // Get any enumeration information from the DTD.
            Vector enumeration =
                (attribute.getType() instanceof DTDEnumeration) ? 
                ((DTDEnumeration)attribute.getType()).getItemsVec() :
                null;

            AtomicProperty atomicProperty =
                new AtomicProperty(xmlName,
                                   "",
                                   xmlType,
                                   ZeusDefaults.SCHEMA_NAMESPACE_URI,
                                   modifiers,
                                   enumeration,
                                   attribute.getDefaultValue());

            container.addProperty(atomicProperty);
        }
    }

    /**
     * <p>
     *  This will take an element's content (in <code>DTDItem</code>
     *    form) and create Zeus <code>{@link AtomicProperty}</code> objects
     *    from its attributes, adding them to the supplied
     *    <code>{@link Container}</code>.
     * </p><p>
     *  This convenience version (called by default) supplies no transitive
     *    cardinality, meaning that each content is treated as isolated from
     *    any other content being processed elsewhere.
     * </p>
     *
     * @param conainer <code>Container</code> to add properties to.
     * @param content <code>DTDItem</code> with element's content.
     */
    private void handleContent(Container container, DTDItem content) {
        handleContent(container, content, DTDCardinal.NONE);
    }
    
    /**
     * <p>
     *  This will take an element's content (in <code>DTDItem</code>
     *    form) and create Zeus <code>{@link AtomicProperty}</code> objects
     *    from its attributes, adding them to the supplied
     *    <code>{@link Container}</code>. The items used have the supplied
     *    cardinality applied to them.
     * </p>
     *
     * @param conainer <code>Container</code> to add properties to.
     * @param content <code>DTDItem</code> with element's content.
     * @param transCardinality <code>DTDCardinality</code> with any transitive
     *        cardinality applied.
     */
    private void handleContent(Container container, DTDItem content,
                               DTDCardinal transCardinality) {
        // Cycle through the various types of content
        if (content instanceof DTDEmpty) {       
            // We're done - this is an empty element
        } else if (content instanceof DTDPCData) {
            /**
             * PCDATA elements have text content that we map to an attribute
             *   with a special XML name. The namespace is the same
             *   as that of the XML Schema namespace, for default type
             *   processing.
             */
            String xmlName = ZeusDefaults.PCDATA_XML_NAME;
            String xmlType = "string";

            AtomicProperty property = 
                new AtomicProperty(xmlName, "",
                                   xmlType, ZeusDefaults.SCHEMA_NAMESPACE_URI);

            container.addProperty(property);
        } else if (content instanceof DTDName) { 
            // Generate a single property
            BitSet modifiers = new BitSet();
            modifiers.set(Property.ACCESS_PRIVATE);
            modifiers.set(Property.SOURCE_ELEMENT);

            // The XML name is the same as in the DTD.
            String xmlName = ((DTDName)content).getValue();

            // The XML type is the same as the XML name for elements.
            String xmlType = (String)elements.get(xmlName);

            AtomicProperty property = new AtomicProperty(xmlName, xmlType);
            property.setModifier(modifiers);

            // Handle cardinality - either a list or a single property
            DTDCardinal cardinality = ((DTDName)content).getCardinal();

            if ((cardinality.equals(DTDCardinal.ZEROMANY)) ||
                (cardinality.equals(DTDCardinal.ONEMANY))) {                    
                property.setIsCollection(true);
            } else if ((transCardinality.equals(DTDCardinal.ZEROMANY)) ||
                       (transCardinality.equals(DTDCardinal.ONEMANY))) {
                           
                property.setIsCollection(true);
            }

            container.addProperty(property);
        } else if (content instanceof DTDAny) {
            String xmlName = "";
            String xmlType = "anyType";

            AtomicProperty property = 
                new AtomicProperty(xmlName, "", 
                                   xmlType, ZeusDefaults.SCHEMA_NAMESPACE_URI);

            property.setIsCollection(true);

            container.addProperty(property);
        } else if (content instanceof DTDContainer) {
            // Get each item and handle it
            Vector items = ((DTDContainer)content).getItemsVec();
            
            // Deal with transitive nature of cardinality on containers
            DTDCardinal cardinality = content.getCardinal();

            for (Iterator i = items.iterator(); i.hasNext(); ) {
                DTDItem subContent = (DTDItem)i.next();

                handleContent(container, subContent, cardinality);
            }
        }    
    }
}
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.