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