001: /*
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2004 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: CastorSourceGenTask.java 6410 2006-11-17 12:06:24Z wguttmn $
044: */
045: package org.castor.anttask;
046:
047: import java.io.File;
048: import java.io.FileInputStream;
049: import java.io.FileNotFoundException;
050: import java.io.IOException;
051: import java.util.Properties;
052: import java.util.Vector;
053:
054: import org.apache.tools.ant.BuildException;
055: import org.apache.tools.ant.DirectoryScanner;
056: import org.apache.tools.ant.taskdefs.MatchingTask;
057: import org.apache.tools.ant.types.FileSet;
058: import org.exolab.castor.builder.SourceGenerator;
059: import org.exolab.castor.builder.binding.ExtendedBinding;
060: import org.exolab.castor.builder.factory.FieldInfoFactory;
061: import org.exolab.castor.util.LocalConfiguration;
062: import org.exolab.castor.xml.XMLException;
063: import org.exolab.castor.xml.schema.Schema;
064: import org.exolab.castor.xml.schema.reader.Sax2ComponentReader;
065: import org.exolab.castor.xml.schema.reader.SchemaUnmarshaller;
066: import org.xml.sax.InputSource;
067: import org.xml.sax.Parser;
068: import org.xml.sax.SAXException;
069:
070: /**
071: * An <a href="http://ant.apache.org/">Ant</a> task to call the Castor
072: * Source Generator. It can be passed a file, a directory, a Fileset or all
073: * three.
074: *
075: * @author <a href="mailto:joel.farquhar@montage-dmc.com">Joel Farquhar</a>
076: * @author <a href="mailto:ferret AT frii DOT com">Bruce Snyder</a>
077: * @version $Revision: 6543 $ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
078: */
079: public final class CastorCodeGenTask extends MatchingTask {
080: //--------------------------------------------------------------------------
081:
082: /** Msg indicating class descriptor generation has been disabled. */
083: private static final String DISABLE_DESCRIPTORS_MSG = "Disabling generation of Class descriptors";
084:
085: /** Msg indicating marshaling framework code will not be generated. */
086: private static final String DISABLE_MARSHAL_MSG = "Disabling generation of Marshaling framework methods (marshal, unmarshal, validate).";
087:
088: /** Msg indicating that castor-testable code will be generated. */
089: private static final String CASTOR_TESTABLE_MSG = "The generated classes will implement org.exolab.castor.tests.CastorTestable";
090:
091: /** Error message -- invalid line seperator. */
092: private static final String INVALID_LINESEP_MSG = "Invalid value for lineseparator, must be win, unix, or mac.";
093:
094: /** Error message -- no schemas to run code generator on. */
095: private static final String NO_SCHEMA_MSG = "At least one of the file or dir attributes, or a fileset element, must be set.";
096:
097: //--------------------------------------------------------------------------
098:
099: /** If processing one schema file, this lists the file. */
100: private File _schemaFile = null;
101:
102: /** If processing all schemas in a directory, this lists the directory. */
103: private File _schemaDir = null;
104:
105: /** If processing a fileset, this lists the fileset. */
106: private Vector _schemaFilesets = new Vector();
107:
108: // Begin Source Generator parameters
109: /** The package that generated code will belong to. */
110: private String _srcpackage;
111:
112: /** The directory that code will be generated into. */
113: private String _todir;
114:
115: /** Binding file for the code generator. */
116: private String _bindingfile;
117:
118: /** Information about how to generate collections. */
119: private String _types;
120:
121: /** Line seperator to use for generated code. */
122: private String _lineseparator;
123:
124: /** If true, the code generator will be verbose. */
125: private boolean _verbose;
126:
127: /** If true, non-fatal warnings will be suppressed. Also, existing source
128: * files will be silently overwritten. */
129: private boolean _warnings = true;
130:
131: /** If true, class descriptors will not be generated. */
132: private boolean _nodesc;
133:
134: /** If true, marshaling code will not be generated. */
135: private boolean _nomarshal;
136:
137: /** If true, Castor's CTF testable framework code will be generated. */
138: private boolean _testable;
139:
140: /** Whether to generate code for imported schemas, too. */
141: private boolean _generateImportedSchemas;
142:
143: /** Whether to generate SAX-1 compliant code. */
144: private boolean _sax1;
145:
146: /** Whether enumerated type lookup should be performed in a case insensitive manner. */
147: private boolean _caseInsensitive;
148:
149: /** CastorBuilderProperties file. */
150: private String _properties;
151:
152: /** The name conflict strategy to use */
153: private String _nameConflictStrategy = "warnViaConsoleDialog";
154:
155: /** Name of the automatic clas name conflict strategy to use */
156: private String _automaticConflictStrategy = "xpath";
157:
158: /** SourceGenerator instance. */
159: private SourceGenerator _sgen;
160:
161: //--------------------------------------------------------------------------
162:
163: /**
164: * No-arg constructor.
165: */
166: public CastorCodeGenTask() {
167: // Nothing needed
168: }
169:
170: //--------------------------------------------------------------------------
171:
172: /**
173: * Sets the individual schema that will have code generated for it.
174: *
175: * @param file One schema file.
176: */
177: public void setFile(final File file) {
178: _schemaFile = file;
179: }
180:
181: /**
182: * Sets the directory such that all schemas in this directory will have code
183: * generated for them.
184: *
185: * @param dir The directory containing schemas to process.
186: */
187: public void setDir(final File dir) {
188: _schemaDir = dir;
189: }
190:
191: /**
192: * Adds a fileset to process that contains schemas to process.
193: *
194: * @param set An individual file set containing schemas.
195: */
196: public void addFileset(final FileSet set) {
197: _schemaFilesets.addElement(set);
198: }
199:
200: /**
201: * Sets the package that generated code will belong to.
202: *
203: * @param pack The package that generated code will belong to.
204: */
205: public void setPackage(final String pack) {
206: _srcpackage = pack;
207: }
208:
209: /**
210: * Sets the directory into which code will be generated.
211: *
212: * @param dest The directory into which code will be generated.
213: */
214: public void setTodir(final String dest) {
215: _todir = dest;
216: }
217:
218: /**
219: * Sets the binding file to be used for code generation.
220: *
221: * @param bindingfile The binding file to be used for code generation.
222: */
223: public void setBindingfile(final String bindingfile) {
224: _bindingfile = bindingfile;
225: }
226:
227: /**
228: * Sets the line seperator to use for code generation.
229: *
230: * @param ls The line seperator to use for code generation.
231: */
232: public void setLineseparator(final String ls) {
233: _lineseparator = ls;
234: }
235:
236: /**
237: * Sets the type factory for code generation.
238: *
239: * @param tf The type factory to use for code generation.
240: */
241: public void setTypes(final String tf) {
242: _types = (tf.equals("j2")) ? "arraylist" : tf;
243: }
244:
245: /**
246: * Sets whether or not code generation gives extra information about its work.
247: *
248: * @param b If true, the code generator will be verbose.
249: */
250: public void setVerbose(final boolean b) {
251: _verbose = b;
252: }
253:
254: /**
255: * Sets the name conflict strategy to use.
256: *
257: * @param nameConflictStrategy The name conflict strategy to use
258: */
259: public void setNameConflictStrategy(
260: final String nameConflictStrategy) {
261: _nameConflictStrategy = nameConflictStrategy;
262: }
263:
264: /**
265: * Sets the name conflict strategy to use.
266: *
267: * @param automaticConflictStrategy The automatic class name conflict strategy to use
268: */
269: public void setAutomaticConflictStrategy(
270: final String automaticConflictStrategy) {
271: _automaticConflictStrategy = automaticConflictStrategy;
272: }
273:
274: /**
275: * Sets whether or not non-fatal warnings should be suppressed.
276: *
277: * @param b If true, non-fatal warnings will be suppressed. This additionally
278: * means that existing source files will be silently overwritten.
279: */
280: public void setWarnings(final boolean b) {
281: _warnings = b;
282: }
283:
284: /**
285: * Sets whether or not class descriptors are generated.
286: *
287: * @param b If true, class descriptors are generated.
288: */
289: public void setNodesc(final boolean b) {
290: _nodesc = b;
291: }
292:
293: /**
294: * Sets whether or not marshaling methods are generated.
295: *
296: * @param b If true, marshaling methods are generated.
297: */
298: public void setNomarshal(final boolean b) {
299: _nomarshal = b;
300: }
301:
302: /**
303: * Sets whether CTF framework code is generated.
304: *
305: * @param b If true, the generated code will be instrumented for the CTF.
306: */
307: public void setTestable(final boolean b) {
308: _testable = b;
309: }
310:
311: /**
312: * Controls whether to generate code for imported schemas as well.
313: *
314: * @param generateImportedSchemas True if code should be generated for imported schemas.
315: */
316: public void setGenerateImportedSchemas(
317: final boolean generateImportedSchemas) {
318: _generateImportedSchemas = generateImportedSchemas;
319: }
320:
321: /**
322: * Controls whether to generate SAX-1 compliant code.
323: *
324: * @param sax1 True if SAX-1 compliant code should be generated.
325: */
326: public void setSAX1(final boolean sax1) {
327: _sax1 = sax1;
328: }
329:
330: /**
331: * Controls whether enumerated type lookup should be performed in a case
332: * insensitive manner.
333: *
334: * @param caseInsensitive True if enumerated type lookup should be performed in a case
335: * insensitive manner
336: */
337: public void setCaseInsensitive(final boolean caseInsensitive) {
338: _caseInsensitive = caseInsensitive;
339: }
340:
341: /**
342: * Sets the file to use for castor builder properties.
343: *
344: * @param properties The properties to use.
345: */
346: public void setProperties(final String properties) {
347: _properties = properties;
348: }
349:
350: //--------------------------------------------------------------------------
351:
352: /**
353: * Configured the code generator. If anything goes wrong during configuration of the
354: * Ant task a BuildException will be thrown.
355: */
356: private void config() {
357: // Create Source Generator with appropriate type factory
358: if (_types != null) {
359: FieldInfoFactory factory;
360: try {
361: factory = new FieldInfoFactory(_types);
362: _sgen = new CastorSourceGeneratorWrapper(factory);
363: } catch (Exception e) {
364: try {
365: factory = (FieldInfoFactory) Class.forName(_types)
366: .newInstance();
367: _sgen = new CastorSourceGeneratorWrapper(factory);
368: } catch (Exception e2) {
369: throw new BuildException("Invalid types \""
370: + _types + "\": " + e.getMessage());
371: }
372: }
373: } else {
374: // default
375: _sgen = new CastorSourceGeneratorWrapper();
376: }
377:
378: // Set Line Separator
379: String lineSep = System.getProperty("line.separator");
380: if (_lineseparator != null) {
381: if ("win".equals(_lineseparator)) {
382: log("Using Windows style line separation.");
383: lineSep = "\r\n";
384: } else if ("unix".equals(_lineseparator)) {
385: log("Using UNIX style line separation.");
386: lineSep = "\n";
387: } else if ("mac".equals(_lineseparator)) {
388: log("Using Macintosh style line separation.");
389: lineSep = "\r";
390: } else {
391: throw new BuildException(INVALID_LINESEP_MSG);
392: }
393: }
394: _sgen.setLineSeparator(lineSep);
395:
396: _sgen.setDestDir(_todir);
397:
398: if (_bindingfile != null) {
399: _sgen.setBinding(_bindingfile);
400: }
401:
402: _sgen.setVerbose(_verbose);
403: _sgen.setSuppressNonFatalWarnings(!_warnings);
404:
405: _sgen.setDescriptorCreation(!_nodesc);
406: if (_nodesc) {
407: log(DISABLE_DESCRIPTORS_MSG);
408: }
409:
410: _sgen.setCreateMarshalMethods(!_nomarshal);
411: if (_nomarshal) {
412: log(DISABLE_MARSHAL_MSG);
413: }
414:
415: _sgen.setGenerateImportedSchemas(_generateImportedSchemas);
416:
417: _sgen.setSAX1(_sax1);
418:
419: _sgen.setCaseInsensitive(_caseInsensitive);
420:
421: _sgen.setNameConflictStrategy(_nameConflictStrategy);
422:
423: _sgen.setClassNameConflictResolver(_automaticConflictStrategy);
424:
425: _sgen.setTestable(_testable);
426: if (this ._testable) {
427: log(CASTOR_TESTABLE_MSG);
428: }
429:
430: // Set Builder Properties;
431: if (_properties != null) {
432: String filePath = new File(_properties).getAbsolutePath();
433: Properties customProperties = new Properties();
434: try {
435: customProperties.load(new FileInputStream(filePath));
436: } catch (FileNotFoundException e) {
437: throw new BuildException("Properties file \""
438: + filePath + "\" not found");
439: } catch (IOException e) {
440: throw new BuildException(
441: "Can't read properties file \"" + filePath
442: + "\": " + e);
443: }
444: _sgen.setDefaultProperties(customProperties);
445: }
446: }
447:
448: /**
449: * Runs source generation. If anything goes wrong during source generation a
450: * BuildException will be thrown.
451: *
452: * @param filePath an individual Schema to generate code for.
453: */
454: private void processFile(final String filePath) {
455: log("Processing " + filePath);
456: try {
457: _sgen.generateSource(filePath, _srcpackage);
458: } catch (FileNotFoundException e) {
459: String message = "XML Schema file \""
460: + _schemaFile.getAbsolutePath() + "\" not found.";
461: log(message);
462: throw new BuildException(message);
463: } catch (Exception iox) {
464: throw new BuildException(iox);
465: }
466: }
467:
468: /**
469: * Public execute method -- entry point for the Ant task. Loops over all
470: * schema that need code generated and creates needed code generators, then
471: * executes them. If anything goes wrong during execution of the Ant task a
472: * BuildException will be thrown.
473: *
474: * @see org.apache.tools.ant.Task#execute()
475: */
476: public void execute() {
477: // Must have something to run the source generator on
478: if (_schemaFile == null && _schemaDir == null
479: && _schemaFilesets.size() == 0) {
480: throw new BuildException(NO_SCHEMA_MSG);
481: }
482:
483: try {
484: config();
485:
486: // Run source generator on file
487: if (_schemaFile != null) {
488: processFile(_schemaFile.getAbsolutePath());
489: }
490:
491: // Run source generator on all files in directory
492: if (_schemaDir != null && _schemaDir.exists()
493: && _schemaDir.isDirectory()) {
494: DirectoryScanner ds = this
495: .getDirectoryScanner(_schemaDir);
496:
497: String[] files = ds.getIncludedFiles();
498: for (int i = 0; i < files.length; i++) {
499: String filePath = _schemaDir.getAbsolutePath()
500: + File.separator + files[i];
501: processFile(filePath);
502: }
503: }
504:
505: // Run source generator on all files in FileSet
506: for (int i = 0; i < _schemaFilesets.size(); i++) {
507: FileSet fs = (FileSet) _schemaFilesets.elementAt(i);
508: DirectoryScanner ds = fs
509: .getDirectoryScanner(getProject());
510: File subdir = fs.getDir(getProject());
511:
512: String[] files = ds.getIncludedFiles();
513: for (int j = 0; j < files.length; j++) {
514: String filePath = subdir.getAbsolutePath()
515: + File.separator + files[j];
516: processFile(filePath);
517: }
518: }
519: } finally {
520: _sgen = null;
521: }
522: }
523:
524: //--------------------------------------------------------------------------
525:
526: /**
527: * Override Castor's SourceGenerator to inject exception handling.
528: * Code based on castor-0.9.5.3-xml.jar.
529: */
530: private final class CastorSourceGeneratorWrapper extends
531: SourceGenerator {
532: /**
533: * No-arg constructor.
534: */
535: public CastorSourceGeneratorWrapper() {
536: super ();
537: }
538:
539: /**
540: * Constructs a source generator with the provided FieldInfoFactory.
541: *
542: * @param fieldInfoFactory A FieldInfoFactory to use for collections.
543: */
544: public CastorSourceGeneratorWrapper(
545: final FieldInfoFactory fieldInfoFactory) {
546: super (fieldInfoFactory);
547: }
548:
549: /**
550: * Constructs a source generator with the provided FieldInfoFactory and
551: * binding file.
552: *
553: * @param fieldInfoFactory a FieldInfoFactory to use for collections.
554: * @param extendedBinding binding information for the code generator.
555: */
556: public CastorSourceGeneratorWrapper(
557: final FieldInfoFactory fieldInfoFactory,
558: final ExtendedBinding extendedBinding) {
559: super (fieldInfoFactory, extendedBinding);
560: }
561:
562: /**
563: * Generate source. If anything goes wrong during generation of source a
564: * BuildException will be thrown.
565: *
566: * @param source an individual schema to process.
567: * @param packageName the package name for generated code.
568: *
569: * @see org.exolab.castor.builder.SourceGenerator
570: * #generateSource(org.xml.sax.InputSource, java.lang.String)
571: */
572: public void generateSource(final InputSource source,
573: final String packageName) {
574: Parser parser = null;
575: try {
576: parser = LocalConfiguration.getInstance().getParser();
577: } catch (RuntimeException e) {
578: throw new BuildException(
579: "Unable to create SAX parser.", e);
580: }
581: if (parser == null) {
582: throw new BuildException("Unable to create SAX parser.");
583: }
584:
585: SchemaUnmarshaller schemaUnmarshaller = null;
586: try {
587: schemaUnmarshaller = new SchemaUnmarshaller();
588: } catch (XMLException e) {
589: throw new BuildException(
590: "Unable to create schema unmarshaller.", e);
591: }
592:
593: Sax2ComponentReader handler = new Sax2ComponentReader(
594: schemaUnmarshaller);
595: parser.setDocumentHandler(handler);
596: parser.setErrorHandler(handler);
597: try {
598: parser.parse(source);
599: } catch (IOException e) {
600: String msg = "Can't read input file "
601: + source.getSystemId() + ".\n" + e;
602: throw new BuildException(msg, e);
603: } catch (SAXException e) {
604: String msg = "Can't parse input file "
605: + source.getSystemId() + ".\n" + e;
606: throw new BuildException(msg, e);
607: }
608: Schema schema = schemaUnmarshaller.getSchema();
609: try {
610: generateSource(schema, packageName);
611: } catch (Exception iox) {
612: throw new BuildException(iox);
613: }
614: }
615: }
616:
617: //--------------------------------------------------------------------------
618: }
|