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