View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 2000 (C) Intalio Inc. All Rights Reserved.
32   *
33   * $Id$
34   */
35  
36  package org.exolab.castor.xml.dtd;
37  
38  import java.io.FileInputStream;
39  import java.io.FileOutputStream;
40  import java.io.IOException;
41  import java.io.InputStreamReader;
42  import java.io.OutputStreamWriter;
43  import java.io.Reader;
44  import java.io.StringReader;
45  import java.io.Writer;
46  import java.util.Enumeration;
47  import java.util.HashMap;
48  import java.util.Iterator;
49  import java.util.Map;
50  
51  import org.apache.commons.cli.CommandLine;
52  import org.apache.commons.cli.CommandLineParser;
53  import org.apache.commons.cli.DefaultParser;
54  import org.apache.commons.cli.HelpFormatter;
55  import org.apache.commons.cli.Option;
56  import org.apache.commons.cli.Options;
57  import org.apache.commons.logging.Log;
58  import org.apache.commons.logging.LogFactory;
59  import org.exolab.castor.xml.dtd.parser.DTDInitialParser;
60  import org.exolab.castor.xml.dtd.parser.DTDParser;
61  import org.exolab.castor.xml.dtd.parser.InputCharStream;
62  import org.exolab.castor.xml.dtd.parser.ParseException;
63  import org.exolab.castor.xml.dtd.parser.TokenMgrError;
64  import org.exolab.castor.xml.schema.Annotation;
65  import org.exolab.castor.xml.schema.AttributeDecl;
66  import org.exolab.castor.xml.schema.ComplexType;
67  import org.exolab.castor.xml.schema.ContentType;
68  import org.exolab.castor.xml.schema.Documentation;
69  import org.exolab.castor.xml.schema.ElementDecl;
70  import org.exolab.castor.xml.schema.Facet;
71  import org.exolab.castor.xml.schema.FacetFactory;
72  import org.exolab.castor.xml.schema.Group;
73  import org.exolab.castor.xml.schema.Order;
74  import org.exolab.castor.xml.schema.Particle;
75  import org.exolab.castor.xml.schema.Schema;
76  import org.exolab.castor.xml.schema.SchemaException;
77  import org.exolab.castor.xml.schema.SimpleType;
78  import org.exolab.castor.xml.schema.SimpleTypesFactory;
79  import org.exolab.castor.xml.schema.Wildcard;
80  import org.exolab.castor.xml.schema.writer.SchemaWriter;
81  import org.xml.sax.SAXException;
82  
83  /**
84   * Class containing methods to parse and convert XML DTD documents to corresponding XML Schema
85   * documents. Also contains simple command line interface to read an XML DTD file and create
86   * corresponding XML Schema object.
87   * 
88   * @author <a href="mailto:totok@intalio.com">Alexander Totok</a>
89   * @version $Revision$ $Date: 2006-04-14 04:14:43 -0600 (Fri, 14 Apr 2006) $
90   */
91  public class Converter {
92  
93    private static final Log log = LogFactory.getLog(Converter.class);
94  
95    public static final String NAME_SPACE_PREFIX_KEY = "nameSpacePrefixKey";
96  
97    public static final String NAME_SPACE_KEY = "nameSpaceKey";
98  
99    public static final String DEFAULT_NAME_SPACE_PREFIX = "tns";
100 
101   public static final String DEFAULT_NAME_SPACE = "generated.castor.org";
102 
103   /**
104    * Simple command line interface to read an XML DTD file and create corresponding XML Schema file.
105    * Usage:
106    * 
107    * <pre>
108    * java org.exolab.castor.xml.dtd.Converter  dtd_file xsd_file [character_encoding]
109    *    [-tns=[TNS_PREFIX:]NAMESPACE_URI]
110    *    [-xmlns=[TNS_PREFIX:]NAMESPACE_URI]*
111    * 
112    * dtd_file: name of the input DTD file
113    * xsd_file: name of the output Schema file
114    * character_encoding: name of the character encoding,
115    *            if not specified, ASCII is chosen
116    * </pre>
117    * 
118    * Help message is provided.
119    * 
120    * @throws DTDException if the input DTD document is malformed.
121    * @throws SchemaException if Schema object can not be created.
122    * @throws SAXException if an error occured during marshalling of schema object constructed from
123    *         the DTD document.
124    */
125   public static void main(String args[])
126       throws IOException, DTDException, SchemaException, SAXException {
127 
128     StringBuilder header = new StringBuilder().append('\n')
129         .append("Converts a DTD to an XML schema.\n\n")
130         .append("   <DTD>: Name of the input DTD file.\n")
131         .append("   <XSD>: Name of the output XML schema file.\n").append('\n').append("Options:");
132 
133     Options options = new Options();
134 
135     Option targetNamespace =
136         Option.builder("tns").desc("target namespace of the XML schema generated").required(false)
137             .longOpt("targetNamespace").hasArg().argName("[prefix:]uri").build();
138 
139     Option xmlns = Option.builder("xmlns").desc("xml namespace declarations").required(false)
140         .hasArgs().valueSeparator(',').argName("[[prefix:]uri]*").build();
141 
142     options.addOption(targetNamespace);
143     options.addOption(xmlns);
144     options.addOption("h", "help", false, "prints usage information");
145     options.addOption("e", "encoding", false, "character encoding");
146 
147     CommandLineParser parser = new DefaultParser();
148     CommandLine line = null;
149     try {
150       line = parser.parse(options, args);
151     } catch (org.apache.commons.cli.ParseException e) {
152       System.err.println("Parsing failed.  Reason: " + e.getMessage());
153     }
154 
155     if (args.length < 2 || line.hasOption("help")) {
156       HelpFormatter formatter = new HelpFormatter();
157       formatter.printHelp("org.exolab.castor.xml.dtd.Converter <DTD> <XSD>", header.toString(),
158           options, "");
159       return;
160     }
161 
162     String encoding = "US-ASCII";
163     String targetNameSpace = DEFAULT_NAME_SPACE;
164     Map<String, String> nameSpaceMap = new HashMap<String, String>();
165 
166     if (line.hasOption("tns")) {
167       log.info("Found option -tns ...");
168       Map<String, String> nameSpaceMapTemp = parseNamespace(line.getOptionValue("tns"));
169       targetNameSpace = nameSpaceMapTemp.get(NAME_SPACE_KEY);
170       nameSpaceMap.put(nameSpaceMapTemp.get(NAME_SPACE_PREFIX_KEY), targetNameSpace);
171     }
172 
173     if (line.hasOption("xmlns")) {
174       log.info("Found option -xmlns ...");
175       Map<String, String> nameSpaceMapTemp = parseNamespace(line.getOptionValue("xmlns"));
176       nameSpaceMap.put(nameSpaceMapTemp.get(NAME_SPACE_PREFIX_KEY),
177           nameSpaceMapTemp.get(NAME_SPACE_KEY));
178     }
179 
180     if (line.hasOption("encoding")) {
181       log.info("Found option -encoding ...");
182       String encodingValue = line.getOptionValue("encoding");
183       if (encodingValue.equalsIgnoreCase("ascii") || args[2].equalsIgnoreCase("us-ascii")) {
184         encoding = "US-ASCII";
185       } else if (encodingValue.equalsIgnoreCase("utf-8")) {
186         encoding = "UTF-8";
187       } else if (encodingValue.equalsIgnoreCase("utf-16")) {
188         encoding = "UTF-16";
189       } else {
190         encoding = encodingValue.toUpperCase();
191       }
192     }
193 
194     String inFile = args[0];
195     String outFile = args[1];
196 
197     Converter convertor = new Converter();
198     convertor.process(inFile, outFile, encoding, targetNameSpace, nameSpaceMap);
199   }
200 
201   /**
202    * @param dtdFile Location of the DTD file.
203    * @param schemaFile Location of the XML schema file.
204    * @param encoding (Optional) encoding.
205    * @param targetNamespace (Optional) target name space.
206    * @param namespaces (Optional) XML name spaces.
207    * @throws SchemaException
208    * @throws DTDException
209    * @throws IOException
210    * @throws SAXException
211    */
212   public void process(final String dtdFile, final String schemaFile, final String encoding,
213       final String targetNamespace, final Map<String, String> namespaces)
214       throws SchemaException, DTDException, IOException, SAXException {
215 
216     // instantiate input byte stream, associated with the input file
217     FileInputStream inputStream = new FileInputStream(dtdFile);
218 
219     // instantiate char reader from input file byte stream
220     InputStreamReader reader = new InputStreamReader(inputStream, encoding);
221 
222     // instantiate output byte stream, associated with the output file
223     FileOutputStream outputStream = new FileOutputStream(schemaFile);
224 
225     // instantiate char writer from output file byte stream
226     OutputStreamWriter writer = new OutputStreamWriter(outputStream, encoding);
227 
228     process(reader, writer, encoding, targetNamespace, namespaces);
229   }
230 
231   /**
232    * @param in Location of the DTD file.
233    * @param out Location of the XML schema file.
234    * @param encoding (Optional) encoding.
235    * @param targetNamespace (Optional) target name space.
236    * @param namespaces (Optional) XML name spaces.
237    * @throws SchemaException
238    * @throws DTDException
239    * @throws IOException
240    * @throws SAXException
241    */
242   public void process(final Reader in, final Writer out, final String encoding,
243       final String targetNamespace, final Map<String, String> namespaces)
244       throws SchemaException, DTDException, IOException, SAXException {
245 
246     try {
247       // -- convert DTD to Schema
248       convertDTDtoSchema(in, out, targetNamespace, namespaces);
249     } finally {
250       in.close();
251       out.close();
252     }
253   }
254 
255   protected static Map<String, String> parseNamespace(final String nameSpaceArg) {
256 
257     Map<String, String> nameSpaceMap = new HashMap<String, String>();
258 
259     if ("".equals(nameSpaceArg.substring("-tns=".length()))) {
260       throw new RuntimeException("name space argument is emty, Spaces after '='? ");
261     }
262 
263     String[] tnsToken = nameSpaceArg.substring("-tns=".length()).split(":", 2);
264 
265     if (isNameSpacePrefix(tnsToken[0])) {
266       nameSpaceMap.put(NAME_SPACE_PREFIX_KEY, tnsToken[0]);
267       nameSpaceMap.put(NAME_SPACE_KEY, tnsToken[1]);
268     } else {
269       nameSpaceMap.put(NAME_SPACE_KEY, nameSpaceArg.substring("-tns=".length()));
270       nameSpaceMap.put(NAME_SPACE_PREFIX_KEY, "tns");
271     } // -- namespace
272 
273     return nameSpaceMap;
274 
275   }
276 
277   protected static boolean isNameSpacePrefix(String nameSpacePrefix2test) {
278 
279     if (!nameSpacePrefix2test.matches("[a-z]*")) {
280       return false;
281     }
282 
283     if (nameSpacePrefix2test.matches("https?")) {
284       return false;
285     }
286 
287     return true;
288 
289   }
290 
291   /**
292    * Convert DTD document to corresponding XML Schema document.
293    * 
294    * @param reader reader of the input DTD document.
295    * @param writer writer to the output Schema document.
296    * @param targetNameSpaceMap (Optional) XML target name space for the XML schema.
297    * @param nameSpaceMap (Optional) XML name space declarations for the XML schema.
298    * @throws DTDException if the DTD document is syntactically or semantically not correct.
299    * @throws SchemaException if Schema object can not be created.
300    * @throws IOException if there is an I/O problem with the <tt>reader</tt> or <tt>writer</tt>.
301    * @throws SAXException if an error occured during schema object marshalling.
302    */
303   public void convertDTDtoSchema(Reader reader, Writer writer, String targetNameSpace,
304       Map<String, String> nameSpaceMap)
305       throws DTDException, SchemaException, IOException, SAXException {
306 
307     // -- parse text of DTD document
308     DTDdocument dtd = parseDTD(reader);
309 
310     // -- convert DTD document object into its corresponding Schema object
311     Schema schema = convertDTDObjectToSchemaObject(dtd, targetNameSpace, nameSpaceMap);
312 
313     // -- marshal Schema object into its corresponding XML document
314     marshalSchema(schema, writer);
315 
316   } // -- convertDTDtoSchema
317 
318   /**
319    * Convert DTD document to corresponding XML Schema document.
320    * 
321    * @param reader reader of the input DTD document.
322    * @param writer writer to the output Schema document.
323    *
324    * @throws DTDException if the DTD document is syntactically or semanticly not correct.
325    * @throws SchemaException if Schema object can not be created.
326    * @throws IOException if there is an I/O problem with the <tt>reader</tt> or <tt>writer</tt>.
327    * @throws SAXException if an error occured during schema object marshalling.
328    */
329   public void convertDTDtoSchema(Reader reader, Writer writer)
330       throws DTDException, SchemaException, IOException, SAXException {
331 
332     String targetNameSpace = DEFAULT_NAME_SPACE;
333     Map<String, String> nameSpaceMap = new HashMap<String, String>();
334 
335     convertDTDtoSchema(reader, writer, targetNameSpace, nameSpaceMap);
336   }
337 
338   /**
339    * Parses text of a DTD document and returns corresponding DTD document object. It is left to
340    * constructor of the <tt>reader</tt> to set up character encoding correctly. This means that
341    * method <u><font color="blue">read</font></u> of the <tt>reader</tt> is used to get next
342    * character, assuming it returns appropriate values.
343    * 
344    * @param reader input char stream reader. It is recommended to use class
345    *        {@link java.io.InputStreamReader java.io.InputStreamReader} as a <tt>reader</tt>, which
346    *        allows to set desired character encoding.
347    * @return DTD document object corresponding to the input text of a DTD document.
348    * @throws DTDException if the DTD document is syntactically or semanticly not correct.
349    */
350   public DTDdocument parseDTD(Reader reader) throws DTDException {
351     try {
352       // -- instantiate char stream for initial parser from the input
353       // reader
354       InputCharStream charStream = new InputCharStream(reader);
355 
356       // -- instantiate initial parser
357       DTDInitialParser initialParser = new DTDInitialParser(charStream);
358 
359       // -- get result of initial parsing - DTD document with parameter
360       // -- entity references expanded
361       String intermedResult = initialParser.Input();
362 
363       // -- construct StringReader from the intermediate result of parsing
364       try (StringReader strReader = new StringReader(intermedResult)) {
365         // -- instantiate char stream for main parser
366         charStream = new InputCharStream(strReader);
367 
368         // -- instantiate main parser
369         DTDParser parser = new DTDParser(charStream);
370 
371         // -- parse intermediate result by the main parser
372         // -- and get corresponding DTD document oblect
373         DTDdocument dtd = parser.Input();
374 
375         // -- return DTD document object
376         return dtd;
377       }
378     } catch (TokenMgrError tme) {
379       String msg = tme.getMessage();
380       throw new DTDException("TokenMgrError" + (msg == null ? "" : ": " + msg));
381     } catch (ParseException pe) {
382       String msg = pe.getMessage();
383       throw new DTDException("ParseException" + (msg == null ? "" : ": " + msg));
384     }
385   } // -- parseDTD
386 
387   /**
388    * Convert DTD document object to corresponding Schema object.
389    * 
390    * @param dtd input XML DTD document object.
391    * @param nameSpacePrefix
392    * 
393    * @param nameSpace
394    * 
395    * @return corresponding XML Schema object.
396    * @throws DTDException if the input DTD document is malformed.
397    * @throws SchemaException if Schema object can not be created.
398    */
399   public Schema convertDTDObjectToSchemaObject(DTDdocument dtd, String targetNamespace,
400       Map<String, String> nameSpaceMap) throws DTDException, SchemaException {
401 
402     Schema schema = new Schema();
403 
404     String name = dtd.getName();
405     if (name != null && !name.equals("")) {
406       schema.setId(name);
407     }
408 
409     schema.setTargetNamespace(targetNamespace);
410 
411     for (Map.Entry<String, String> entry : nameSpaceMap.entrySet()) {
412       schema.addNamespace(entry.getKey(), entry.getValue());
413     }
414 
415     // convert Notation declarations
416     Enumeration<Notation> dtdNotations = dtd.getNotations();
417 
418     while (dtdNotations.hasMoreElements()) {
419       dtdNotations.nextElement();
420       // do nothing for now as the Castor Schema object model does not
421       // support Notation declarations
422     } // -- convert Notations declarations
423 
424     // convert General Entity declarations.
425     // XML Schema does not provide facilities analogous to General Entity
426     // declarations in XML DTD, so we convert each General Entity
427     // declaration
428     // to Documentation subelement of XML Schema document annotaion.
429     Enumeration<GeneralEntity> dtdGeneralEntities = dtd.getGeneralEntities();
430     if (dtdGeneralEntities.hasMoreElements()) {
431       Annotation annotation = new Annotation();
432 
433       while (dtdGeneralEntities.hasMoreElements()) {
434         GeneralEntity ge = dtdGeneralEntities.nextElement();
435         Documentation documentation = new Documentation();
436 
437         String text = "General Entity Declaration";
438         documentation.add(text);
439         documentation.add(ge);
440         annotation.addDocumentation(documentation);
441       }
442 
443       schema.addAnnotation(annotation);
444     }
445     // -- convert General Entity declarations
446 
447     // convert Element declarations
448     Enumeration<Element> dtdElements = dtd.getElements();
449     while (dtdElements.hasMoreElements()) {
450       Element dtdElement = dtdElements.nextElement();
451       ElementDecl schemaElement = convertDTDElementToSchemaElement(dtdElement, schema);
452       schema.addElementDecl(schemaElement);
453     } // -- convert Element declarations
454 
455     return schema;
456 
457   } // -- convertDTDObjectToSchemaObject
458 
459   /**
460    * Convert DTD Element declaration to Schema Element Declaration.
461    * 
462    * @param dtdElement DTD Element declaration.
463    * @param schema Schema owning Element declaration.
464    * @throws DTDException if the input DTD Element Declaration is malformed.
465    * @throws SchemaException if unable to construct return
466    *         {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl} object from the input DTD
467    *         {@link org.exolab.castor.xml.dtd.Element Element} object.
468    * @return corresponding Schema Element declaration.
469    */
470   public ElementDecl convertDTDElementToSchemaElement(Element dtdElement, Schema schema)
471       throws DTDException, SchemaException {
472 
473     String name = dtdElement.getName();
474     if (name == null || name.equals("")) {
475       String err = "DTD to Schema converter: a DTD element has no name.";
476       throw new DTDException(err);
477     }
478     ElementDecl schemaElement = new ElementDecl(schema, name);
479 
480     // start converting content of the element
481     ComplexType complexType = schema.createComplexType();
482     ContentType contentType = null;
483     Group group = null; // auxiliary
484     Iterator mixedChildrenIterator = null; // auxiliary
485     String elementRef = null; // auxiliary
486     ElementDecl elem = null; // auxiliary
487 
488     if (dtdElement.isEmptyContent()) {
489 
490       // mixed="false"
491       contentType = ContentType.elemOnly;
492       // -- mixed="false"
493 
494     } else if (dtdElement.isAnyContent()) {
495 
496       // mixed="true"
497       contentType = ContentType.mixed;
498       // -- mixed="true"
499 
500       group = new Group();
501       group.setOrder(Order.sequence);
502       group.setMinOccurs(0);
503       group.setMaxOccurs(-1);
504       Wildcard any = new Wildcard(group);
505       group.addWildcard(any);
506       complexType.addGroup(group);
507 
508     } else if (dtdElement.isElemOnlyContent()) {
509 
510       // mixed="false"
511       contentType = ContentType.elemOnly;
512       // -- mixed="false"
513 
514       ContentParticle dtdContent = dtdElement.getContent();
515       if (dtdContent == null) { // content is not specified
516         String err = "DTD to Schema converter: element \"" + dtdElement.getName();
517         err += "\" has no content.";
518         throw new DTDException(err);
519       }
520 
521       Particle content = null;
522       try {
523         content = convertContentParticle(dtdContent, schema);
524       } catch (DTDException e) {
525         String err = "DTD to Schema converter: content of DTD element \"" + dtdElement.getName();
526         err += "\", represented by a Content Particle, is malformed.";
527         throw new DTDException(err);
528       }
529 
530       if (content instanceof ElementDecl) {
531         group = new Group();
532         group.setOrder(Order.sequence);
533         group.addElementDecl((ElementDecl) content);
534         complexType.addGroup(group);
535       } else {
536         complexType.addGroup((Group) content);
537       }
538 
539     } else if (dtdElement.isMixedContent()) {
540 
541       // mixed="true"
542       contentType = ContentType.mixed;
543       // -- mixed="true"
544 
545       mixedChildrenIterator = dtdElement.getMixedContentChildren();
546       if ((mixedChildrenIterator != null) && (mixedChildrenIterator.hasNext())) {
547         group = new Group();
548         group.setOrder(Order.choice);
549         group.setMinOccurs(0);
550         group.setMaxOccurs(-1);
551         while (mixedChildrenIterator.hasNext()) {
552           elementRef = (String) mixedChildrenIterator.next();
553           elem = new ElementDecl(schema);
554           elem.setReferenceName(elementRef);
555           group.addElementDecl(elem);
556         }
557         complexType.addGroup(group);
558       }
559 
560     } else { // the type of the element has not been specified
561       String err = "DTD to Schema converter: content type of DTD element \"" + dtdElement.getName()
562           + "\" has not been specified.";
563       throw new DTDException(err);
564     }
565     complexType.setContentType(contentType);
566     // finish converting content of the element
567 
568     // start attributes convertion
569     Enumeration<Attribute> dtdAttributes = dtdElement.getAttributes();
570     while (dtdAttributes.hasMoreElements()) {
571       Attribute dtdAttribute = dtdAttributes.nextElement();
572       AttributeDecl schemaAttribute = convertAttribute(dtdAttribute, schema);
573       complexType.addAttributeDecl(schemaAttribute);
574     }
575     // end attributes convertion
576 
577     schemaElement.setType(complexType);
578     return schemaElement;
579 
580   } // -- convertDTDElementToSchemaElement
581 
582   /**
583    * Method to convert {@link org.exolab.castor.xml.dtd.ContentParticle ContentParticle} object,
584    * used to implement element content in the DTD object model, to the corresponding object in the
585    * Schema object model: either {@link org.exolab.castor.xml.schema.Group Group} or
586    * {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl}.
587    * 
588    * @param dtdContent input {@link org.exolab.castor.xml.dtd.ContentParticle ContentParticle}
589    *        object.
590    * @return object returned is an instance of either {@link org.exolab.castor.xml.schema.Group
591    *         Group} class or {@link org.exolab.castor.xml.schema.ElementDecl ElementDecl} class.
592    * @throws DTDException if the input ContentParticle is malformed.
593    * @throws SchemaException if unable to construct return content object from a given
594    *         ContentParticle
595    */
596   public Particle convertContentParticle(ContentParticle dtdContent, Schema schema)
597       throws DTDException, SchemaException {
598 
599     Particle returnValue;
600 
601     if (dtdContent.isReferenceType()) {
602 
603       ElementDecl elem = new ElementDecl(schema);
604       elem.setReferenceName(dtdContent.getReference());
605       returnValue = elem;
606 
607     } else if (dtdContent.isSeqType() || dtdContent.isChoiceType()) {
608 
609       Group group = new Group();
610       if (dtdContent.isSeqType())
611         group.setOrder(Order.sequence);
612       else
613         group.setOrder(Order.choice);
614 
615       Enumeration<ContentParticle> children = dtdContent.getChildren();
616       while (children.hasMoreElements()) {
617         ContentParticle child = children.nextElement();
618         Particle contentParticle = convertContentParticle(child, schema);
619 
620         if (contentParticle instanceof ElementDecl) {
621           group.addElementDecl((ElementDecl) contentParticle);
622         } else {
623           group.addGroup((Group) contentParticle);
624         }
625       }
626 
627       returnValue = group;
628 
629     } else { // -- type of input DTD Content Particle is not specified
630       throw new DTDException();
631     }
632 
633     if (dtdContent.isOneOccurance()) {
634       returnValue.setMinOccurs(1);
635       returnValue.setMaxOccurs(1);
636     } else if (dtdContent.isOneOrMoreOccurances()) {
637       returnValue.setMinOccurs(1);
638       returnValue.setMaxOccurs(-1);
639     } else if (dtdContent.isZeroOrMoreOccurances()) {
640       returnValue.setMinOccurs(0);
641       returnValue.setMaxOccurs(-1);
642     } else if (dtdContent.isZeroOrOneOccurance()) {
643       returnValue.setMinOccurs(0);
644       returnValue.setMaxOccurs(1);
645     } else {
646       // a content particle always has "one occurance" default
647       // occurance specification
648     }
649 
650     return returnValue;
651 
652   } // -- convertContentParticle
653 
654   /**
655    * Convert DTD Attribute declaration to Schema Attribute Declaration.
656    * 
657    * @param dtdAttribute DTD Attribute declaration.
658    * @param schema Schema owning Element of this Attribute.
659    * @throws DTDException if the input DTD Attribute Declaration is malformed.
660    * @return corresponding Schema Attribute declaration.
661    */
662   public AttributeDecl convertAttribute(Attribute dtdAttribute, Schema schema) throws DTDException {
663 
664     AttributeDecl schemaAttribute = new AttributeDecl(schema, dtdAttribute.getName());
665 
666     SimpleType type = null;
667 
668     if (dtdAttribute.isStringType()) {
669       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.STRING_TYPE));
670     } else if (dtdAttribute.isIDType()) {
671       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.ID_TYPE));
672     } else if (dtdAttribute.isIDREFType()) {
673       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.IDREF_TYPE));
674     } else if (dtdAttribute.isIDREFSType()) {
675       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.IDREFS_TYPE));
676     } else if (dtdAttribute.isENTITYType()) {
677       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.ENTITY_TYPE));
678     } else if (dtdAttribute.isENTITIESType()) {
679       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.ENTITIES_TYPE));
680     } else if (dtdAttribute.isNMTOKENType()) {
681       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKEN_TYPE));
682     } else if (dtdAttribute.isNMTOKENSType()) {
683       type = schema.getSimpleType(schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKENS_TYPE));
684     } else if (dtdAttribute.isNOTATIONType()) {
685       type = schema.createSimpleType(null,
686           schema.getBuiltInTypeName(SimpleTypesFactory.NOTATION_TYPE), "restriction");
687       Iterator<String> values = dtdAttribute.getValues();
688       FacetFactory facetFactory = FacetFactory.getInstance();
689       while (values.hasNext()) {
690         Facet facet = facetFactory.createFacet(Facet.ENUMERATION, values.next());
691         facet.setOwningType(type);
692         type.addFacet(facet);
693       }
694 
695     } else if (dtdAttribute.isEnumerationType()) {
696       type = schema.createSimpleType(null,
697           schema.getBuiltInTypeName(SimpleTypesFactory.NMTOKEN_TYPE), "restriction");
698       Iterator<String> values = dtdAttribute.getValues();
699       FacetFactory facetFactory = FacetFactory.getInstance();
700       while (values.hasNext()) {
701         Facet facet = facetFactory.createFacet(Facet.ENUMERATION, values.next());
702         facet.setOwningType(type);
703         type.addFacet(facet);
704       }
705     } else {
706       String err = "DTD to Schema converter: DTD attribute \"" + dtdAttribute.getName()
707           + "\" has unspecified type.";
708       throw new DTDException(err);
709     }
710 
711     schemaAttribute.setSimpleType(type);
712 
713     if (dtdAttribute.isREQUIRED()) {
714       schemaAttribute.setUse(AttributeDecl.USE_REQUIRED);
715     } else if (dtdAttribute.isIMPLIED()) {
716       schemaAttribute.setUse(AttributeDecl.USE_OPTIONAL);
717     } else if (dtdAttribute.isFIXED()) {
718       //
719     } else { // DTD attribute is of "DEFAULT" type
720       schemaAttribute.setDefaultValue(dtdAttribute.getDefaultValue());
721     }
722 
723     return schemaAttribute;
724   } // -- convertAttribute
725 
726   /**
727    * Marshals XML Schema to output char stream.
728    * 
729    * @param schema XML Schema object to marshal.
730    * @param writer output char stream to marshal Schema to.
731    * @throws IOException if there is an I/O problem with the <tt>writer</tt>.
732    * @throws SAXException if an error occured during <tt>schema</tt> marshalling.
733    */
734   public void marshalSchema(Schema schema, Writer writer) throws IOException, SAXException {
735 
736     SchemaWriter sw = new SchemaWriter(writer);
737     sw.write(schema);
738   } // -- marshalSchema
739 
740 } // -- Converter