View Javadoc
1   /*
2    * Redistribution and use of this software and associated documentation ("Software"), with or
3    * without modification, are permitted provided that the following conditions are met:
4    *
5    * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6    * must also contain a copy of this document.
7    *
8    * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9    * conditions and the following disclaimer in the documentation and/or other materials provided with
10   * the distribution.
11   *
12   * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13   * without prior written permission of Intalio, Inc. For written permission, please contact
14   * info@exolab.org.
15   *
16   * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17   * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18   * Intalio, Inc.
19   *
20   * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21   *
22   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29   * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   *
31   * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
32   *
33   * This file was originally developed by Keith Visco during the course of employment at Intalio Inc.
34   * All portions of this file developed by Keith Visco after Jan 19 2005 are Copyright (C) 2005 Keith
35   * Visco. All Rights Reserved.
36   *
37   * $Id: $
38   */
39  package org.exolab.castor.builder;
40  
41  import java.io.PrintWriter;
42  import java.util.Properties;
43  
44  import org.exolab.castor.builder.binding.BindingException;
45  import org.exolab.castor.builder.binding.BindingLoader;
46  import org.exolab.castor.builder.factory.FieldInfoFactory;
47  import org.exolab.castor.util.CommandLineOptions;
48  import org.xml.sax.InputSource;
49  
50  /**
51   * Main line method for command-line invokation of the source generation tool. Because this class
52   * exists only to provide a main(String[]) for command-line use, all methods are static.
53   *
54   * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> - Main author.
55   * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a> - Contributions.
56   * @author <a href="mailto:nsgreen@thazar.com">Nathan Green</a> - Contributions.
57   * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a> - Cut out of SourceGenerator.java
58   * @version $Revision: 0000 $ $Date: $
59   */
60  public final class SourceGeneratorMain {
61    // --------------------------/
62    // - Command line arguments -/
63    // --------------------------/
64  
65    private static final String ARGUMENT_BINDING_FILENAME = "binding-file";
66    private static final String ARGUMENT_CASE_INSENSITIVE = "case-insensitive";
67    private static final String ARGUMENT_DESTINATION_DIR = "dest";
68    private static final String ARGUMENT_DISABLE_DESCRIPTORS = "nodesc";
69    private static final String ARGUMENT_FORCE = "f";
70    private static final String ARGUMENT_GENERATE_IMPORTED_SCHEMAS = "generateImportedSchemas";
71    private static final String ARGUMENT_GENERATE_MAPPING = "gen-mapping";
72    private static final String ARGUMENT_HELP = "h";
73    private static final String ARGUMENT_INPUT = "i";
74    private static final String ARGUMENT_INPUT_SOURCE = "is";
75    private static final String ARGUMENT_LINE_SEPARATOR = "line-separator";
76    private static final String ARGUMENT_NOMARSHALL = "nomarshall";
77    private static final String ARGUMENT_PACKAGE = "package";
78    private static final String ARGUMENT_RESOURCES_DESTINATION_DIR = "resourcesDestination";
79    private static final String ARGUMENT_SAX1 = "sax1";
80    private static final String ARGUMENT_TESTABLE = "testable";
81    private static final String ARGUMENT_TYPES = "types";
82    private static final String ARGUMENT_TYPES_DEPRECATED = "type-factory";
83    private static final String ARGUMENT_TYPES_JAVA2 = "j2";
84    private static final String ARGUMENT_VERBOSE = "verbose";
85    private static final String ARGUMENT_FAIL_ON_ERROR = "fail";
86    private static final String ARGUMENT_NAME_CONFLICT_STRATEGY = "nameConflictStrategy";
87    private static final String ARGUMENT_NAME_JCLASSPRINTER = "classPrinter";
88    private static final String ARGUMENT_USE_OLD_FIELD_NAMING = "useOldFieldNaming";
89  
90    private static final String ARG_VALUE_LINE_SEPARATION_MAC = "mac";
91    private static final String ARG_VALUE_LINE_SEPARATION_UNIX = "unix";
92    private static final String ARG_VALUE_LINE_SEPARATION_WIN = "win";
93  
94    // -------------------------/
95    // - Command line messages -/
96    // -------------------------/
97  
98    /** Message used when descriptor creation is disabled. */
99    private static final String DISABLE_DESCRIPTORS_MSG =
100       "Disabling generation of Class descriptors.";
101 
102   /** Message used when marshaling methods creation is disabled. */
103   private static final String DISABLE_MARSHALL_MSG =
104       "Disabling generation of Marshaling framework methods (marshal, unmarshal, validate).";
105 
106   /** Message used when implementing CastorTestable. */
107   private static final String CASTOR_TESTABLE_MSG =
108       "The generated classes will implement org.castor.xmlctf.CastorTestable.";
109 
110   /** Message used when using SAX1. */
111   private static final String SAX1_MSG = "The generated classes will use SAX 1.";
112 
113   private static final String GENERATE_IMPORT_MSG =
114       "Imported XML Schemas will be processed automatically.";
115 
116   private static final String CASE_INSENSITIVE_MSG =
117       "The generated classes will use a case insensitive method "
118           + "for looking up enumerated type values.";
119 
120   private static final String TYPE_FACTORY_ARG_MSG =
121       "The argument '-type-factory' is deprecated; please use '-types' in its place.";
122 
123   private static final String SUPPRESS_NON_FATAL_WARN_MSG = "Suppressing non fatal warnings.";
124 
125   private static final String GENERATING_MAPPING_FILE_MSG = "Generating mapping file: ";
126 
127   private static final String BINDING_FILE_ERROR1_MSG = "Unable to load binding file ";
128 
129   private static final String BINDING_FILE_ERROR2_MSG = " due to the following Exception:";
130 
131   private static final String BINDING_FILE_ERROR3_MSG = "No binding file will be used.";
132 
133   private static final String INVALID_TYPES_OPTION_MSG = "Invalid option for '-types': ";
134 
135   private static final String DEFAULT_FIELD_INFO_MSG = "Using default source generator types.";
136 
137   private static final String LINE_SEPARATION_WIN_MSG = "Using Windows style line separation.";
138 
139   private static final String LINE_SEPARATION_UNIX_MSG = "Using UNIX style line separation.";
140 
141   private static final String LINE_SEPARATION_MAC_MSG = "Using Macintosh style line separation.";
142 
143   private static final String DEFAULT_LINE_SEPARATOR_MSG =
144       "Using default line separator for this platform";
145 
146   private static final String INVALID_LINE_SEPARATOR_MSG = "Invalid option for line-separator: ";
147 
148   private static final String NAME_CONFLICT_STRATEGY_MSG = "Using name conflict strategy ";
149 
150   private static final String JCLASSPRINTER_TYPE_MSG = "Using JClass printing type ";
151 
152   private static final String USING_SEPARATE_RESOURCES_DIRECTORY =
153       "Using a separate destination for resources.";
154 
155   private static final String USE_OLD_FIELD_NAMING_MSG = "Using old Java field naming conventions";
156 
157   private static final String USE_NEW_FIELD_NAMING_MSG = "Using new Java field naming conventions";
158 
159   /** The full set of command-line options. */
160   private static final CommandLineOptions ALL_OPTIONS = setupCommandLineOptions();
161 
162   /**
163    * As a static utility class, we want a private constructor.
164    */
165   private SourceGeneratorMain() {
166     // Private constructor
167   }
168 
169   //////////////////
170   // MAIN METHOD //
171   //////////////////
172 
173   /**
174    * Parses the command line, converting everything into the proper form for the source generation
175    * main class, then invokes source generation.
176    *
177    * @param args the String[] consisting of the command line arguments
178    */
179   public static void main(final String[] args) {
180     // -- Process the specified command line options
181     Properties options = ALL_OPTIONS.getOptions(args);
182 
183     // -- check for help option
184     if (options.getProperty(ARGUMENT_HELP) != null) {
185       PrintWriter pw = new PrintWriter(System.out, true);
186       ALL_OPTIONS.printHelp(pw);
187       return;
188     }
189 
190     // -- Make sure we have a schema to work on
191     String schemaFilename = options.getProperty(ARGUMENT_INPUT);
192     String schemaURL = options.getProperty(ARGUMENT_INPUT_SOURCE);
193 
194     if (schemaFilename == null && schemaURL == null) {
195       System.out.println(SourceGenerator.APP_NAME);
196       ALL_OPTIONS.printUsage(new PrintWriter(System.out));
197       return;
198     }
199 
200     // Instantiate our SourceGenerator
201     FieldInfoFactory factory = getTypeFactory(options);
202     SourceGenerator sgen = (factory == null) ? new SourceGenerator() : new SourceGenerator(factory);
203 
204     // Everything below here sets options on our SourceGenerator
205 
206     sgen.setLineSeparator(getLineSeparator(options.getProperty(ARGUMENT_LINE_SEPARATOR)));
207     sgen.setDestDir(options.getProperty(ARGUMENT_DESTINATION_DIR));
208 
209     // set a resource destination if specified
210     String resourcesDestination = options.getProperty(ARGUMENT_RESOURCES_DESTINATION_DIR);
211     if (resourcesDestination != null) {
212       sgen.setResourceDestination(resourcesDestination);
213       System.out.print("-- ");
214       System.out.println(USING_SEPARATE_RESOURCES_DIRECTORY);
215     }
216 
217     sgen.setVerbose(options.getProperty(ARGUMENT_VERBOSE) != null);
218     sgen.setFailOnFirstError(options.getProperty(ARGUMENT_FAIL_ON_ERROR) != null);
219 
220     boolean force = (options.getProperty(ARGUMENT_FORCE) != null);
221     sgen.setSuppressNonFatalWarnings(force);
222     if (force) {
223       System.out.print("-- ");
224       System.out.println(SUPPRESS_NON_FATAL_WARN_MSG);
225     }
226 
227     if (options.getProperty(ARGUMENT_DISABLE_DESCRIPTORS) != null) {
228       sgen.setDescriptorCreation(false);
229       System.out.print("-- ");
230       System.out.println(DISABLE_DESCRIPTORS_MSG);
231     }
232 
233     String mappingFilename = options.getProperty(ARGUMENT_GENERATE_MAPPING);
234     if (mappingFilename != null) {
235       sgen.setGenerateMappingFile(true);
236       if (mappingFilename.length() > 0) {
237         sgen.setMappingFilename(mappingFilename);
238       }
239       System.out.print("-- ");
240       System.out.println(GENERATING_MAPPING_FILE_MSG + "'" + mappingFilename + "'");
241     }
242 
243     if (options.getProperty(ARGUMENT_NOMARSHALL) != null) {
244       sgen.setCreateMarshalMethods(false);
245       System.out.print("-- ");
246       System.out.println(DISABLE_MARSHALL_MSG);
247     }
248 
249     if (options.getProperty(ARGUMENT_TESTABLE) != null) {
250       sgen.setTestable(true);
251       System.out.print("-- ");
252       System.out.println(CASTOR_TESTABLE_MSG);
253     }
254 
255     if (options.getProperty(ARGUMENT_SAX1) != null) {
256       sgen.setSAX1(true);
257       System.out.print("-- ");
258       System.out.println(SAX1_MSG);
259     }
260 
261     if (options.getProperty(ARGUMENT_CASE_INSENSITIVE) != null) {
262       sgen.setCaseInsensitive(true);
263       System.out.print("-- ");
264       System.out.println(CASE_INSENSITIVE_MSG);
265     }
266 
267     String nameConflictStrategy = options.getProperty(ARGUMENT_NAME_CONFLICT_STRATEGY);
268     if (nameConflictStrategy != null) {
269       sgen.setNameConflictStrategy(nameConflictStrategy);
270       System.out.print("-- ");
271       System.out.println(CASE_INSENSITIVE_MSG + nameConflictStrategy);
272     }
273 
274     String jClassPrinterType = options.getProperty(ARGUMENT_NAME_JCLASSPRINTER);
275     if (jClassPrinterType != null) {
276       sgen.setJClassPrinterType(jClassPrinterType);
277       System.out.print("-- ");
278       System.out.println(JCLASSPRINTER_TYPE_MSG + jClassPrinterType);
279     }
280 
281     String bindingFilename = options.getProperty(ARGUMENT_BINDING_FILENAME);
282     if (bindingFilename != null) {
283       try {
284         sgen.setBinding(BindingLoader.createBinding(bindingFilename));
285       } catch (BindingException e) {
286         System.out.print("--");
287         System.out.println(
288             BINDING_FILE_ERROR1_MSG + "'" + bindingFilename + "'" + BINDING_FILE_ERROR2_MSG);
289         e.printStackTrace();
290         System.out.print("--");
291         System.out.println(BINDING_FILE_ERROR3_MSG);
292       }
293     }
294 
295     if (options.getProperty(ARGUMENT_GENERATE_IMPORTED_SCHEMAS) != null) {
296       sgen.setGenerateImportedSchemas(true);
297       System.out.print("-- ");
298       System.out.println(GENERATE_IMPORT_MSG);
299     }
300 
301     try {
302       if (schemaFilename != null) {
303         sgen.generateSource(schemaFilename, options.getProperty(ARGUMENT_PACKAGE));
304       } else if (schemaURL != null) {
305         sgen.generateSource(new InputSource(schemaURL), options.getProperty(ARGUMENT_PACKAGE));
306       }
307     } catch (Exception ex) {
308       ex.printStackTrace();
309     }
310   } // -- main
311 
312   /**
313    * Creates and returns a <code>FieldInfoFactory</code> (or null if none requested or if an error
314    * is encountered making the requested type) to be used during source generation. If the
315    * <code>-types</code> argument is not found, check for the deprecated but still supported
316    * <code>-type-factory</code> argument. If we find that, give a deprecation warning.
317    * <p>
318    * Once we've parsed the command-line arguments, if there are no arguemnts appropriate for a type
319    * factory, then we have nothing to do. Otherwise, try to instantiate the requested
320    * <code>FieldInfoFactory</code>. If the <code>FieldInfoFactory</code> throws an
321    * <code>IllegalArgumentException</code> then try to instantiate the class provided, which allows
322    * someone to provide their own implementation of a <code>FieldInfoFactory</code>, which must
323    * extend (@link org.exolab.castor.builder.FieldInfoFactory).
324    *
325    * @param options the full set of command-line options
326    * @return a FieldInfoFactory to be used during source generation, or null if the default should
327    *         be used
328    * @see org.exolab.castor.builder.FieldInfoFactory
329    */
330   private static FieldInfoFactory getTypeFactory(final Properties options) {
331     String typeFactory = options.getProperty(ARGUMENT_TYPES);
332     if (typeFactory == null) {
333       // This backwards-compatible option is retained temporarily
334       typeFactory = options.getProperty(ARGUMENT_TYPES_DEPRECATED);
335       if (typeFactory != null) {
336         System.out.print("-- ");
337         System.out.println(TYPE_FACTORY_ARG_MSG);
338       }
339     }
340 
341     // -- For backwards compatibility
342     if (typeFactory != null && typeFactory.equals(ARGUMENT_TYPES_JAVA2)) {
343       typeFactory = "arraylist";
344     }
345 
346     // If no command-line arguments, then we have nothing to do
347     if (typeFactory == null) {
348       return null;
349     }
350 
351     FieldInfoFactory factory = null;
352     try {
353       System.out.print("-- ");
354       if (Boolean.valueOf(options.getProperty(ARGUMENT_USE_OLD_FIELD_NAMING, "true"))) {
355         System.out.println(USE_OLD_FIELD_NAMING_MSG);
356         factory = new FieldInfoFactory(typeFactory);
357       } else {
358         System.out.println(USE_NEW_FIELD_NAMING_MSG);
359         factory = new FieldInfoFactory(typeFactory, false);
360       }
361     } catch (IllegalArgumentException e) {
362       try {
363         // Allow someone to provide their own FieldInfoFactory implementation
364         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
365         factory = (FieldInfoFactory) classLoader.loadClass(typeFactory).newInstance();
366       } catch (Exception e2) {
367         System.out.println("-- " + INVALID_TYPES_OPTION_MSG + typeFactory);
368         System.out.println("-- " + e.getMessage());
369         System.out.println("-- " + DEFAULT_FIELD_INFO_MSG);
370       }
371     }
372 
373     return factory;
374   }
375 
376   /**
377    * Parses the command-line argument for line separator style, handles bad values, and returns a
378    * value that the source generator will understand.
379    *
380    * @param lineSepStyle the command-line argument for line separation style
381    * @return the line separator string as the source generator understands it.
382    */
383   private static String getLineSeparator(final String lineSepStyle) {
384     String lineSep = System.getProperty("line.separator");
385     if (lineSepStyle != null) {
386       if (ARG_VALUE_LINE_SEPARATION_WIN.equals(lineSepStyle)) {
387         System.out.println("-- " + LINE_SEPARATION_WIN_MSG);
388         lineSep = "\r\n";
389       } else if (ARG_VALUE_LINE_SEPARATION_UNIX.equals(lineSepStyle)) {
390         System.out.println("-- " + LINE_SEPARATION_UNIX_MSG);
391         lineSep = "\n";
392       } else if (ARG_VALUE_LINE_SEPARATION_MAC.equals(lineSepStyle)) {
393         System.out.println("-- " + LINE_SEPARATION_MAC_MSG);
394         lineSep = "\r";
395       } else {
396         System.out.println("-- " + INVALID_LINE_SEPARATOR_MSG + "'" + lineSepStyle + "'");
397         System.out.println("-- " + DEFAULT_LINE_SEPARATOR_MSG);
398       }
399     }
400     return lineSep;
401   }
402 
403   /**
404    * Configures our command-line options object with the command line options that we recognize.
405    *
406    * @return a new CommandLineOptions object fully configured to parse our command line.
407    */
408   private static CommandLineOptions setupCommandLineOptions() {
409     CommandLineOptions allOptions = new CommandLineOptions();
410     String desc;
411 
412     // -- filename flag
413     desc = "Sets the filename for the schema used as input.";
414     allOptions.addFlag(ARGUMENT_INPUT, "schema filename", desc);
415 
416     // -- filename flag
417     desc = "Sets the input source for the schema used as input.";
418     allOptions.addFlag(ARGUMENT_INPUT_SOURCE, "input source for XML schema", desc);
419 
420     // -- package name flag
421     desc = "Sets the package name for generated code.";
422     allOptions.addFlag(ARGUMENT_PACKAGE, "package name", desc, true);
423 
424     // -- destination directory
425     desc = "Sets the destination output directory.";
426     allOptions.addFlag(ARGUMENT_DESTINATION_DIR, "destination directory", desc, true);
427 
428     // -- resources destination directory
429     desc = "Sets the destination output directory for resources.";
430     allOptions.addFlag(ARGUMENT_RESOURCES_DESTINATION_DIR, "resources destination directory", desc,
431         true);
432 
433     // -- line break flag
434     desc = "Sets the line separator style for the desired platform.";
435     allOptions.addFlag(ARGUMENT_LINE_SEPARATOR, "(unix | mac | win)", desc, true);
436 
437     // -- Force flag
438     desc = "Suppresses non fatal warnings, such as overwriting files.";
439     allOptions.addFlag(ARGUMENT_FORCE, "", desc, true);
440 
441     // -- Help flag
442     desc = "Displays this help screen.";
443     allOptions.addFlag(ARGUMENT_HELP, "", desc, true);
444 
445     // -- verbose flag
446     desc = "Prints out additional messages when creating source.";
447     allOptions.addFlag(ARGUMENT_VERBOSE, "", desc, true);
448 
449     // -- fail on first error flag
450     desc = "Causes source generation to fail on the first error encountered.";
451     allOptions.addFlag(ARGUMENT_FAIL_ON_ERROR, "", desc, true);
452 
453     // -- no descriptors flag
454     desc = "Disables the generation of the Class descriptors.";
455     allOptions.addFlag(ARGUMENT_DISABLE_DESCRIPTORS, "", desc, true);
456 
457     // -- mapping file flag
458     desc = "Indicates that a mapping file should be generated.";
459     allOptions.addFlag(ARGUMENT_GENERATE_MAPPING, "mapping filename", desc, true);
460 
461     // -- source generator types name flag
462     desc = "Sets the source generator types name (SGTypeFactory).";
463     allOptions.addFlag(ARGUMENT_TYPES, "types", desc, true);
464 
465     // -- We temporarily maintain backwards compatibility, but this argument is deprecated
466     desc = "";
467     allOptions.addFlag(ARGUMENT_TYPES_DEPRECATED, "collections class name", desc, true);
468 
469     // -- no marshaling framework methods
470     desc = "Disables the generation of the methods specific to the XML marshaling framework.";
471     allOptions.addFlag(ARGUMENT_NOMARSHALL, "", desc, true);
472 
473     // -- implements org.castor.xmlctf.CastorTestable?
474     desc = "Implements some specific methods to allow the generated classes"
475         + " to be used with Castor Testing Framework.";
476     allOptions.addFlag(ARGUMENT_TESTABLE, "", desc, true);
477 
478     // -- use SAX1?
479     desc = "Uses SAX 1 in the generated code.";
480     allOptions.addFlag(ARGUMENT_SAX1, "", desc, true);
481 
482     // -- Source Generator Binding
483     desc = "Sets the Source Generator Binding File name.";
484     allOptions.addFlag(ARGUMENT_BINDING_FILENAME, "filename", desc, true);
485 
486     // -- Generates sources for imported XML Schemas
487     desc = "Generates sources for imported XML schemas.";
488     allOptions.addFlag(ARGUMENT_GENERATE_IMPORTED_SCHEMAS, "", desc, true);
489 
490     // -- Sets enumerated type to use a case insensitive lookup
491     desc = "Sets enumerated types to use a case insensitive lookup.";
492     allOptions.addFlag(ARGUMENT_CASE_INSENSITIVE, "", desc);
493 
494     // -- Sets enumerated type to use a case insensitive lookup
495     desc = "Sets name conflict strategy to use (possible values are "
496         + "'informViaLog', 'warnViaConsoleDialog').";
497     allOptions.addFlag(ARGUMENT_NAME_CONFLICT_STRATEGY, "", desc);
498 
499     desc = "Selects the JClass printer type (default 'standard')";
500     allOptions.addFlag(ARGUMENT_NAME_JCLASSPRINTER, "<mode>", desc, true);
501 
502     desc = "Whether to use old Java field naming conventions (default to 'true')";
503     allOptions.addFlag(ARGUMENT_USE_OLD_FIELD_NAMING, "", desc, true);
504 
505     return allOptions;
506   }
507 
508 }