/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.tools.entitybuilder;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.exolab.castor.builder.FieldInfoFactory;
import org.exolab.castor.builder.SourceGenerator;
import org.exolab.castor.xml.schema.reader.SchemaUnmarshaller;
/**
* Generates Java binding classes from xml schema files using the Castor generator framework.
* Needs at least one configuration file for the mapping from xml namespaces to Java packages.
*/
public class CastorBuilder
{
/**
* @param args <code>args[0]</code>: xml config file (with path) complying to EntitybuilderSettings.xsd;
* for the schemas that need code generation; <br>
* <code>args[1]</code>: output directory under which the generated Java files will be put. <br>
* <code>args[2..n]</code>: -I schemaIncludeDirectory (optional)
*/
public static void main(String[] args)
{
// System.err.println("Running THE NEW CastorBuilder...");
// System.out.println("--- main args: ----");
// for (int i = 0; i < args.length; i++) {
// System.out.println(args[i]);
// }
// System.out.println("---- end main args --- ");
if (args.length < 2)
{
System.out.println(
"usage: java " + CastorBuilder.class.getName() + " configFile javaOutputDir [-I schemaIncludeDir]");
System.exit(1);
}
try
{
String configFileConcat = System.getProperty("ACS.schemaconfigfiles");
// System.out.println("ACS.schemaconfigfiles: '" + configFileConcat + "'");
CastorBuilder castorBuilder = new CastorBuilder();
castorBuilder.run0(args, configFileConcat);
}
catch (Exception ex)
{
System.err.println("schema compilation failed!");
ex.printStackTrace(System.err);
System.exit(1);
}
}
/**
* Parses the arguments given to <code>main</code> and then hands over control to method <code>run</code>.
* @param args as in main
* @param configFileConcat space-separated list of xsd binding config files for included schemas.
* @throws BindingException
* @throws FileNotFoundException
*/
public void run0(String[] args, String configFileConcat) throws BindingException, FileNotFoundException
{
// the xml config file that knows the schemas that should be compiled,
// as well as namespace and java package info.
File primaryConfigFile = new File(args[0]);
// the directory with the schemas that should be compiled
File schemaDir = primaryConfigFile.getParentFile();
// the root directory for Java class output
File javaOutputDir = new File(args[1]);
// -- config files for included schemas
ArrayList<String> otherConfigFileNames = new ArrayList<String>();
if (configFileConcat != null)
{
StringTokenizer includeSchemasConfigFilesTok = new StringTokenizer(configFileConcat, " ");
while (includeSchemasConfigFilesTok.hasMoreTokens())
{
String configFileName = includeSchemasConfigFilesTok.nextToken().trim();
if (configFileName.length() > 0) {
if (!configFileName.endsWith(".xml")) {
configFileName += ".xml";
}
otherConfigFileNames.add(configFileName);
// System.out.println("adding config file " + configFileName);
}
}
}
// include directories (both schema files and config files)
ArrayList<File> includeDirs = new ArrayList<File>();
boolean pendingInclude = false;
for (int argInd = 2; argInd < args.length; argInd++)
{
String arg = args[argInd].trim();
arg = (arg.charAt(0) == '"' ? arg.substring(1) : arg);
arg = (arg.charAt(arg.length()-1) == '"' ? arg.substring(0, arg.length()-1) : arg);
if (pendingInclude)
{
includeDirs.add(new File(arg));
pendingInclude = false;
}
else if (arg.startsWith("-I"))
{
if (arg.length() == 2)
{
// just -I, no path
pendingInclude = true;
}
else
{
// "-I/a/b/dir" or "-I /a/b/dir" is given as one option
includeDirs.add(new File(arg.substring(2)));
pendingInclude = false;
}
}
}
run(schemaDir, primaryConfigFile, otherConfigFileNames, includeDirs, javaOutputDir );
}
/**
* Runs the Castor code generator.
*
* @param schemaDir (base) directory where the xsd files are, for which code will be generated.
* @param primaryConfigFile config file for the schema code generation.
* Currently must be in the directory <code>schemaDir</code>.
* @param otherConfigFileNames Names without paths of schema code generation config files.
* While <code>primaryConfigFile</code> must contain the information for the schemas to compile directly,
* these config files have similar information for other schemas which are included by the "primary" schemas.
* This data is needed to generate correct Java packages of already existing binding classes.
* @param includeDirs directories from which other xsd files or config files should be included,
* with preference to directories that appear first in case of multiple occurences of the same file.
* @param javaOutputDir root directory under which the generated Java binding classes will be put
* @throws BindingException
* @throws FileNotFoundException
*/
public void run(File schemaDir, File primaryConfigFile, List<String> otherConfigFileNames, List<File> includeDirs, File javaOutputDir) throws BindingException, FileNotFoundException
{
// the xml config file that knows the schemas that should be compiled
if (!primaryConfigFile.exists()) {
throw new FileNotFoundException(
"invalid configuration file: " + primaryConfigFile.getAbsolutePath());
}
List<String> allConfigFileNames = new ArrayList<String>();
allConfigFileNames.add(primaryConfigFile.getName());
allConfigFileNames.addAll(otherConfigFileNames);
List<File> allIncludeDirs = new ArrayList<File>();
// the ALMA Makefile explicitly lists the local schema directory as an include dir,
// but it does not hurt to try adding it anyway
allIncludeDirs.add(schemaDir);
// in some cases the schema dir may be different from the primary config file's directory.
// Thus we add the latter explicity, because otherwise the config file would no longer be found.
allIncludeDirs.add(primaryConfigFile.getParentFile());
// and of course we must add the explicitly given include dirs
allIncludeDirs.addAll(includeDirs);
if (!javaOutputDir.exists()) {
System.out.println("will create output directory " + javaOutputDir.getAbsolutePath());
}
XsdFileFinder xsdFileFinder = new XsdFileFinder(allIncludeDirs, allConfigFileNames);
xsdFileFinder.setVerbose(false);
EntitybuilderConfig ebc = new EntitybuilderConfig();
ebc.load(primaryConfigFile, xsdFileFinder.getAllXsdConfigFiles());
// From the config files, the ebc has now learned about all schemas.
// We pass that knowledge to the AlmaURIResolver which will later find schema files for the Castor generator.
AlmaURIResolver uriResolver = new AlmaURIResolver(ebc.getSchemaName2File());
// Currently the URI resolver can be passed to SchemaUnmarshaller only through a ALMA patch in module Tools/castor;
// this is needed to search for imported/included schemas.
// Hopefully later castor will allow to pass in a URIResolver via SourceGenerator.
SchemaUnmarshaller.setDefaultURIResolver(uriResolver);
// Create Castor source generator and configure it
// Java2 collection types: Collection, ArrayList instead of Vector
FieldInfoFactory fieldInfoFactory = new org.exolab.castor.builder.FieldInfoFactory("arraylist");
SourceGenerator sgen = new SourceGenerator(fieldInfoFactory);
// set all namespace to package mappings (also for include/import schemas) for Castor
String[] allNamespaces = ebc.getAllNamespaces();
for (int i = 0; i < allNamespaces.length; i++) {
String jPackage = ebc.getJPackageForNamespace(allNamespaces[i]);
sgen.setNamespacePackageMapping(allNamespaces[i], jPackage);
}
// set all schema file to package mappings (also for included/imported schemas) -- Castor wants it both ways...
for (File schemaFile : ebc.getAllSchemaFiles()) {
String schemaName = schemaFile.getName();
String jPackage = ebc.getJPackageForSchema(schemaName);
// System.out.println("SourceGenerator#setLocationPackageMapping: " + schemaFileWithPath.getAbsolutePath() + " -> " + jPackage);
sgen.setLocationPackageMapping(schemaFile.getAbsolutePath(), jPackage);
}
// adjust options here
sgen.setDestDir(javaOutputDir.getAbsolutePath());
sgen.setLineSeparator(System.getProperty("line.separator"));
sgen.setSuppressNonFatalWarnings(true);
sgen.setVerbose(false);
sgen.setDescriptorCreation(true);
sgen.setCreateMarshalMethods(true);
sgen.setTestable(false);
// adjust properties (overwrite setting in org.exolab.castor.builder.castorbuilder.properties)
sgen.setEqualsMethod(false); // true: generate equals()
sgen.setClassDescFieldNames(false); // true: expose class members
sgen.setPrimitiveWrapper(false); // true: Integer instead of int (does not use null-check instead of hasXXX though!)
// schema files that need code generation
List<File> schemaFiles = ebc.getPrimarySchemaFiles();
for (File schemaFile : schemaFiles) {
String schemaPackage = ebc.getJPackageForSchema(schemaFile.getName());
generate(sgen, schemaFile, schemaPackage);
}
System.out.println("schema compile done!\n");
}
// private static boolean isIncludeDir(String includeDirName) {
// if (includeDirName != null)
// {
// File includeDir = new File(includeDirName);
// if (includeDir.exists() && includeDir.isDirectory() && !s_includeDirs.contains(includeDir))
// {
// s_includeDirs.add(includeDir);
// s_absFilePaths.clear();
// if (DEBUG)
// {
// System.out.println("appended include directory " + includeDir.getAbsolutePath());
// }
// }
// else if (DEBUG)
// {
// System.out.println("faulty or already existing include directory '" + includeDir.getAbsolutePath() +
// "' not appended.");
// }
// }
//
// }
private void generate(SourceGenerator sgen, File schemaFile, String packageName)
throws FileNotFoundException
{
if (!(schemaFile.exists() && schemaFile.isFile()))
{
throw new FileNotFoundException(
"unable to open XML schema file " + schemaFile.getAbsolutePath());
}
System.out.println(
"\n-- generating classes for " + schemaFile.getAbsolutePath() + " into " + packageName + " --");
sgen.generateSource(schemaFile.getAbsolutePath(), packageName);
}
}