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 1999-2003 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * This file was originally developed by Keith Visco during the
44   * course of employment at Intalio Inc.
45   * All portions of this file developed by Keith Visco after Jan 19 2005 are
46   * Copyright (C) 2005 Keith Visco. All Rights Reserved.
47   *
48   * $Id$
49   */
50  package org.exolab.castor.builder;
51  
52  import java.io.File;
53  import java.io.FileOutputStream;
54  import java.io.FileReader;
55  import java.io.FileWriter;
56  import java.io.IOException;
57  import java.io.Reader;
58  import java.util.Enumeration;
59  import java.util.Iterator;
60  import java.util.Properties;
61  import java.util.Vector;
62  
63  import org.apache.commons.logging.Log;
64  import org.apache.commons.logging.LogFactory;
65  import org.castor.core.exceptions.CastorRuntimeException;
66  import org.castor.xml.BackwardCompatibilityContext;
67  import org.castor.xml.InternalContext;
68  import org.exolab.castor.builder.binding.BindingException;
69  import org.exolab.castor.builder.binding.BindingLoader;
70  import org.exolab.castor.builder.binding.ExtendedBinding;
71  import org.exolab.castor.builder.binding.XMLBindingComponent;
72  import org.exolab.castor.builder.binding.XPathHelper;
73  import org.exolab.castor.builder.binding.xml.PackageType;
74  import org.exolab.castor.builder.binding.xml.PackageTypeChoice;
75  import org.exolab.castor.builder.binding.xml.types.BindingType;
76  import org.exolab.castor.builder.conflict.strategy.ClassNameConflictResolver;
77  import org.exolab.castor.builder.conflict.strategy.TypeClassNameConflictResolver;
78  import org.exolab.castor.builder.conflict.strategy.XPATHClassNameConflictResolver;
79  import org.exolab.castor.builder.conflictresolution.WarningViaDialogClassNameCRStrategy;
80  import org.exolab.castor.builder.factory.FieldInfoFactory;
81  import org.exolab.castor.builder.factory.SourceFactory;
82  import org.exolab.castor.builder.info.ClassInfo;
83  import org.exolab.castor.builder.printing.JClassPrinter;
84  import org.exolab.castor.builder.printing.JClassPrinterFactory;
85  import org.exolab.castor.builder.printing.JClassPrinterFactoryRegistry;
86  import org.exolab.castor.mapping.xml.MappingRoot;
87  import org.exolab.castor.util.NestedIOException;
88  import org.exolab.castor.util.Version;
89  import org.exolab.castor.util.dialog.ConsoleDialog;
90  import org.exolab.castor.xml.Marshaller;
91  import org.exolab.castor.xml.ValidationException;
92  import org.exolab.castor.xml.XMLException;
93  import org.exolab.castor.xml.schema.AttributeDecl;
94  import org.exolab.castor.xml.schema.ComplexType;
95  import org.exolab.castor.xml.schema.ContentModelGroup;
96  import org.exolab.castor.xml.schema.ContentType;
97  import org.exolab.castor.xml.schema.ElementDecl;
98  import org.exolab.castor.xml.schema.Facet;
99  import org.exolab.castor.xml.schema.Group;
100 import org.exolab.castor.xml.schema.ModelGroup;
101 import org.exolab.castor.xml.schema.Schema;
102 import org.exolab.castor.xml.schema.SchemaContext;
103 import org.exolab.castor.xml.schema.SchemaContextImpl;
104 import org.exolab.castor.xml.schema.SimpleContent;
105 import org.exolab.castor.xml.schema.SimpleType;
106 import org.exolab.castor.xml.schema.Structure;
107 import org.exolab.castor.xml.schema.XMLType;
108 import org.exolab.castor.xml.schema.reader.Sax2ComponentReader;
109 import org.exolab.castor.xml.schema.reader.SchemaUnmarshaller;
110 import org.exolab.javasource.JClass;
111 import org.xml.sax.InputSource;
112 import org.xml.sax.Parser;
113 import org.xml.sax.SAXParseException;
114 
115 /**
116  * A Java Source generation tool which uses XML Schema definitions
117  * to create an Object model.
118  *
119  * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> - Main author.
120  * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a> - Contributions.
121  * @author <a href="mailto:nsgreen@thazar.com">Nathan Green</a> - Contributions.
122  * @version $Revision$ $Date: 2006-03-30 14:58:45 -0700 (Thu, 30 Mar 2006) $
123  */
124 public class SourceGenerator extends BuilderConfiguration {
125 
126     /** Jakarta's common-logging logger. */
127     private static final Log LOG = LogFactory.getLog(SourceGenerator.class);
128 
129     //-------------/
130     //- Constants -/
131     //-------------/
132     /** The application name. */
133     static final String APP_NAME = "Castor";
134     /** The application description. */
135     static final String APP_DESC = "XML data binder for Java";
136     /** The application version. */
137     static final String VERSION = Version.VERSION;
138     /** The application URI. */
139     static final String APP_URI = "http://www.castor.org";
140     /** Warning message to remind users to create source code for imported schema. */
141     private static final String IMPORT_WARNING = 
142         "Note: No code will be generated for the following *imported* schema: ";
143 
144     //----------------------/
145     //- Instance Variables -/
146     //----------------------/
147 
148     /** Castor internal context - mother of all. */
149     private final InternalContext _internalContext;
150     /** The XMLBindingComponent used to create Java classes from an XML Schema. */
151     private final XMLBindingComponent _bindingComponent;
152     /** Our object used to generate source for a single source file. */
153     private final SingleClassGenerator _singleClassGenerator;
154     /** The field info factory. */
155     private final FieldInfoFactory _infoFactory;
156     /** Allows us to ask the user questions. */
157     private final ConsoleDialog _dialog;
158     /** A vector that keeps track of all the schemas processed. */
159     private final Vector<Schema> _schemasProcessed = new Vector<Schema>(7);
160     
161     /** True if we should suppress non-fatal warnings. */
162     private boolean _suppressNonFatalWarnings = false;
163     /** Determines whether or not to print extra messages. */
164     private boolean _verbose = false;
165     
166     /** 
167      * A flag indicating whether or not to create XML-specific class
168      * descriptors for the generated classes.
169      */
170     private boolean _createDescriptors = true;
171     /** 
172      * A flag indicating whether or not to create JDO-specific class
173      * descriptors for the generated classes.
174      */
175     private boolean _createJdoDescriptors = false;
176     
177     /** A flag indicating whether or not to generate sources
178      *  for imported XML Schemas. */
179     private boolean _generateImported = false;
180     /** The source factory. */
181     private SourceFactory _sourceFactory = null;
182     /** A flag to indicate that the mapping file should be generated. */
183     private boolean _generateMapping = false;
184     /** The name of the mapping file to create used with the gen-mapping flag. */
185     private String  _mappingFilename = "mapping.xml";
186     /** A flag indicating whether or not to generate XML marshalling
187      *  framework specific methods. */
188     private boolean _createMarshalMethods = true;
189     /** A flag indicating whether or not to implement CastorTestable
190      *  (used by the Castor Testing Framework). */
191     private boolean _testable = false;
192     /** A flag indicating that SAX1 should be used when generating the source. */
193     private boolean _sax1 = false;
194     /** A flag indicating that enumerated types should be constructed to perform
195      *  case insensitive lookups based on the values. */
196     private boolean _caseInsensitive = false;
197     /** A flag indicating, if true, that source generation should fail on the
198      * first error. */
199     private boolean _failOnFirstError = false;
200     /** A GroupNaming helper class used to named anonymous groups. */
201     private GroupNaming _groupNaming = null;
202     /** Strategy for name conflict resolution. */
203     private String _nameConflictStrategy = WarningViaDialogClassNameCRStrategy.NAME;
204     /** JClass to XPATH registry, used for class name conflict resolution. */
205     private JClassRegistry _xmlInfoRegistry;
206 
207     /**
208      * Strategy implementation for resolving class name conflicts.
209      */
210     private ClassNameConflictResolver _conflictResolver = 
211         new XPATHClassNameConflictResolver();
212 
213     /**
214      * The default type of the {@link JClassPrinterFactory} to use for instantiating
215      * instances of {@link JClassPrinter} instances.
216      */
217     private String _jclassPrinterType = "standard";
218 
219     /**
220      * {@link JClassPrinterFactoryRegistry} instance to be used for obtaining instances
221      * of {@link JClassPrinterFactory} instances.
222      */
223     private JClassPrinterFactoryRegistry _jclassPrinterFactoryRegistry;
224 
225     protected SGStateInfo _sInfo;
226 
227     /**
228      * Creates a SourceGenerator using the default FieldInfo factory.
229      */
230     public SourceGenerator() {
231         this(null);
232     } //-- SourceGenerator
233 
234     /**
235      * Creates a SourceGenerator using the specific field info Factory.
236      *
237      * @param infoFactory the FieldInfoFactory to use.
238      */
239     public SourceGenerator(final FieldInfoFactory infoFactory) {
240         this(infoFactory, null);
241     }
242 
243     /**
244      * Creates a SourceGenerator using the specific field info Factory and the
245      * given Binding element.
246      *
247      * @param infoFactory the FieldInfoFactory to use.
248      * @param binding the binding element to use.
249      */
250     public SourceGenerator(final FieldInfoFactory infoFactory, final ExtendedBinding binding) {
251         super();
252 
253         _internalContext = new BackwardCompatibilityContext();
254 
255         setJavaNaming(_internalContext.getJavaNaming());
256         _dialog = new ConsoleDialog();
257         if (infoFactory == null) {
258            if (useOldFieldNaming()) {
259               _infoFactory = new FieldInfoFactory();
260            } else {
261               _infoFactory = new FieldInfoFactory(false);
262            }
263         } else {
264            _infoFactory = infoFactory;
265         }
266 
267         super.load();
268 
269         _groupNaming = new GroupNaming(getJavaNaming());
270 
271         _jclassPrinterFactoryRegistry = new JClassPrinterFactoryRegistry(this);
272         _singleClassGenerator = new SingleClassGenerator(_dialog, this, _nameConflictStrategy, 
273                 _jclassPrinterType);
274         _bindingComponent = new XMLBindingComponent(this, _groupNaming);
275         setBinding(binding);
276         
277         _conflictResolver.setSourceGenerator(this);
278         _xmlInfoRegistry = new JClassRegistry(_conflictResolver, getJavaNaming());
279     } //-- SourceGenerator
280 
281     /**
282      * Returns the selected {@link JClassPrinter} type, as defined by the list of 
283      * {@link JClassPrinterFactory} instances enlisted in the Castor XML code generator
284      * property file.
285      * @return the selected {@link JClassPrinter} type.
286      */
287     private String getJClassPrinterType() {
288         return _jclassPrinterType;
289     }
290 
291     /**
292      * Sets the filename of the mapping file.
293      * @param filename filename of the mapping file
294      */
295     public final void setMappingFilename(final String filename) {
296         _mappingFilename = filename;
297     }
298 
299     /**
300      * Sets the strategy for handling name conflicts.
301      *
302      * @param nameConflictStrategy the name of the stretegy to use for handling
303      *        name conflicts.
304      */
305     public final void setNameConflictStrategy(final String nameConflictStrategy) {
306         _nameConflictStrategy = nameConflictStrategy;
307         _singleClassGenerator.setNameConflictStrategy(nameConflictStrategy);
308     }
309     /**
310      * Returns the version number of this SourceGenerator.
311      *
312      * @return the version number of this SourceGenerator
313      */
314     public static String getVersion() {
315         return VERSION;
316     } //-- getVersion
317 
318     /**
319      * Set to true if SAX1 should be used in the marshal method.
320      * @param sax1 true if SAX1 should be used in the marshal method
321      */
322     public final void setSAX1(final boolean sax1) {
323         _sax1 = sax1;
324     }
325 
326     /**
327      * Set to true if enumerated type lookups should be performed in a case
328      * insensitive manner.
329      *
330      * @param caseInsensitive when true, enumerated type lookups will be
331      *        performed in a case insensitive manner.
332      */
333     public final void setCaseInsensitive(final boolean caseInsensitive) {
334         _caseInsensitive = caseInsensitive;
335     }
336 
337     /**
338      * If true, the source generator will fail on the first error encountered.
339      * Otherwise, the source generator will continue on certain errors.
340      *
341      * @param failOnFirstError if true, the source generator will fail on the
342      *        first error encountered.
343      */
344     public final void setFailOnFirstError(final boolean failOnFirstError) {
345         _failOnFirstError = failOnFirstError;
346     }
347 
348     /**
349      * Sets whether or not to suppress non-fatal warnings encountered during
350      * source generation.
351      * @param suppress true if non-fatal warnings should be suppressed.
352      */
353     public final void setSuppressNonFatalWarnings(final boolean suppress) {
354         _singleClassGenerator.setPromptForOverwrite(!suppress);
355         _suppressNonFatalWarnings = suppress;
356     } //-- setSuppressNonFatalWarnings
357 
358     /**
359      * Sets whether or not the source code generator prints additional messages
360      * during generating source code.
361      *
362      * @param verbose a boolean, when true indicates to print additional
363      *        messages
364      */
365     public void setVerbose(final boolean verbose) {
366         _verbose = verbose;
367     } //-- setVerbose
368 
369     /**
370      * Sets the ClassNameConflictResolver instance to be used for automatic class name
371      * conflict resolution.
372     *
373      * @param resolverName The name of the resolver to be used for automatic class name
374      * conflict resolution
375      */
376     public void setClassNameConflictResolver(final String resolverName) {
377         if (resolverName.equals("type")) {
378             _conflictResolver = new TypeClassNameConflictResolver();
379             _conflictResolver.setSourceGenerator(this);
380         } else if (resolverName.equals("xpath")) {
381             // leave default
382         } else {
383             throw new IllegalArgumentException("Invalid resolver type.");
384         }
385         _xmlInfoRegistry.setClassNameConflictResolver(_conflictResolver);
386     } //-- setClassNameConflictResolver
387     
388     /**
389      * Sets whether or not to create ClassDescriptors for the generated classes.
390      * By default, descriptors are generated.
391      *
392      * @param createDescriptors a boolean, when true indicates to generated
393      *        ClassDescriptors
394      *
395      */
396     public final void setDescriptorCreation(final boolean createDescriptors) {
397         _createDescriptors = createDescriptors;
398         _singleClassGenerator.setDescriptorCreation(createDescriptors);
399     } //-- setDescriptorCreation
400 
401     /**
402      * Sets whether or not to create JDO-specific class descriptors for the 
403      * generated classes.
404      * By default, JDO-specific class descriptors are NOT generated.
405      *
406      * @param createJdoDescriptors a boolean, when true indicates to generated
407      *        JDO-specific class descriptors
408      *
409      */
410     public final void setJdoDescriptorCreation(final boolean createJdoDescriptors) {
411         _createJdoDescriptors = createJdoDescriptors;
412         _singleClassGenerator.setJdoDescriptorCreation(createJdoDescriptors);
413     } //-- setDescriptorCreation
414 
415     /**
416      * Sets the destination directory.
417      *
418      * @param destDir the destination directory.
419      */
420     public final void setDestDir(final String destDir) {
421         _singleClassGenerator.setDestDir(destDir);
422     }
423 
424     /**
425      * Sets the destination directory for resources, e.g. '.castor.cdr' files.
426      *
427      * @param destDir the destination directory for resources.
428      */
429     public final void setResourceDestination(final String destination) {
430         _singleClassGenerator.setResourceDestinationDirectory(destination);
431     }
432 
433     /**
434      * Sets whether or not to create the XML marshaling framework specific
435      * methods (marshal, unmarshal, validate) in the generated classes. By
436      * default, these methods are generated.
437      *
438      * @param createMarshalMethods a boolean, when true indicates to generated
439      *        the marshaling framework methods
440      */
441     public final void setCreateMarshalMethods(final boolean createMarshalMethods) {
442         _createMarshalMethods = createMarshalMethods;
443     } //-- setCreateMarshalMethods
444 
445     /**
446      * Sets whether or not to generate Java sources for imported XML Schema.
447      * By default Java sources for imported XML schemas are not generated.
448      *
449      * @param generate true to generate the java classes for the imported XML Schema
450      */
451     public final void setGenerateImportedSchemas(final boolean generate) {
452         _generateImported = generate;
453     }
454 
455     /**
456      * Sets whether or not a mapping file should be generated, this is false by
457      * default. Note that this will only be used when generation of descriptors
458      * has been disabled.
459      *
460      * @param generateMapping a flag that indicates whether or not a mapping
461      *        file should be generated.
462      */
463     public final void setGenerateMappingFile(final boolean generateMapping) {
464         _generateMapping = generateMapping;
465     } //-- setGenerateMappingFile
466 
467     /**
468      * Sets whether or not to implement CastorTestable.
469      *
470      * @param testable
471      *            a boolean, when true indicates to implement CastorTestable
472      */
473     public final void setTestable(final boolean testable) {
474         _testable = testable;
475     } //-- setTestable
476 
477     /**
478      * Sets the binding to use with this instance of the SourceGenerator.
479      *
480      * @param binding
481      *            the binding to use, null indicates that the default binding
482      *            will be used.
483      */
484     public final void setBinding(final ExtendedBinding binding) {
485         if (binding != null) {
486             processNamespaces(binding.getPackage());
487         }
488         //--initialize the XMLBindingComponent
489         _bindingComponent.setBinding(binding);
490     } //-- setBinding
491 
492     /**
493      * Sets the binding to use given the path name of a Castor Binding File.
494      *
495      * @param fileName the file that represents a Binding
496      */
497     public final void setBinding(final String fileName) {
498         try {
499             ExtendedBinding binding = BindingLoader.createBinding(fileName);
500             setBinding(binding);
501         } catch (BindingException e) {
502             String err = "Unable to load a binding file due to the following:\n"
503                     + e.getMessage()
504                     + "\nThe Source Generator will continue with no binding file.";
505             _dialog.notify(err);
506         }
507     }
508 
509     /**
510      * Sets the binding to use given an InputSource identifying
511      * a Castor Binding File.
512      *
513      * @param source an InputSource identifying a Castor Binding File.
514      */
515     public final void setBinding(final InputSource source) {
516         try {
517             ExtendedBinding binding = BindingLoader.createBinding(source);
518             setBinding(binding);
519         } catch (BindingException e) {
520             String err = "unable to load a binding file due to the following:\n"
521                     + e.getMessage()
522                     + "\nThe Source Generator will continue with no binding file.";
523             _dialog.notify(err);
524         }
525     }
526 
527     /**
528      * Sets the line separator to use when printing the source code.
529      * <p>
530      * <B>Note:</B>This can be any string, so be careful. I recommend either
531      * using the default or using one of the following:
532      *
533      * <PRE>
534      * windows systems use: "\r\n"
535      * unix systems use: "\n"
536      * mac systems use: "\r"
537      * </PRE>
538      *
539      * @param lineSeparator the line separator to use when printing the source
540      *        code. This method is useful if you are generating source on one
541      *        platform, but will be compiling the source on a different
542      *        platform.
543      */
544     public final void setLineSeparator(final String lineSeparator) {
545         _singleClassGenerator.setLineSeparator(lineSeparator);
546     } //-- setLineSeparator
547 
548     /**
549      * Tests the org.exolab.castor.builder.javaclassmapping property for the
550      * 'element' value.
551      *
552      * @return True if the Source Generator is mapping schema elements to Java
553      *         classes.
554      */
555     public final boolean mappingSchemaElement2Java() {
556         if (_bindingComponent != null) {
557             ExtendedBinding binding = _bindingComponent.getBinding();
558             if (binding != null) {
559                 BindingType type = binding.getDefaultBindingType();
560                 if (type != null) {
561                     return (type == BindingType.ELEMENT);
562                 }
563             }
564         }
565         return super.mappingSchemaElement2Java();
566     } //-- mappingSchemaElement2Java
567 
568     /**
569      * Tests the org.exolab.castor.builder.javaclassmapping property for the
570      * 'type' value.
571      *
572      * @return True if the Source Generator is mapping schema types to Java
573      *         classes.
574      */
575     public final boolean mappingSchemaType2Java() {
576         if (_bindingComponent != null) {
577             ExtendedBinding binding = _bindingComponent.getBinding();
578             if (binding != null) {
579                 BindingType type = binding.getDefaultBindingType();
580                 if (type != null) {
581                     return (type == BindingType.TYPE);
582                 }
583             }
584         }
585         return super.mappingSchemaType2Java();
586     } //-- mappingSchemaType2Java
587 
588     /**
589      * Creates Java Source code (Object model) for the given XML Schema. If the
590      * file exists, opens a FileReader and passes control to
591      * {@link #generateSource(InputSource, String)}.
592      *
593      * @param filename the full path to the XML Schema definition
594      * @param packageName the package for the generated source files
595      * @throws IOException if an IOException occurs writing the new source files
596      */
597     public final void generateSource(
598             final String filename, final String packageName) throws IOException {
599         final File schemaFile;
600         if (filename.startsWith("./")) {
601             schemaFile = new File(filename.substring(2));
602         } else {
603             schemaFile = new File(filename);
604         }
605 
606         FileReader reader = new FileReader(schemaFile);
607 
608         try {
609             InputSource source = new InputSource(reader);
610             source.setSystemId(toURIRepresentation(schemaFile.getAbsolutePath()));
611             generateSource(source, packageName);
612         } finally {
613             try { reader.close(); } catch (java.io.IOException iox) {
614                 // ignore
615             }
616         }
617     } //-- generateSource
618 
619     /**
620      * Creates Java Source code (Object model) for the given XML Schema. This
621      * method just passes control to
622      * {@link #generateSource(InputSource, String)}.
623      *
624      * @param reader the Reader with which to read the XML Schema definition.
625      *        The caller should close the reader, since thie method will not do
626      *        so.
627      * @param packageName the package for the generated source files
628      * @throws IOException if an IOException occurs writing the new source files
629      */
630     public final void generateSource(
631             final Reader reader, final String packageName) throws IOException {
632         InputSource source = new InputSource(reader);
633         generateSource(source, packageName);
634     } //-- generateSource
635 
636     /**
637      * Creates Java Source code (Object model) for the given XML Schema. Parses
638      * the schema provided by the InputSource and then calls
639      * {@link #generateSource(Schema, String)} to actually generate the source.
640      *
641      * @param source - the InputSource representing the XML schema.
642      * @param packageName the package for the generated source files
643      * @throws IOException if an IOException occurs writing the new source files
644      */
645     public void generateSource(final InputSource source, final String packageName)
646     throws IOException {
647         // -- get default parser from Configuration
648         Parser parser = null;
649         try {
650             parser = _internalContext.getParser();
651         } catch (RuntimeException rte) {
652             // ignore
653         }
654 
655         if (parser == null) {
656             _dialog.notify("fatal error: unable to create SAX parser.");
657             return;
658         }
659 
660         SchemaContext schemaContext = new SchemaContextImpl();
661         SchemaUnmarshaller schemaUnmarshaller = null;
662         try {
663            schemaUnmarshaller = new SchemaUnmarshaller(schemaContext);
664         } catch (XMLException e) {
665             //--The default constructor cannot throw exception so this should never happen
666             //--just log the exception
667             e.printStackTrace();
668             System.exit(1);
669         }
670 
671         Sax2ComponentReader handler = new Sax2ComponentReader(schemaUnmarshaller);
672         parser.setDocumentHandler(handler);
673         parser.setErrorHandler(handler);
674 
675         try {
676             parser.parse(source);
677         } catch (java.io.IOException ioe) {
678             _dialog.notify("error reading XML Schema file");
679             if (_failOnFirstError) {
680                 throw ioe;
681             }
682             return;
683         } catch (org.xml.sax.SAXException sx) {
684             Exception except = sx.getException();
685             if (except == null) {
686                 except = sx;
687             }
688 
689             if (except instanceof SAXParseException) {
690                 SAXParseException spe = (SAXParseException) except;
691                 _dialog.notify("SAXParseException: " + spe);
692                 _dialog.notify(" - occured at line ");
693                 _dialog.notify(Integer.toString(spe.getLineNumber()));
694                 _dialog.notify(", column ");
695                 _dialog.notify(Integer.toString(spe.getColumnNumber()));
696             } else {
697                 except.printStackTrace();
698             }
699             if (_failOnFirstError) {
700                 String msg = "Source Generator: schema parser threw an Exception";
701                 throw new CastorRuntimeException(msg, sx);
702             }
703             return;
704         }
705 
706         Schema schema = schemaUnmarshaller.getSchema();
707         
708         try {
709             schema.validate();
710         } catch (ValidationException vx) {
711             throw new NestedIOException(vx);
712         }
713         
714         generateSource(schema, packageName);
715     } //-- generateSource
716 
717     /**
718      * Creates Java Source code (Object model) for the given XML Schema.
719      * Convenience methods exist if you don't have a
720      * {@link org.exolab.castor.xml.schema.Schema} already parsed.
721      *
722      * @param schema the XML schema to generate the Java sources for.
723      * @param packageName the package for the generated source files.
724      * @throws IOException if this Exception occurs while generating source
725      * @see #generateSource(String, String) to provide the schema filename
726      * @see #generateSource(Reader, String) to provide a Reader for the schema
727      * @see #generateSource(InputSource, String) to provide an InputSource for
728      *      the schema
729      */
730     public final void generateSource(
731             final Schema schema, final String packageName) throws IOException {
732         if (schema == null) {
733             throw new IllegalArgumentException("The argument 'schema' must not be null.");
734         }
735 
736         //--make sure the XML Schema is valid
737         try {
738             schema.validate();
739         } catch (ValidationException ve) {
740             String err = "The schema: " + schema.getSchemaLocation() + " is not valid.\n"
741                          + ve.getMessage();
742             throw new IllegalArgumentException(err);
743         }
744 
745         // Now that we're ready to generate source and we know our configuration
746         // has been fully parsed, create our SourceFactory.  (See CASTOR-1346.)
747         // We will reuse this SourceFactory if we are invoked multiple times.
748         if (_sourceFactory == null) {
749             _sourceFactory = new SourceFactory(this, _infoFactory, _groupNaming, this);
750             _sourceFactory.setCreateMarshalMethods(_createMarshalMethods);
751             _sourceFactory.setTestable(_testable);
752             _sourceFactory.setSAX1(_sax1);
753             _sourceFactory.setCaseInsensitive(_caseInsensitive);
754         }
755 
756         _sInfo = new SGStateInfo(schema, this);
757         _sInfo.setPackageName(packageName);
758         _sInfo.setDialog(_dialog);
759         _sInfo.setVerbose(_verbose);
760         _sInfo.setSuppressNonFatalWarnings(_suppressNonFatalWarnings);
761 
762         //--map the schemaLocation of the schema with the packageName defined
763         if (packageName != null) {
764             super.setLocationPackageMapping(schema.getSchemaLocation(), packageName);
765         }
766 
767         //--We start with a blank list of schemas processed
768         _schemasProcessed.clear();
769 
770         generateAllClassFiles(schema, _sInfo);
771 
772         //-- TODO Cleanup integration (what does this comment mean?)
773         if (!_createDescriptors && _generateMapping) {
774             generateMappingFile(packageName, _sInfo);
775         }
776         
777         // output statistical information from JClassRegistry in 'automatic'mode only
778         if (isAutomaticConflictResolution()) {
779             _xmlInfoRegistry.printStatistics(_bindingComponent);
780         }
781     } //-- generateSource
782 
783     //-------------------/
784     //- Private Methods -/
785     //-------------------/
786 
787     /**
788      * Generate all class files for the provided schema.  Before processing
789      * the current schema, process the schemas imported by this one (unless our
790      * configuration says not to).
791      *
792      * @param schema the schema whose imported schemas to process
793      * @param sInfo source generator state information
794      * @throws IOException if this Exception occurs while processing an import schema
795      */
796     private void generateAllClassFiles(final Schema schema, final SGStateInfo sInfo)
797                                                                  throws IOException {
798         // Before processing the current schema, process its imported schemas
799         processImportedSchemas(schema, sInfo);
800 
801         //-- ** Generate code for all TOP-LEVEL structures **
802 
803         //-- register all global element names for name conflict resolution
804         for (ElementDecl element : schema.getElementDecls()) {
805             _xmlInfoRegistry.prebindGlobalElement(XPathHelper.getSchemaLocation(element));
806         }
807         for (ModelGroup modelGroup : schema.getModelGroups()) {
808             _xmlInfoRegistry.prebindGlobalElement(XPathHelper.getSchemaLocation(modelGroup));
809         }
810 
811         //-- handle all top-level element declarations
812         for (ElementDecl element : schema.getElementDecls()) {
813             createClasses(element, sInfo);
814         }
815 
816         //-- handle all top-level complextypes
817         for (ComplexType complexType : schema.getComplexTypes()) {
818             processComplexType(complexType, sInfo);
819         }
820 
821         //-- handle all top-level simpletypes
822         for (SimpleType simpleType : schema.getSimpleTypes()) {
823             processSimpleType(simpleType, sInfo);
824         }
825 
826         //-- handle all top-level groups
827         for (ModelGroup modelGroup : schema.getModelGroups()) {
828             createClasses(modelGroup, sInfo);
829         }
830 
831         //-- clean up any remaining JClasses which need printing
832         _singleClassGenerator.processIfNotAlreadyProcessed(sInfo.keys(), sInfo);
833 
834         //-- handle cdr files
835         for (Enumeration<String> cdrFiles = sInfo.getCDRFilenames(); cdrFiles.hasMoreElements(); ) {
836             String filename = cdrFiles.nextElement();
837             Properties props = sInfo.getCDRFile(filename);
838             final FileOutputStream fileOutputStream = new FileOutputStream(new File(filename));
839             props.store(fileOutputStream, null);
840             fileOutputStream.close();
841         }
842     } //-- createClasses
843 
844     /**
845      * Look at each schema imported by the given schema.  Either warn that the
846      * invoker needs to separately generate source from that schema or process
847      * that schema, depending on settings.
848      * @param schema the schema whose imported schemas to process
849      * @param sInfo source generator state information
850      * @throws IOException if this Exception occurs while processing an import schema
851      */
852     private void processImportedSchemas(final Schema schema, final SGStateInfo sInfo)
853     throws IOException {
854         Iterator<Schema> enumeration = schema.getImportedSchema().iterator();
855         while (enumeration.hasNext()) {
856             Schema importedSchema = enumeration.next();
857             if (!_generateImported) {
858                 LOG.warn("Note: No code will be generated for the following *imported* schema: " 
859                         + importedSchema.getSchemaLocation() + "; if this is on intention, please "
860                         + "do not forget to generate code for this schema as well. Alternatively," 
861                         + "consider using the 'generateImportedSchemas' flag.");
862                 continue;
863             }
864 
865             _schemasProcessed.add(schema);
866             if (!_schemasProcessed.contains(importedSchema)) {
867                 SGStateInfo importedSInfo  = new SGStateInfo(importedSchema, this);
868                 String packageName = sInfo.getPackageName();
869                 importedSInfo.setPackageName(packageName);
870                 
871                 //--map the schemaLocation of the imported schema with the packageName defined
872                 if (packageName != null) {
873                     setLocationPackageMapping(importedSchema.getSchemaLocation(), packageName);
874                 }
875                 
876                 generateAllClassFiles(importedSchema, importedSInfo);
877 
878                 //--'store' the imported JClass instances
879                 sInfo.storeImportedSourcesByName(importedSInfo.getSourcesByName());
880                 sInfo.storeImportedSourcesByName(importedSInfo.getImportedSourcesByName());
881                 //--discard the SGStateInfo
882                 importedSInfo = null;
883             }
884         }
885     }
886 
887     /**
888      * Generates the mapping file.
889      * @param packageName Package name to be generated
890      * @param sInfo Source Generator current state
891      * @throws IOException if this Exception occurs while generating the mapping file
892      */
893     private void generateMappingFile(final String packageName, final SGStateInfo sInfo)
894                                                                       throws IOException {
895         String pkg = (packageName != null) ? packageName : "";
896         MappingRoot mapping = sInfo.getMapping(pkg);
897         if (mapping == null) {
898             return;
899         }
900 
901         FileWriter writer = new FileWriter(_mappingFilename);
902         try {
903             Marshaller marshaller = new Marshaller(writer);
904             marshaller.setSuppressNamespaces(true);
905             marshaller.marshal(mapping);
906         } catch (Exception ex) {
907             throw new NestedIOException(ex);
908         } finally {
909             writer.flush();
910             writer.close();
911         }
912     }
913 
914     /**
915      * Processes the given Element declaration and creates all necessary classes
916      * to support it.
917      *
918      * @param elementDecl the Element declaration to process
919      * @param sInfo our state information
920      * @throws IOException if this exception occurs while writing source files
921      */
922     private void createClasses(final ElementDecl elementDecl, final SGStateInfo sInfo)
923                                                                      throws IOException {
924         if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || elementDecl == null) {
925             return;
926         }
927 
928         //-- when mapping schema types, only interested in producing classes
929         //-- for elements with anonymous complex types
930         XMLType xmlType = elementDecl.getType();
931         if (mappingSchemaType2Java()) {
932             if (elementDecl.isReference() || ((xmlType != null) && (xmlType.getName() != null))) {
933                 return;
934             }
935         }
936 
937         //--create component
938         _bindingComponent.setView(elementDecl);
939 
940         //-- already processed --> just return
941         ClassInfo cInfo = sInfo.resolve(elementDecl);
942         if (cInfo != null && cInfo.getJClass() != null) {
943             JClass jClass = cInfo.getJClass();
944             if (sInfo.processed(jClass)) {
945                 return;
946             }
947             jClass = null;
948         }
949 
950         //-- No type definition
951         if (xmlType == null) {
952             if (sInfo.verbose()) {
953                 String msg = "No type found for element: " + elementDecl.getName();
954                 sInfo.getDialog().notify(msg);
955             }
956             return;
957         } else if (xmlType.isComplexType()) {
958             //-- ComplexType
959             JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
960             if (!_singleClassGenerator.process(classes, sInfo)) {
961                 return;
962             }
963 
964             //only create classes for types that are not imported
965             if (xmlType.getSchema() == _bindingComponent.getSchema()) {
966                 processComplexType((ComplexType) xmlType, sInfo);
967             }
968         } else if (xmlType.isSimpleType()) {
969             //-- SimpleType
970             processSimpleType((SimpleType) xmlType, sInfo);
971         } else {
972             //-- AnyType
973             //-- no processing needed for 'anyType'
974         }
975     }  //-- createClasses
976 
977     /**
978      * Processes the given Group and creates all necessary classes to support
979      * it.
980      *
981      * @param group the Group to process
982      * @param sInfo our state information
983      * @throws IOException if this exception occurs while writing source files
984      */
985     private void createClasses(final Group group, final SGStateInfo sInfo)
986                                                          throws IOException {
987         if (group == null) {
988             return;
989         }
990 
991         //-- don't generate classes for empty groups
992         if (group.getParticleCount() == 0) {
993             if (group instanceof ModelGroup) {
994                 ModelGroup mg = (ModelGroup) group;
995                 if (mg.isReference()) {
996                     mg = mg.getReference();
997                     if (mg.getParticleCount() == 0) {
998                         return;
999                     }
1000                 }
1001             } else {
1002                 return;
1003             }
1004         }
1005 
1006         _bindingComponent.setView(group);
1007 
1008         JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
1009         processContentModel(group, sInfo);
1010         _singleClassGenerator.process(classes, sInfo);
1011     } //-- createClasses
1012 
1013     /**
1014      * Processes the given ComplexType and creates all necessary classes to
1015      * support it.
1016      *
1017      * @param complexType the ComplexType to process
1018      * @param sInfo our state information
1019      * @throws IOException if this exception occurs while writing source files
1020      */
1021     private void processComplexType(final ComplexType complexType, final SGStateInfo sInfo)
1022                                                                            throws IOException {
1023         if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || complexType == null) {
1024             return;
1025         }
1026 
1027         _bindingComponent.setView(complexType);
1028 
1029         ClassInfo classInfo = sInfo.resolve(complexType);
1030         if (classInfo == null) {
1031             //-- handle top-level complextypes
1032             if (complexType.isTopLevel()) {
1033                 JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
1034                 if (!_singleClassGenerator.process(classes, sInfo)) {
1035                     return;
1036                 }
1037             }
1038 
1039             //-- process AttributeDecl
1040             processAttributes(complexType, sInfo);
1041 
1042             //--process content type if necessary
1043             ContentType temp = complexType.getContentType();
1044             if (temp.getType() == ContentType.SIMPLE) {
1045                 processSimpleType(((SimpleContent) temp).getSimpleType(), sInfo);
1046             }
1047 
1048             //-- process ContentModel
1049             processContentModel(complexType, sInfo);
1050         } else {
1051             JClass jClass = classInfo.getJClass();
1052             if (!sInfo.processed(jClass)) {
1053                 //-- process AttributeDecl
1054                 processAttributes(complexType, sInfo);
1055                 //-- process ContentModel
1056                 processContentModel(complexType, sInfo);
1057                 _singleClassGenerator.process(jClass, sInfo);
1058             }
1059         }
1060     } //-- processComplexType
1061 
1062     /**
1063      * Processes the attribute declarations for the given complex type.
1064      *
1065      * @param complexType the ComplexType containing the attribute declarations
1066      *        to process.
1067      * @param sInfo the current source generator state information
1068      * @throws IOException if this Exception occurs while generating source
1069      *         files
1070      */
1071     private void processAttributes(final ComplexType complexType, final SGStateInfo sInfo)
1072                                                                          throws IOException {
1073         if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || complexType == null) {
1074             return;
1075         }
1076 
1077         Enumeration<AttributeDecl> enumeration = complexType.getAttributeDecls();
1078         while (enumeration.hasMoreElements()) {
1079             AttributeDecl attribute = enumeration.nextElement();
1080             processSimpleType(attribute.getSimpleType(), sInfo);
1081         }
1082     } //-- processAttributes
1083 
1084     /**
1085      * Processes the given ContentModelGroup.
1086      *
1087      * @param cmGroup the ContentModelGroup to process
1088      * @param sInfo the current source generator state information
1089      * @throws IOException if this Exception occurs while generating source files
1090      */
1091     private void processContentModel(final ContentModelGroup cmGroup, final SGStateInfo sInfo)
1092                                                                              throws IOException {
1093         if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || cmGroup == null) {
1094             return;
1095         }
1096 
1097         //Some special code to handle the fact that the enumerate method will simply skip
1098         //the first group is the number of particle is one
1099 
1100         Enumeration<Structure> enumeration = cmGroup.enumerate();
1101 
1102         while (enumeration.hasMoreElements()) {
1103             Structure struct = enumeration.nextElement();
1104             switch(struct.getStructureType()) {
1105                 case Structure.ELEMENT:
1106                     ElementDecl eDecl = (ElementDecl) struct;
1107                     if (eDecl.isReference()) {
1108                         continue;
1109                     }
1110                     createClasses(eDecl, sInfo);
1111                     break;
1112                 case Structure.GROUP:
1113                     processContentModel((Group) struct, sInfo);
1114                     //handle nested groups
1115                     if (!((cmGroup instanceof ComplexType) || (cmGroup instanceof ModelGroup))) {
1116                         createClasses((Group) struct, sInfo);
1117                     }
1118                     break;
1119                 default:
1120                     break;
1121             }
1122         }
1123     } //-- processContentModel
1124 
1125     /**
1126      * Handle simpleTypes.
1127      *
1128      * @param simpleType the SimpleType to be processed
1129      * @param sInfo the current source generator state information
1130      * @throws IOException if this Exception occurs while generating source files
1131      */
1132     private void processSimpleType(final SimpleType simpleType, final SGStateInfo sInfo)
1133                                                                        throws IOException {
1134         if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || simpleType == null
1135                 || simpleType.getSchema() != sInfo.getSchema()) {
1136             return;
1137         }
1138 
1139         //-- Right now the only time we actually generate source for a simpletype is
1140         //-- when it's an enumeration
1141         if (simpleType.hasFacet(Facet.ENUMERATION)) {
1142             ClassInfo classInfo = sInfo.resolve(simpleType);
1143             if (classInfo == null) {
1144                 JClass jClass = _sourceFactory.createSourceCode(
1145                         _bindingComponent.getBinding(), simpleType, sInfo);
1146                 _singleClassGenerator.process(jClass, sInfo);
1147             } else {
1148                 JClass jClass = classInfo.getJClass();
1149                 _singleClassGenerator.process(jClass, sInfo);
1150             }
1151         }
1152     } //-- processSimpleType
1153 
1154    /**
1155      * Called by setBinding to fill in the mapping between namespaces and Java
1156      * packages.
1157      *
1158      * @param packages
1159      *            the array of package element
1160      */
1161     private void processNamespaces(final PackageType[] packages) {
1162         if (packages.length == 0) {
1163             return;
1164         }
1165 
1166         for (int i = 0; i < packages.length; i++) {
1167             PackageType temp = packages[i];
1168             PackageTypeChoice choice = temp.getPackageTypeChoice();
1169             if (choice.getNamespace() != null) {
1170                 super.setNamespacePackageMapping(choice.getNamespace(), temp.getName());
1171             } else if (choice.getSchemaLocation() != null) {
1172                 //1--Handle relative locations
1173                 String tempLocation = choice.getSchemaLocation();
1174                 String currentDir = System.getProperty("user.dir");
1175                 currentDir = currentDir.replace('\\', '/');
1176                 if (tempLocation.startsWith("./")) {
1177                     tempLocation = tempLocation.substring(1);
1178                     tempLocation = currentDir + tempLocation;
1179                 } else if (tempLocation.startsWith("../")) {
1180                      tempLocation = tempLocation.substring("../".length());
1181                      int lastDir = currentDir.lastIndexOf('/');
1182                      currentDir = currentDir.substring(0, lastDir + 1);
1183                      tempLocation = currentDir + tempLocation;
1184                 }
1185                 super.setLocationPackageMapping(tempLocation, temp.getName());
1186                 currentDir = null;
1187                 tempLocation = null;
1188             }
1189         }
1190     }
1191 
1192     /**
1193      * Returns a string which is the URI of a file.
1194      * <ul>
1195      * <li>file:///DOSpath</li>
1196      * <li>file://UnixPath</li>
1197      * </ul>
1198      * No validation is done to check whether the file exists or not. This
1199      * method will be no longer used when the JDK URL.toString() is fixed.
1200      *
1201      * @param path The absolute path of the file.
1202      * @return A string representing the URI of the file.
1203      */
1204     public static String toURIRepresentation(final String path) {
1205         String result = path;
1206         if (!new File(result).isAbsolute()) {
1207            throw new IllegalArgumentException("The parameter must represent an absolute path.");
1208         }
1209 
1210         if (File.separatorChar != '/') {
1211             result = result.replace(File.separatorChar, '/');
1212         }
1213 
1214         if (result.startsWith("/")) {
1215             result = "file://" + result;    /*Unix platform*/
1216         } else {
1217             result = "file:///" + result;   /*DOS platform*/
1218         }
1219 
1220         return result;
1221     }
1222 
1223     /**
1224      * For backwards compability, when we are called as the main() routine,
1225      * delegate the command-line usage to the proper class.
1226      *
1227      * @param args our command line arguments.
1228      * @deprecated Please use {@link SourceGeneratorMain#main(String[])}
1229      */
1230     public static void main(final String[] args) {
1231         LOG.info("org.exolab.castor.builder.SourceGenerator.main() is deprecated. "
1232                 + "Please use org.exolab.castor.builder.SourceGeneratorMain#main() instead.");
1233 
1234         SourceGeneratorMain.main(args);
1235     }
1236 
1237     /**
1238      * Returns the {@link JClassRegistry} instance associated with this source generator.
1239      * @return  the {@link JClassRegistry} instance currently in use.
1240      */
1241     public JClassRegistry getXMLInfoRegistry() {
1242         return _xmlInfoRegistry;
1243     }
1244     
1245     /**
1246      * Sets the jclassPrinter type.
1247      * @param jClassPrinterType The string identifier of the printer to use.
1248      */
1249     public final void setJClassPrinterType(final String jClassPrinterType) {
1250         LOG.info("Setting JClass printing mode " + jClassPrinterType);
1251         _jclassPrinterType = jClassPrinterType;
1252         if (_singleClassGenerator != null) {
1253             _singleClassGenerator.setJClassPrinterType(jClassPrinterType);
1254         }
1255     }
1256 
1257     /**
1258      * Indicates whether classes should be created for imported XML schemas as well.
1259      * @return true if classes should be created for imported schemas.
1260      */
1261     public boolean getGenerateImportedSchemas() {
1262         return _generateImported;
1263     }
1264 
1265     /**
1266      * Returns the registry for {@link JClassPrinterFactory} instances.
1267      * @return the registry for {@link JClassPrinterFactory} instances.
1268      */
1269     public JClassPrinterFactoryRegistry getJClassPrinterFactoryRegistry() {
1270         return _jclassPrinterFactoryRegistry;
1271     }    
1272     
1273 } //-- SourceGenerator