/**
* 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 2000 (C) Intalio Inc. All Rights Reserved.
*
* $Id: Converter.java 7996 2008-12-16 08:25:44Z wguttmn $
*/
package org.exolab.castor.xml.dtd;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Iterator;
import org.exolab.castor.xml.dtd.parser.DTDInitialParser;
import org.exolab.castor.xml.dtd.parser.DTDParser;
import org.exolab.castor.xml.dtd.parser.InputCharStream;
import org.exolab.castor.xml.dtd.parser.ParseException;
import org.exolab.castor.xml.dtd.parser.TokenMgrError;
import org.exolab.castor.xml.schema.Annotation;
import org.exolab.castor.xml.schema.AttributeDecl;
import org.exolab.castor.xml.schema.ComplexType;
import org.exolab.castor.xml.schema.ContentType;
import org.exolab.castor.xml.schema.Documentation;
import org.exolab.castor.xml.schema.ElementDecl;
import org.exolab.castor.xml.schema.Facet;
import org.exolab.castor.xml.schema.FacetFactory;
import org.exolab.castor.xml.schema.Group;
import org.exolab.castor.xml.schema.Order;
import org.exolab.castor.xml.schema.Particle;
import org.exolab.castor.xml.schema.Schema;
import org.exolab.castor.xml.schema.SchemaException;
import org.exolab.castor.xml.schema.SimpleType;
import org.exolab.castor.xml.schema.SimpleTypesFactory;
import org.exolab.castor.xml.schema.Wildcard;
import org.exolab.castor.xml.schema.writer.SchemaWriter;
import org.xml.sax.SAXException;
/**
* Class containing static top-level methods to parse and convert
* XML DTD documents to corresponding XML Schema documents.
* Also contains simple command line interface to read
* an XML DTD file and create corresponding XML Schema object.
* @author <a href="mailto:totok@intalio.com">Alexander Totok</a>
* @version $Revision: 7996 $ $Date: 2006-04-14 04:14:43 -0600 (Fri, 14 Apr 2006) $
*/
public class Converter {
/**
* Simple command line interface to read an XML DTD file and create
* corresponding XML Schema file. Usage: <pre>
* java org.exolab.castor.xml.dtd.Converter dtd_file xsd_file [character_encoding]
*
* dtd_file: name of the input DTD file
* xsd_file: name of the output Schema file
* character_encoding: name of the character encoding,
* if not specified, ASCII is chosen</pre>
* Help message is provided.
* @throws DTDException if the input DTD document is malformed.
* @throws SchemaException if Schema object can not be created.
* @throws SAXException if an error occured during marshalling of schema object
* constructed from the DTD document.
*/
public static void main (String args[]) throws IOException,
DTDException,
SchemaException,
SAXException {
if ((args.length < 2) || (args.length > 3)) {
String s = "\nUsage:\n";
s += "java org.exolab.castor.xml.dtd.Converter dtd_file xsd_file [character_encoding]\n\n";
s += "dtd_file: name of the input DTD file\n";
s += "xsd_file: name of the output Schema file\n";
s += "character_encoding: name of the character encoding,\n";
s += " if not specified, ASCII is chosen\n";
System.out.println(s);
} else {
String encoding;
//-- choose encoding
if (args.length == 2) encoding = "US-ASCII";
else {
if (args[2].equals("ascii") || args[2].equals("ASCII")
|| args[2].equals("us-ascii"))
encoding = "US-ASCII";
else if (args[2].equals("utf-8"))
encoding = "UTF-8";
else if (args[2].equals("utf-16"))
encoding = "UTF-16";
else encoding = args[2];
} //-- choose encoding
//-- instantiate input byte stream, associated with the input file
FileInputStream inputStream = new FileInputStream(args[0]);
//-- instantiate char reader from input file byte stream
InputStreamReader reader = new InputStreamReader(inputStream, encoding);
//-- instantiate output byte stream, associated with the output file
FileOutputStream outputStream = new FileOutputStream(args[1]);
//-- instantiate char writer from output file byte stream
OutputStreamWriter writer = new OutputStreamWriter(outputStream, encoding);
//-- convert DTD to Schema
convertDTDtoSchema(reader, writer);
reader.close();
writer.close();
}
}
/**
* Convert DTD document to corresponding XML Schema document.
* @param reader reader of the input DTD document.
* @param writer writer to the output Schema document.
* @throws DTDException if the DTD document is syntactically or semanticly
* not correct.
* @throws SchemaException if Schema object can not be created.
* @throws IOException if there is an I/O problem with the <tt>reader</tt>
* or <tt>writer</tt>.
* @throws SAXException if an error occured during schema object marshalling.
*/
public static void convertDTDtoSchema(Reader reader, Writer writer)
throws DTDException,
SchemaException,
IOException,
SAXException {
//-- parse text of DTD document
DTDdocument dtd = parseDTD(reader);
//-- convert DTD document object into its corresponding Schema object
Schema schema = convertDTDObjectToSchemaObject(dtd);
//-- marshal Schema object into its corresponding XML document
marshalSchema(schema, writer);
} //-- convertDTDtoSchema
/**
* Parses text of a DTD document and returns corresponding DTD document object.
* It is left to constructor of the <tt>reader</tt> to set up
* character encoding correctly. This means that method
* <u><font color="blue">read</font></u> of
* the <tt>reader</tt> is used to get next character, assuming it returns
* appropriate values.
* @param reader input char stream reader. It is recommended
* to use class {@link java.io.InputStreamReader java.io.InputStreamReader}
* as a <tt>reader</tt>, which allows to set desired character encoding.
* @return DTD document object corresponding to the input text
* of a DTD document.
* @throws DTDException if the DTD document is syntactically or semanticly
* not correct.
*/
public static DTDdocument parseDTD (Reader reader) throws
DTDException {
try {
InputCharStream charStream;
//-- instantiate char stream for initial parser from the input reader
charStream = new InputCharStream(reader);
//-- instantiate initial parser
DTDInitialParser initialParser = new DTDInitialParser(charStream);
//-- get result of initial parsing - DTD document with parameter
//-- entity references expanded
String intermedResult = initialParser.Input();
//-- construct StringReader from the intermediate result of parsing
StringReader strReader= new StringReader(intermedResult);
//-- instantiate char stream for main parser
charStream = new InputCharStream(strReader);
//-- instantiate main parser
DTDParser parser = new DTDParser(charStream);
//-- parse intermediate result by the main parser
//-- and get corresponding DTD document oblect
DTDdocument dtd = parser.Input();
strReader.close();
//-- return DTD document object
return dtd;
}
catch (TokenMgrError tme) {
String msg = tme.getMessage();
throw new DTDException("TokenMgrError" + (msg == null ? "" : ": " + msg));
}
catch (ParseException pe) {
String msg = pe.getMessage();
throw new DTDException("ParseException" + (msg == null ? "" : ": " + msg));
}
} //-- parseDTD
/**
* Convert DTD document object to corresponding Schema object.
* @param dtd input XML DTD document object.
* @return corresponding XML Schema object.
* @throws DTDException if the input DTD document is malformed.
* @throws SchemaException if Schema object can not be created.
*/
public static Schema convertDTDObjectToSchemaObject (DTDdocument dtd)
throws DTDException,
SchemaException {
Schema schema = new Schema();
String name = dtd.getName();
if (name != null && !name.equals("")) schema.setId(name);
// convert Notation declarations
Enumeration dtdNotations = dtd.getNotations();
while (dtdNotations.hasMoreElements()) {
dtdNotations.nextElement();
// do nothing for now as the Castor Schema object model does not
// support Notation declarations
} //-- convert Notations declarations
// convert General Entity declarations.
// XML Schema does not provide facilities analogous to General Entity
// declarations in XML DTD, so we convert each General Entity declaration
// to Documentation subelement of XML Schema document annotaion.
Enumeration dtdGeneralEntities = dtd.getGeneralEntities();
if (dtdGeneralEntities.hasMoreElements()) {
GeneralEntity ge;
Annotation annotation = new Annotation();
Documentation documentation;
String text;
while (dtdGeneralEntities.hasMoreElements()) {
ge = (GeneralEntity)dtdGeneralEntities.nextElement();
documentation = new Documentation();
text = "General Entity Declaration";
documentation.add(text);
documentation.add(ge);
annotation.addDocumentation(documentation);
}
schema.addAnnotation(annotation);
}
//-- convert General Entity declarations
// convert Element declarations
Enumeration dtdElements = dtd.getElements();
Element dtdElement; // DTD Element declaration
ElementDecl schemaElement; // Schema Element declaration
while (dtdElements.hasMoreElements()) {
dtdElement = (Element)dtdElements.nextElement();
schemaElement = convertDTDElementToSchemaElement(dtdElement, schema);
schema.addElementDecl(schemaElement);
} //-- convert Element declarations
return schema;
} //-- convertDTDObjectToSchemaObject
/**
* Convert DTD Element declaration to Schema Element Declaration.
* @param dtdElement DTD Element declaration.
* @param schema Schema owning Element declaration.
* @throws DTDException if the input DTD Element Declaration is malformed.
* @throws SchemaException if unable to construct return
* {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl} object from
* the input DTD {@link org.exolab.castor.xml.dtd.Element Element} object.
* @return corresponding Schema Element declaration.
*/
public static ElementDecl convertDTDElementToSchemaElement(Element dtdElement,
Schema schema)
throws DTDException,
SchemaException {
String name = dtdElement.getName();
if (name == null || name.equals("")) {
String err = "DTD to Schema converter: a DTD element has no name.";
throw new DTDException(err);
}
ElementDecl schemaElement = new ElementDecl(schema, name);
// start converting content of the element
ComplexType complexType = schema.createComplexType();
ContentType contentType = null;
Group group = null; // auxiliary
Iterator mixedChildrenIterator = null; // auxiliary
String elementRef = null; // auxiliary
ElementDecl elem = null; // auxiliary
if (dtdElement.isEmptyContent()) {
// mixed="false"
contentType = ContentType.elemOnly;
//-- mixed="false"
} else if (dtdElement.isAnyContent()) {
// mixed="true"
contentType = ContentType.mixed;
//-- mixed="true"
group = new Group();
group.setOrder(Order.sequence);
group.setMinOccurs(0);
group.setMaxOccurs(-1);
Wildcard any = new Wildcard(group);
group.addWildcard(any);
complexType.addGroup(group);
} else if (dtdElement.isElemOnlyContent()) {
// mixed="false"
contentType = ContentType.elemOnly;
//-- mixed="false"
ContentParticle dtdContent = dtdElement.getContent();
if (dtdContent == null) { // content is not specified
String err = "DTD to Schema converter: element \"" + dtdElement.getName();
err += "\" has no content.";
throw new DTDException(err);
}
Particle content = null;
try {
content = convertContentParticle(dtdContent, schema);
}
catch (DTDException e) {
String err = "DTD to Schema converter: content of DTD element \"" + dtdElement.getName();
err += "\", represented by a Content Particle, is malformed.";
throw new DTDException(err);
}
if (content instanceof ElementDecl) {
group = new Group();
group.setOrder(Order.sequence);
group.addElementDecl((ElementDecl)content);
complexType.addGroup(group);
} else {
complexType.addGroup((Group)content);
}
} else if (dtdElement.isMixedContent()) {
// mixed="true"
contentType = ContentType.mixed;
//-- mixed="true"
mixedChildrenIterator = dtdElement.getMixedContentChildren();
if ((mixedChildrenIterator != null) && (mixedChildrenIterator.hasNext())) {
group = new Group();
group.setOrder(Order.choice);
group.setMinOccurs(0);
group.setMaxOccurs(-1);
while (mixedChildrenIterator.hasNext()) {
elementRef = (String)mixedChildrenIterator.next();
elem = new ElementDecl(schema);
elem.setReferenceName(elementRef);
group.addElementDecl(elem);
}
complexType.addGroup(group);
}
} else { // the type of the element has not been specified
String err = "DTD to Schema converter: content type of DTD element \"" + dtdElement.getName();
err += "\" has not been specified.";
throw new DTDException(err);
}
complexType.setContentType(contentType);
// finish converting content of the element
// start attributes convertion
Enumeration dtdAttributes = dtdElement.getAttributes();
Attribute dtdAttribute;
AttributeDecl schemaAttribute;
while (dtdAttributes.hasMoreElements()) {
dtdAttribute = (Attribute)dtdAttributes.nextElement();
schemaAttribute = convertAttribute(dtdAttribute, schema);
complexType.addAttributeDecl(schemaAttribute);
}
// end attributes convertion
schemaElement.setType(complexType);
return schemaElement;
} //-- convertDTDElementToSchemaElement
/**
* Method to convert
* {@link org.exolab.castor.xml.dtd.ContentParticle ContentParticle} object,
* used to implement element content in the DTD object model, to the corresponding
* object in the Schema object model: either
* {@link org.exolab.castor.xml.schema.Group Group} or
* {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl}.
* @param dtdContent input
* {@link org.exolab.castor.xml.dtd.ContentParticle ContentParticle} object.
* @return object returned is an instance of either
* {@link org.exolab.castor.xml.schema.Group Group} class or
* {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl} class.
* @throws DTDException if the input ContentParticle is malformed.
* @throws SchemaException if unable to construct return content object
* from a given ContentParticle
*/
public static Particle convertContentParticle(ContentParticle dtdContent,
Schema schema)
throws DTDException, SchemaException {
Particle returnValue;
if (dtdContent.isReferenceType()) {
ElementDecl elem = new ElementDecl(schema);
elem.setReferenceName(dtdContent.getReference());
returnValue = elem;
} else if (dtdContent.isSeqType() || dtdContent.isChoiceType()) {
Group group = new Group();
if (dtdContent.isSeqType()) group.setOrder(Order.sequence);
else group.setOrder(Order.choice);
Enumeration children = dtdContent.getChildren();
ContentParticle child;
Particle contentParticle;
while(children.hasMoreElements()) {
child = (ContentParticle)children.nextElement();
contentParticle = convertContentParticle(child, schema);
if (contentParticle instanceof ElementDecl) {
group.addElementDecl((ElementDecl)contentParticle);
} else {
group.addGroup((Group)contentParticle);
}
}
returnValue = group;
} else { //-- type of input DTD Content Particle is not specified
throw new DTDException();
}
if (dtdContent.isOneOccurance()) {
returnValue.setMinOccurs(1);
returnValue.setMaxOccurs(1);
} else if (dtdContent.isOneOrMoreOccurances()) {
returnValue.setMinOccurs(1);
returnValue.setMaxOccurs(-1);
} else if (dtdContent.isZeroOrMoreOccurances()) {
returnValue.setMinOccurs(0);
returnValue.setMaxOccurs(-1);
} else if (dtdContent.isZeroOrOneOccurance()) {
returnValue.setMinOccurs(0);
returnValue.setMaxOccurs(1);
} else {
// a content particle always has "one occurance" default
// occurance specification
}
return returnValue;
} //-- convertContentParticle
/**
* Convert DTD Attribute declaration to Schema Attribute Declaration.
* @param dtdAttribute DTD Attribute declaration.
* @param schema Schema owning Element of this Attribute.
* @throws DTDException if the input DTD Attribute Declaration is malformed.
* @return corresponding Schema Attribute declaration.
*/
public static AttributeDecl convertAttribute(Attribute dtdAttribute,
Schema schema)
throws DTDException {
AttributeDecl schemaAttribute = new AttributeDecl(schema,
dtdAttribute.getName());
SimpleType type = null;
if (dtdAttribute.isStringType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.STRING_TYPE) );
}
else if (dtdAttribute.isIDType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.ID_TYPE) );
}
else if (dtdAttribute.isIDREFType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.IDREF_TYPE) );
}
else if (dtdAttribute.isIDREFSType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.IDREFS_TYPE) );
}
else if (dtdAttribute.isENTITYType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.ENTITY_TYPE) );
}
else if (dtdAttribute.isENTITIESType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.ENTITIES_TYPE) );
}
else if (dtdAttribute.isNMTOKENType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKEN_TYPE) );
}
else if (dtdAttribute.isNMTOKENSType())
{
type = schema.getSimpleType( schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKENS_TYPE) );
}
else if (dtdAttribute.isNOTATIONType())
{
type = schema.createSimpleType(null,
schema.getBuiltInTypeName(SimpleTypesFactory.NOTATION_TYPE),
"restriction");
Iterator values = dtdAttribute.getValues();
FacetFactory facetFactory = FacetFactory.getInstance();
while (values.hasNext()) {
Facet facet = facetFactory.createFacet(
Facet.ENUMERATION, (String) values.next());
facet.setOwningType(type);
type.addFacet(facet);
}
}
else if (dtdAttribute.isEnumerationType())
{
type = schema.createSimpleType(null,
schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKEN_TYPE),
"restriction");
Iterator values = dtdAttribute.getValues();
FacetFactory facetFactory = FacetFactory.getInstance();
while (values.hasNext()) {
Facet facet = facetFactory.createFacet(
Facet.ENUMERATION, (String)values.next());
facet.setOwningType(type);
type.addFacet(facet);
}
}
else
{
String err = "DTD to Schema converter: DTD attribute \"" + dtdAttribute.getName();
err += "\" has unspecified type.";
throw new DTDException(err);
}
schemaAttribute.setSimpleType(type);
if (dtdAttribute.isREQUIRED()) {
schemaAttribute.setUse(AttributeDecl.USE_REQUIRED);
} else if (dtdAttribute.isIMPLIED()) {
schemaAttribute.setUse(AttributeDecl.USE_OPTIONAL);
} else if (dtdAttribute.isFIXED()) {
} else { // DTD attribute is of "DEFAULT" type
schemaAttribute.setDefaultValue(dtdAttribute.getDefaultValue());
}
return schemaAttribute;
} //-- convertAttribute
/**
* Marshals XML Schema to output char stream.
* @param schema XML Schema object to marshal.
* @param writer output char stream to marshal Schema to.
* @throws IOException if there is an I/O problem
* with the <tt>writer</tt>.
* @throws SAXException if an error occured during <tt>schema</tt> marshalling.
*/
public static void marshalSchema(Schema schema, Writer writer)
throws IOException,
SAXException {
SchemaWriter.enable = true;
SchemaWriter sw = new SchemaWriter(writer);
sw.write(schema);
} //-- marshalSchema
} //-- Converter