/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Intalio, Inc. For written permission,
* please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Intalio, Inc. Exolab is a registered
* trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
*
* This file was originally developed by Keith Visco during the
* course of employment at Intalio Inc.
* All portions of this file developed by Keith Visco after Jan 19 2005 are
* Copyright (C) 2005 Keith Visco. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.builder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.castor.core.exceptions.CastorRuntimeException;
import org.castor.xml.BackwardCompatibilityContext;
import org.castor.xml.InternalContext;
import org.exolab.castor.builder.binding.BindingException;
import org.exolab.castor.builder.binding.BindingLoader;
import org.exolab.castor.builder.binding.ExtendedBinding;
import org.exolab.castor.builder.binding.XMLBindingComponent;
import org.exolab.castor.builder.binding.XPathHelper;
import org.exolab.castor.builder.binding.xml.PackageType;
import org.exolab.castor.builder.binding.xml.PackageTypeChoice;
import org.exolab.castor.builder.binding.xml.types.BindingType;
import org.exolab.castor.builder.conflict.strategy.ClassNameConflictResolver;
import org.exolab.castor.builder.conflict.strategy.TypeClassNameConflictResolver;
import org.exolab.castor.builder.conflict.strategy.XPATHClassNameConflictResolver;
import org.exolab.castor.builder.conflictresolution.WarningViaDialogClassNameCRStrategy;
import org.exolab.castor.builder.factory.FieldInfoFactory;
import org.exolab.castor.builder.factory.SourceFactory;
import org.exolab.castor.builder.info.ClassInfo;
import org.exolab.castor.builder.printing.JClassPrinter;
import org.exolab.castor.builder.printing.JClassPrinterFactory;
import org.exolab.castor.builder.printing.JClassPrinterFactoryRegistry;
import org.exolab.castor.mapping.xml.MappingRoot;
import org.exolab.castor.util.NestedIOException;
import org.exolab.castor.util.Version;
import org.exolab.castor.util.dialog.ConsoleDialog;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.ValidationException;
import org.exolab.castor.xml.XMLException;
import org.exolab.castor.xml.schema.AttributeDecl;
import org.exolab.castor.xml.schema.ComplexType;
import org.exolab.castor.xml.schema.ContentModelGroup;
import org.exolab.castor.xml.schema.ContentType;
import org.exolab.castor.xml.schema.ElementDecl;
import org.exolab.castor.xml.schema.Facet;
import org.exolab.castor.xml.schema.Group;
import org.exolab.castor.xml.schema.ModelGroup;
import org.exolab.castor.xml.schema.Schema;
import org.exolab.castor.xml.schema.SchemaContext;
import org.exolab.castor.xml.schema.SchemaContextImpl;
import org.exolab.castor.xml.schema.SimpleContent;
import org.exolab.castor.xml.schema.SimpleType;
import org.exolab.castor.xml.schema.Structure;
import org.exolab.castor.xml.schema.XMLType;
import org.exolab.castor.xml.schema.reader.Sax2ComponentReader;
import org.exolab.castor.xml.schema.reader.SchemaUnmarshaller;
import org.exolab.javasource.JClass;
import org.xml.sax.InputSource;
import org.xml.sax.Parser;
import org.xml.sax.SAXParseException;
/**
* A Java Source generation tool which uses XML Schema definitions
* to create an Object model.
*
* @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> - Main author.
* @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a> - Contributions.
* @author <a href="mailto:nsgreen@thazar.com">Nathan Green</a> - Contributions.
* @version $Revision$ $Date: 2006-03-30 14:58:45 -0700 (Thu, 30 Mar 2006) $
*/
public class SourceGenerator extends BuilderConfiguration {
/** Jakarta's common-logging logger. */
private static final Log LOG = LogFactory.getLog(SourceGenerator.class);
//-------------/
//- Constants -/
//-------------/
/** The application name. */
static final String APP_NAME = "Castor";
/** The application description. */
static final String APP_DESC = "XML data binder for Java";
/** The application version. */
static final String VERSION = Version.VERSION;
/** The application URI. */
static final String APP_URI = "http://www.castor.org";
/** Warning message to remind users to create source code for imported schema. */
private static final String IMPORT_WARNING =
"Note: No code will be generated for the following *imported* schema: ";
//----------------------/
//- Instance Variables -/
//----------------------/
/** Castor internal context - mother of all. */
private final InternalContext _internalContext;
/** The XMLBindingComponent used to create Java classes from an XML Schema. */
private final XMLBindingComponent _bindingComponent;
/** Our object used to generate source for a single source file. */
private final SingleClassGenerator _singleClassGenerator;
/** The field info factory. */
private final FieldInfoFactory _infoFactory;
/** Allows us to ask the user questions. */
private final ConsoleDialog _dialog;
/** A vector that keeps track of all the schemas processed. */
private final Vector<Schema> _schemasProcessed = new Vector<Schema>(7);
/** True if we should suppress non-fatal warnings. */
private boolean _suppressNonFatalWarnings = false;
/** Determines whether or not to print extra messages. */
private boolean _verbose = false;
/**
* A flag indicating whether or not to create XML-specific class
* descriptors for the generated classes.
*/
private boolean _createDescriptors = true;
/**
* A flag indicating whether or not to create JDO-specific class
* descriptors for the generated classes.
*/
private boolean _createJdoDescriptors = false;
/** A flag indicating whether or not to generate sources
* for imported XML Schemas. */
private boolean _generateImported = false;
/** The source factory. */
private SourceFactory _sourceFactory = null;
/** A flag to indicate that the mapping file should be generated. */
private boolean _generateMapping = false;
/** The name of the mapping file to create used with the gen-mapping flag. */
private String _mappingFilename = "mapping.xml";
/** A flag indicating whether or not to generate XML marshalling
* framework specific methods. */
private boolean _createMarshalMethods = true;
/** A flag indicating whether or not to implement CastorTestable
* (used by the Castor Testing Framework). */
private boolean _testable = false;
/** A flag indicating that SAX1 should be used when generating the source. */
private boolean _sax1 = false;
/** A flag indicating that enumerated types should be constructed to perform
* case insensitive lookups based on the values. */
private boolean _caseInsensitive = false;
/** A flag indicating, if true, that source generation should fail on the
* first error. */
private boolean _failOnFirstError = false;
/** A GroupNaming helper class used to named anonymous groups. */
private GroupNaming _groupNaming = null;
/** Strategy for name conflict resolution. */
private String _nameConflictStrategy = WarningViaDialogClassNameCRStrategy.NAME;
/** JClass to XPATH registry, used for class name conflict resolution. */
private JClassRegistry _xmlInfoRegistry;
/**
* Strategy implementation for resolving class name conflicts.
*/
private ClassNameConflictResolver _conflictResolver =
new XPATHClassNameConflictResolver();
/**
* The default type of the {@link JClassPrinterFactory} to use for instantiating
* instances of {@link JClassPrinter} instances.
*/
private String _jclassPrinterType = "standard";
/**
* {@link JClassPrinterFactoryRegistry} instance to be used for obtaining instances
* of {@link JClassPrinterFactory} instances.
*/
private JClassPrinterFactoryRegistry _jclassPrinterFactoryRegistry;
protected SGStateInfo _sInfo;
/**
* Creates a SourceGenerator using the default FieldInfo factory.
*/
public SourceGenerator() {
this(null);
} //-- SourceGenerator
/**
* Creates a SourceGenerator using the specific field info Factory.
*
* @param infoFactory the FieldInfoFactory to use.
*/
public SourceGenerator(final FieldInfoFactory infoFactory) {
this(infoFactory, null);
}
/**
* Creates a SourceGenerator using the specific field info Factory and the
* given Binding element.
*
* @param infoFactory the FieldInfoFactory to use.
* @param binding the binding element to use.
*/
public SourceGenerator(final FieldInfoFactory infoFactory, final ExtendedBinding binding) {
super();
_internalContext = new BackwardCompatibilityContext();
setJavaNaming(_internalContext.getJavaNaming());
_dialog = new ConsoleDialog();
_infoFactory = (infoFactory == null) ? new FieldInfoFactory() : infoFactory;
super.load();
_groupNaming = new GroupNaming(getJavaNaming());
_jclassPrinterFactoryRegistry = new JClassPrinterFactoryRegistry(this);
_singleClassGenerator = new SingleClassGenerator(_dialog, this, _nameConflictStrategy,
_jclassPrinterType);
_bindingComponent = new XMLBindingComponent(this, _groupNaming);
setBinding(binding);
_conflictResolver.setSourceGenerator(this);
_xmlInfoRegistry = new JClassRegistry(_conflictResolver, getJavaNaming());
} //-- SourceGenerator
/**
* Returns the selected {@link JClassPrinter} type, as defined by the list of
* {@link JClassPrinterFactory} instances enlisted in the Castor XML code generator
* property file.
* @return the selected {@link JClassPrinter} type.
*/
private String getJClassPrinterType() {
return _jclassPrinterType;
}
/**
* Sets the filename of the mapping file.
* @param filename filename of the mapping file
*/
public final void setMappingFilename(final String filename) {
_mappingFilename = filename;
}
/**
* Sets the strategy for handling name conflicts.
*
* @param nameConflictStrategy the name of the stretegy to use for handling
* name conflicts.
*/
public final void setNameConflictStrategy(final String nameConflictStrategy) {
_nameConflictStrategy = nameConflictStrategy;
_singleClassGenerator.setNameConflictStrategy(nameConflictStrategy);
}
/**
* Returns the version number of this SourceGenerator.
*
* @return the version number of this SourceGenerator
*/
public static String getVersion() {
return VERSION;
} //-- getVersion
/**
* Set to true if SAX1 should be used in the marshal method.
* @param sax1 true if SAX1 should be used in the marshal method
*/
public final void setSAX1(final boolean sax1) {
_sax1 = sax1;
}
/**
* Set to true if enumerated type lookups should be performed in a case
* insensitive manner.
*
* @param caseInsensitive when true, enumerated type lookups will be
* performed in a case insensitive manner.
*/
public final void setCaseInsensitive(final boolean caseInsensitive) {
_caseInsensitive = caseInsensitive;
}
/**
* If true, the source generator will fail on the first error encountered.
* Otherwise, the source generator will continue on certain errors.
*
* @param failOnFirstError if true, the source generator will fail on the
* first error encountered.
*/
public final void setFailOnFirstError(final boolean failOnFirstError) {
_failOnFirstError = failOnFirstError;
}
/**
* Sets whether or not to suppress non-fatal warnings encountered during
* source generation.
* @param suppress true if non-fatal warnings should be suppressed.
*/
public final void setSuppressNonFatalWarnings(final boolean suppress) {
_singleClassGenerator.setPromptForOverwrite(!suppress);
_suppressNonFatalWarnings = suppress;
} //-- setSuppressNonFatalWarnings
/**
* Sets whether or not the source code generator prints additional messages
* during generating source code.
*
* @param verbose a boolean, when true indicates to print additional
* messages
*/
public void setVerbose(final boolean verbose) {
_verbose = verbose;
} //-- setVerbose
/**
* Sets the ClassNameConflictResolver instance to be used for automatic class name
* conflict resolution.
*
* @param resolverName The name of the resolver to be used for automatic class name
* conflict resolution
*/
public void setClassNameConflictResolver(final String resolverName) {
if (resolverName.equals("type")) {
_conflictResolver = new TypeClassNameConflictResolver();
_conflictResolver.setSourceGenerator(this);
} else if (resolverName.equals("xpath")) {
// leave default
} else {
throw new IllegalArgumentException("Invalid resolver type.");
}
_xmlInfoRegistry.setClassNameConflictResolver(_conflictResolver);
} //-- setClassNameConflictResolver
/**
* Sets whether or not to create ClassDescriptors for the generated classes.
* By default, descriptors are generated.
*
* @param createDescriptors a boolean, when true indicates to generated
* ClassDescriptors
*
*/
public final void setDescriptorCreation(final boolean createDescriptors) {
_createDescriptors = createDescriptors;
_singleClassGenerator.setDescriptorCreation(createDescriptors);
} //-- setDescriptorCreation
/**
* Sets whether or not to create JDO-specific class descriptors for the
* generated classes.
* By default, JDO-specific class descriptors are NOT generated.
*
* @param createJdoDescriptors a boolean, when true indicates to generated
* JDO-specific class descriptors
*
*/
public final void setJdoDescriptorCreation(final boolean createJdoDescriptors) {
_createJdoDescriptors = createJdoDescriptors;
_singleClassGenerator.setJdoDescriptorCreation(createJdoDescriptors);
} //-- setDescriptorCreation
/**
* Sets the destination directory.
*
* @param destDir the destination directory.
*/
public final void setDestDir(final String destDir) {
_singleClassGenerator.setDestDir(destDir);
}
/**
* Sets the destination directory for resources, e.g. '.castor.cdr' files.
*
* @param destDir the destination directory for resources.
*/
public final void setResourceDestination(final String destination) {
_singleClassGenerator.setResourceDestinationDirectory(destination);
}
/**
* Sets whether or not to create the XML marshaling framework specific
* methods (marshal, unmarshal, validate) in the generated classes. By
* default, these methods are generated.
*
* @param createMarshalMethods a boolean, when true indicates to generated
* the marshaling framework methods
*/
public final void setCreateMarshalMethods(final boolean createMarshalMethods) {
_createMarshalMethods = createMarshalMethods;
} //-- setCreateMarshalMethods
/**
* Sets whether or not to generate Java sources for imported XML Schema.
* By default Java sources for imported XML schemas are not generated.
*
* @param generate true to generate the java classes for the imported XML Schema
*/
public final void setGenerateImportedSchemas(final boolean generate) {
_generateImported = generate;
}
/**
* Sets whether or not a mapping file should be generated, this is false by
* default. Note that this will only be used when generation of descriptors
* has been disabled.
*
* @param generateMapping a flag that indicates whether or not a mapping
* file should be generated.
*/
public final void setGenerateMappingFile(final boolean generateMapping) {
_generateMapping = generateMapping;
} //-- setGenerateMappingFile
/**
* Sets whether or not to implement CastorTestable.
*
* @param testable
* a boolean, when true indicates to implement CastorTestable
*/
public final void setTestable(final boolean testable) {
_testable = testable;
} //-- setTestable
/**
* Sets the binding to use with this instance of the SourceGenerator.
*
* @param binding
* the binding to use, null indicates that the default binding
* will be used.
*/
public final void setBinding(final ExtendedBinding binding) {
if (binding != null) {
processNamespaces(binding.getPackage());
}
//--initialize the XMLBindingComponent
_bindingComponent.setBinding(binding);
} //-- setBinding
/**
* Sets the binding to use given the path name of a Castor Binding File.
*
* @param fileName the file that represents a Binding
*/
public final void setBinding(final String fileName) {
try {
ExtendedBinding binding = BindingLoader.createBinding(fileName);
setBinding(binding);
} catch (BindingException e) {
String err = "Unable to load a binding file due to the following:\n"
+ e.getMessage()
+ "\nThe Source Generator will continue with no binding file.";
_dialog.notify(err);
}
}
/**
* Sets the binding to use given an InputSource identifying
* a Castor Binding File.
*
* @param source an InputSource identifying a Castor Binding File.
*/
public final void setBinding(final InputSource source) {
try {
ExtendedBinding binding = BindingLoader.createBinding(source);
setBinding(binding);
} catch (BindingException e) {
String err = "unable to load a binding file due to the following:\n"
+ e.getMessage()
+ "\nThe Source Generator will continue with no binding file.";
_dialog.notify(err);
}
}
/**
* Sets the line separator to use when printing the source code.
* <p>
* <B>Note:</B>This can be any string, so be careful. I recommend either
* using the default or using one of the following:
*
* <PRE>
* windows systems use: "\r\n"
* unix systems use: "\n"
* mac systems use: "\r"
* </PRE>
*
* @param lineSeparator the line separator to use when printing the source
* code. This method is useful if you are generating source on one
* platform, but will be compiling the source on a different
* platform.
*/
public final void setLineSeparator(final String lineSeparator) {
_singleClassGenerator.setLineSeparator(lineSeparator);
} //-- setLineSeparator
/**
* Tests the org.exolab.castor.builder.javaclassmapping property for the
* 'element' value.
*
* @return True if the Source Generator is mapping schema elements to Java
* classes.
*/
public final boolean mappingSchemaElement2Java() {
if (_bindingComponent != null) {
ExtendedBinding binding = _bindingComponent.getBinding();
if (binding != null) {
BindingType type = binding.getDefaultBindingType();
if (type != null) {
return (type == BindingType.ELEMENT);
}
}
}
return super.mappingSchemaElement2Java();
} //-- mappingSchemaElement2Java
/**
* Tests the org.exolab.castor.builder.javaclassmapping property for the
* 'type' value.
*
* @return True if the Source Generator is mapping schema types to Java
* classes.
*/
public final boolean mappingSchemaType2Java() {
if (_bindingComponent != null) {
ExtendedBinding binding = _bindingComponent.getBinding();
if (binding != null) {
BindingType type = binding.getDefaultBindingType();
if (type != null) {
return (type == BindingType.TYPE);
}
}
}
return super.mappingSchemaType2Java();
} //-- mappingSchemaType2Java
/**
* Creates Java Source code (Object model) for the given XML Schema. If the
* file exists, opens a FileReader and passes control to
* {@link #generateSource(InputSource, String)}.
*
* @param filename the full path to the XML Schema definition
* @param packageName the package for the generated source files
* @throws IOException if an IOException occurs writing the new source files
*/
public final void generateSource(
final String filename, final String packageName) throws IOException {
final File schemaFile;
if (filename.startsWith("./")) {
schemaFile = new File(filename.substring(2));
} else {
schemaFile = new File(filename);
}
FileReader reader = new FileReader(schemaFile);
try {
InputSource source = new InputSource(reader);
source.setSystemId(toURIRepresentation(schemaFile.getAbsolutePath()));
generateSource(source, packageName);
} finally {
try { reader.close(); } catch (java.io.IOException iox) {
// ignore
}
}
} //-- generateSource
/**
* Creates Java Source code (Object model) for the given XML Schema. This
* method just passes control to
* {@link #generateSource(InputSource, String)}.
*
* @param reader the Reader with which to read the XML Schema definition.
* The caller should close the reader, since thie method will not do
* so.
* @param packageName the package for the generated source files
* @throws IOException if an IOException occurs writing the new source files
*/
public final void generateSource(
final Reader reader, final String packageName) throws IOException {
InputSource source = new InputSource(reader);
generateSource(source, packageName);
} //-- generateSource
/**
* Creates Java Source code (Object model) for the given XML Schema. Parses
* the schema provided by the InputSource and then calls
* {@link #generateSource(Schema, String)} to actually generate the source.
*
* @param source - the InputSource representing the XML schema.
* @param packageName the package for the generated source files
* @throws IOException if an IOException occurs writing the new source files
*/
public void generateSource(final InputSource source, final String packageName)
throws IOException {
// -- get default parser from Configuration
Parser parser = null;
try {
parser = _internalContext.getParser();
} catch (RuntimeException rte) {
// ignore
}
if (parser == null) {
_dialog.notify("fatal error: unable to create SAX parser.");
return;
}
SchemaContext schemaContext = new SchemaContextImpl();
SchemaUnmarshaller schemaUnmarshaller = null;
try {
schemaUnmarshaller = new SchemaUnmarshaller(schemaContext);
} catch (XMLException e) {
//--The default constructor cannot throw exception so this should never happen
//--just log the exception
e.printStackTrace();
System.exit(1);
}
Sax2ComponentReader handler = new Sax2ComponentReader(schemaUnmarshaller);
parser.setDocumentHandler(handler);
parser.setErrorHandler(handler);
try {
parser.parse(source);
} catch (java.io.IOException ioe) {
_dialog.notify("error reading XML Schema file");
if (_failOnFirstError) {
throw ioe;
}
return;
} catch (org.xml.sax.SAXException sx) {
Exception except = sx.getException();
if (except == null) {
except = sx;
}
if (except instanceof SAXParseException) {
SAXParseException spe = (SAXParseException) except;
_dialog.notify("SAXParseException: " + spe);
_dialog.notify(" - occured at line ");
_dialog.notify(Integer.toString(spe.getLineNumber()));
_dialog.notify(", column ");
_dialog.notify(Integer.toString(spe.getColumnNumber()));
} else {
except.printStackTrace();
}
if (_failOnFirstError) {
String msg = "Source Generator: schema parser threw an Exception";
throw new CastorRuntimeException(msg, sx);
}
return;
}
Schema schema = schemaUnmarshaller.getSchema();
try {
schema.validate();
} catch (ValidationException vx) {
throw new NestedIOException(vx);
}
generateSource(schema, packageName);
} //-- generateSource
/**
* Creates Java Source code (Object model) for the given XML Schema.
* Convenience methods exist if you don't have a
* {@link org.exolab.castor.xml.schema.Schema} already parsed.
*
* @param schema the XML schema to generate the Java sources for.
* @param packageName the package for the generated source files.
* @throws IOException if this Exception occurs while generating source
* @see #generateSource(String, String) to provide the schema filename
* @see #generateSource(Reader, String) to provide a Reader for the schema
* @see #generateSource(InputSource, String) to provide an InputSource for
* the schema
*/
public final void generateSource(
final Schema schema, final String packageName) throws IOException {
if (schema == null) {
throw new IllegalArgumentException("The argument 'schema' must not be null.");
}
//--make sure the XML Schema is valid
try {
schema.validate();
} catch (ValidationException ve) {
String err = "The schema: " + schema.getSchemaLocation() + " is not valid.\n"
+ ve.getMessage();
throw new IllegalArgumentException(err);
}
// Now that we're ready to generate source and we know our configuration
// has been fully parsed, create our SourceFactory. (See CASTOR-1346.)
// We will reuse this SourceFactory if we are invoked multiple times.
if (_sourceFactory == null) {
_sourceFactory = new SourceFactory(this, _infoFactory, _groupNaming, this);
_sourceFactory.setCreateMarshalMethods(_createMarshalMethods);
_sourceFactory.setTestable(_testable);
_sourceFactory.setSAX1(_sax1);
_sourceFactory.setCaseInsensitive(_caseInsensitive);
}
_sInfo = new SGStateInfo(schema, this);
_sInfo.setPackageName(packageName);
_sInfo.setDialog(_dialog);
_sInfo.setVerbose(_verbose);
_sInfo.setSuppressNonFatalWarnings(_suppressNonFatalWarnings);
//--map the schemaLocation of the schema with the packageName defined
if (packageName != null) {
super.setLocationPackageMapping(schema.getSchemaLocation(), packageName);
}
//--We start with a blank list of schemas processed
_schemasProcessed.clear();
generateAllClassFiles(schema, _sInfo);
//-- TODO Cleanup integration (what does this comment mean?)
if (!_createDescriptors && _generateMapping) {
generateMappingFile(packageName, _sInfo);
}
// output statistical information from JClassRegistry in 'automatic'mode only
if (isAutomaticConflictResolution()) {
_xmlInfoRegistry.printStatistics(_bindingComponent);
}
} //-- generateSource
//-------------------/
//- Private Methods -/
//-------------------/
/**
* Generate all class files for the provided schema. Before processing
* the current schema, process the schemas imported by this one (unless our
* configuration says not to).
*
* @param schema the schema whose imported schemas to process
* @param sInfo source generator state information
* @throws IOException if this Exception occurs while processing an import schema
*/
private void generateAllClassFiles(final Schema schema, final SGStateInfo sInfo)
throws IOException {
// Before processing the current schema, process its imported schemas
processImportedSchemas(schema, sInfo);
//-- ** Generate code for all TOP-LEVEL structures **
//-- register all global element names for name conflict resolution
for (ElementDecl element : schema.getElementDecls()) {
_xmlInfoRegistry.prebindGlobalElement(XPathHelper.getSchemaLocation(element));
}
for (ModelGroup modelGroup : schema.getModelGroups()) {
_xmlInfoRegistry.prebindGlobalElement(XPathHelper.getSchemaLocation(modelGroup));
}
//-- handle all top-level element declarations
for (ElementDecl element : schema.getElementDecls()) {
createClasses(element, sInfo);
}
//-- handle all top-level complextypes
for (ComplexType complexType : schema.getComplexTypes()) {
processComplexType(complexType, sInfo);
}
//-- handle all top-level simpletypes
for (SimpleType simpleType : schema.getSimpleTypes()) {
processSimpleType(simpleType, sInfo);
}
//-- handle all top-level groups
for (ModelGroup modelGroup : schema.getModelGroups()) {
createClasses(modelGroup, sInfo);
}
//-- clean up any remaining JClasses which need printing
_singleClassGenerator.processIfNotAlreadyProcessed(sInfo.keys(), sInfo);
//-- handle cdr files
for (Enumeration<String> cdrFiles = sInfo.getCDRFilenames(); cdrFiles.hasMoreElements(); ) {
String filename = cdrFiles.nextElement();
Properties props = sInfo.getCDRFile(filename);
final FileOutputStream fileOutputStream = new FileOutputStream(new File(filename));
props.store(fileOutputStream, null);
fileOutputStream.close();
}
} //-- createClasses
/**
* Look at each schema imported by the given schema. Either warn that the
* invoker needs to separately generate source from that schema or process
* that schema, depending on settings.
* @param schema the schema whose imported schemas to process
* @param sInfo source generator state information
* @throws IOException if this Exception occurs while processing an import schema
*/
private void processImportedSchemas(final Schema schema, final SGStateInfo sInfo)
throws IOException {
Iterator<Schema> enumeration = schema.getImportedSchema().iterator();
while (enumeration.hasNext()) {
Schema importedSchema = enumeration.next();
if (!_generateImported) {
LOG.warn("Note: No code will be generated for the following *imported* schema: "
+ importedSchema.getSchemaLocation() + "; if this is on intention, please "
+ "do not forget to generate code for this schema as well. Alternatively,"
+ "consider using the 'generateImportedSchemas' flag.");
continue;
}
_schemasProcessed.add(schema);
if (!_schemasProcessed.contains(importedSchema)) {
SGStateInfo importedSInfo = new SGStateInfo(importedSchema, this);
String packageName = sInfo.getPackageName();
importedSInfo.setPackageName(packageName);
//--map the schemaLocation of the imported schema with the packageName defined
if (packageName != null) {
setLocationPackageMapping(importedSchema.getSchemaLocation(), packageName);
}
generateAllClassFiles(importedSchema, importedSInfo);
//--'store' the imported JClass instances
sInfo.storeImportedSourcesByName(importedSInfo.getSourcesByName());
sInfo.storeImportedSourcesByName(importedSInfo.getImportedSourcesByName());
//--discard the SGStateInfo
importedSInfo = null;
}
}
}
/**
* Generates the mapping file.
* @param packageName Package name to be generated
* @param sInfo Source Generator current state
* @throws IOException if this Exception occurs while generating the mapping file
*/
private void generateMappingFile(final String packageName, final SGStateInfo sInfo)
throws IOException {
String pkg = (packageName != null) ? packageName : "";
MappingRoot mapping = sInfo.getMapping(pkg);
if (mapping == null) {
return;
}
FileWriter writer = new FileWriter(_mappingFilename);
try {
Marshaller marshaller = new Marshaller(writer);
marshaller.setSuppressNamespaces(true);
marshaller.marshal(mapping);
} catch (Exception ex) {
throw new NestedIOException(ex);
} finally {
writer.flush();
writer.close();
}
}
/**
* Processes the given Element declaration and creates all necessary classes
* to support it.
*
* @param elementDecl the Element declaration to process
* @param sInfo our state information
* @throws IOException if this exception occurs while writing source files
*/
private void createClasses(final ElementDecl elementDecl, final SGStateInfo sInfo)
throws IOException {
if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || elementDecl == null) {
return;
}
//-- when mapping schema types, only interested in producing classes
//-- for elements with anonymous complex types
XMLType xmlType = elementDecl.getType();
if (mappingSchemaType2Java()) {
if (elementDecl.isReference() || ((xmlType != null) && (xmlType.getName() != null))) {
return;
}
}
//--create component
_bindingComponent.setView(elementDecl);
//-- already processed --> just return
ClassInfo cInfo = sInfo.resolve(elementDecl);
if (cInfo != null && cInfo.getJClass() != null) {
JClass jClass = cInfo.getJClass();
if (sInfo.processed(jClass)) {
return;
}
jClass = null;
}
//-- No type definition
if (xmlType == null) {
if (sInfo.verbose()) {
String msg = "No type found for element: " + elementDecl.getName();
sInfo.getDialog().notify(msg);
}
return;
} else if (xmlType.isComplexType()) {
//-- ComplexType
JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
if (!_singleClassGenerator.process(classes, sInfo)) {
return;
}
//only create classes for types that are not imported
if (xmlType.getSchema() == _bindingComponent.getSchema()) {
processComplexType((ComplexType) xmlType, sInfo);
}
} else if (xmlType.isSimpleType()) {
//-- SimpleType
processSimpleType((SimpleType) xmlType, sInfo);
} else {
//-- AnyType
//-- no processing needed for 'anyType'
}
} //-- createClasses
/**
* Processes the given Group and creates all necessary classes to support
* it.
*
* @param group the Group to process
* @param sInfo our state information
* @throws IOException if this exception occurs while writing source files
*/
private void createClasses(final Group group, final SGStateInfo sInfo)
throws IOException {
if (group == null) {
return;
}
//-- don't generate classes for empty groups
if (group.getParticleCount() == 0) {
if (group instanceof ModelGroup) {
ModelGroup mg = (ModelGroup) group;
if (mg.isReference()) {
mg = mg.getReference();
if (mg.getParticleCount() == 0) {
return;
}
}
} else {
return;
}
}
_bindingComponent.setView(group);
JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
processContentModel(group, sInfo);
_singleClassGenerator.process(classes, sInfo);
} //-- createClasses
/**
* Processes the given ComplexType and creates all necessary classes to
* support it.
*
* @param complexType the ComplexType to process
* @param sInfo our state information
* @throws IOException if this exception occurs while writing source files
*/
private void processComplexType(final ComplexType complexType, final SGStateInfo sInfo)
throws IOException {
if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || complexType == null) {
return;
}
_bindingComponent.setView(complexType);
ClassInfo classInfo = sInfo.resolve(complexType);
if (classInfo == null) {
//-- handle top-level complextypes
if (complexType.isTopLevel()) {
JClass[] classes = _sourceFactory.createSourceCode(_bindingComponent, sInfo);
if (!_singleClassGenerator.process(classes, sInfo)) {
return;
}
}
//-- process AttributeDecl
processAttributes(complexType, sInfo);
//--process content type if necessary
ContentType temp = complexType.getContentType();
if (temp.getType() == ContentType.SIMPLE) {
processSimpleType(((SimpleContent) temp).getSimpleType(), sInfo);
}
//-- process ContentModel
processContentModel(complexType, sInfo);
} else {
JClass jClass = classInfo.getJClass();
if (!sInfo.processed(jClass)) {
//-- process AttributeDecl
processAttributes(complexType, sInfo);
//-- process ContentModel
processContentModel(complexType, sInfo);
_singleClassGenerator.process(jClass, sInfo);
}
}
} //-- processComplexType
/**
* Processes the attribute declarations for the given complex type.
*
* @param complexType the ComplexType containing the attribute declarations
* to process.
* @param sInfo the current source generator state information
* @throws IOException if this Exception occurs while generating source
* files
*/
private void processAttributes(final ComplexType complexType, final SGStateInfo sInfo)
throws IOException {
if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || complexType == null) {
return;
}
Enumeration<AttributeDecl> enumeration = complexType.getAttributeDecls();
while (enumeration.hasMoreElements()) {
AttributeDecl attribute = enumeration.nextElement();
processSimpleType(attribute.getSimpleType(), sInfo);
}
} //-- processAttributes
/**
* Processes the given ContentModelGroup.
*
* @param cmGroup the ContentModelGroup to process
* @param sInfo the current source generator state information
* @throws IOException if this Exception occurs while generating source files
*/
private void processContentModel(final ContentModelGroup cmGroup, final SGStateInfo sInfo)
throws IOException {
if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || cmGroup == null) {
return;
}
//Some special code to handle the fact that the enumerate method will simply skip
//the first group is the number of particle is one
Enumeration<Structure> enumeration = cmGroup.enumerate();
while (enumeration.hasMoreElements()) {
Structure struct = enumeration.nextElement();
switch(struct.getStructureType()) {
case Structure.ELEMENT:
ElementDecl eDecl = (ElementDecl) struct;
if (eDecl.isReference()) {
continue;
}
createClasses(eDecl, sInfo);
break;
case Structure.GROUP:
processContentModel((Group) struct, sInfo);
//handle nested groups
if (!((cmGroup instanceof ComplexType) || (cmGroup instanceof ModelGroup))) {
createClasses((Group) struct, sInfo);
}
break;
default:
break;
}
}
} //-- processContentModel
/**
* Handle simpleTypes.
*
* @param simpleType the SimpleType to be processed
* @param sInfo the current source generator state information
* @throws IOException if this Exception occurs while generating source files
*/
private void processSimpleType(final SimpleType simpleType, final SGStateInfo sInfo)
throws IOException {
if (sInfo.getStatusCode() == SGStateInfo.STOP_STATUS || simpleType == null
|| simpleType.getSchema() != sInfo.getSchema()) {
return;
}
//-- Right now the only time we actually generate source for a simpletype is
//-- when it's an enumeration
if (simpleType.hasFacet(Facet.ENUMERATION)) {
ClassInfo classInfo = sInfo.resolve(simpleType);
if (classInfo == null) {
JClass jClass = _sourceFactory.createSourceCode(
_bindingComponent.getBinding(), simpleType, sInfo);
_singleClassGenerator.process(jClass, sInfo);
} else {
JClass jClass = classInfo.getJClass();
_singleClassGenerator.process(jClass, sInfo);
}
}
} //-- processSimpleType
/**
* Called by setBinding to fill in the mapping between namespaces and Java
* packages.
*
* @param packages
* the array of package element
*/
private void processNamespaces(final PackageType[] packages) {
if (packages.length == 0) {
return;
}
for (int i = 0; i < packages.length; i++) {
PackageType temp = packages[i];
PackageTypeChoice choice = temp.getPackageTypeChoice();
if (choice.getNamespace() != null) {
super.setNamespacePackageMapping(choice.getNamespace(), temp.getName());
} else if (choice.getSchemaLocation() != null) {
//1--Handle relative locations
String tempLocation = choice.getSchemaLocation();
String currentDir = System.getProperty("user.dir");
currentDir = currentDir.replace('\\', '/');
if (tempLocation.startsWith("./")) {
tempLocation = tempLocation.substring(1);
tempLocation = currentDir + tempLocation;
} else if (tempLocation.startsWith("../")) {
tempLocation = tempLocation.substring("../".length());
int lastDir = currentDir.lastIndexOf('/');
currentDir = currentDir.substring(0, lastDir + 1);
tempLocation = currentDir + tempLocation;
}
super.setLocationPackageMapping(tempLocation, temp.getName());
currentDir = null;
tempLocation = null;
}
}
}
/**
* Returns a string which is the URI of a file.
* <ul>
* <li>file:///DOSpath</li>
* <li>file://UnixPath</li>
* </ul>
* No validation is done to check whether the file exists or not. This
* method will be no longer used when the JDK URL.toString() is fixed.
*
* @param path The absolute path of the file.
* @return A string representing the URI of the file.
*/
public static String toURIRepresentation(final String path) {
String result = path;
if (!new File(result).isAbsolute()) {
throw new IllegalArgumentException("The parameter must represent an absolute path.");
}
if (File.separatorChar != '/') {
result = result.replace(File.separatorChar, '/');
}
if (result.startsWith("/")) {
result = "file://" + result; /*Unix platform*/
} else {
result = "file:///" + result; /*DOS platform*/
}
return result;
}
/**
* For backwards compability, when we are called as the main() routine,
* delegate the command-line usage to the proper class.
*
* @param args our command line arguments.
* @deprecated Please use {@link SourceGeneratorMain#main(String[])}
*/
public static void main(final String[] args) {
LOG.info("org.exolab.castor.builder.SourceGenerator.main() is deprecated. "
+ "Please use org.exolab.castor.builder.SourceGeneratorMain#main() instead.");
SourceGeneratorMain.main(args);
}
/**
* Returns the {@link JClassRegistry} instance associated with this source generator.
* @return the {@link JClassRegistry} instance currently in use.
*/
public JClassRegistry getXMLInfoRegistry() {
return _xmlInfoRegistry;
}
/**
* Sets the jclassPrinter type.
* @param jClassPrinterType The string identifier of the printer to use.
*/
public final void setJClassPrinterType(final String jClassPrinterType) {
LOG.info("Setting JClass printing mode " + jClassPrinterType);
_jclassPrinterType = jClassPrinterType;
if (_singleClassGenerator != null) {
_singleClassGenerator.setJClassPrinterType(jClassPrinterType);
}
}
/**
* Indicates whether classes should be created for imported XML schemas as well.
* @return true if classes should be created for imported schemas.
*/
public boolean getGenerateImportedSchemas() {
return _generateImported;
}
/**
* Returns the registry for {@link JClassPrinterFactory} instances.
* @return the registry for {@link JClassPrinterFactory} instances.
*/
public JClassPrinterFactoryRegistry getJClassPrinterFactoryRegistry() {
return _jclassPrinterFactoryRegistry;
}
} //-- SourceGenerator