Source code: org/apache/ws/jaxme/generator/XJCTask.java
1 /*
2 * Copyright 2003,2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15
16 */
17 package org.apache.ws.jaxme.generator;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Set;
28
29 import org.apache.ws.jaxme.generator.impl.GeneratorImpl;
30 import org.apache.ws.jaxme.generator.sg.SGFactoryChain;
31 import org.apache.ws.jaxme.generator.sg.SchemaSG;
32 import org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader;
33 import org.apache.ws.jaxme.generator.sg.impl.JaxMeSchemaReader;
34 import org.apache.ws.jaxme.js.JavaSource;
35 import org.apache.ws.jaxme.js.JavaSourceFactory;
36 import org.apache.ws.jaxme.js.TextFile;
37 import org.apache.ws.jaxme.logging.AntProjectLoggerFactory;
38 import org.apache.ws.jaxme.logging.LoggerAccess;
39 import org.apache.ws.jaxme.logging.LoggerFactory;
40 import org.apache.ws.jaxme.util.ClassLoader;
41 import org.apache.ws.jaxme.xs.parser.impl.LocSAXException;
42 import org.apache.tools.ant.BuildException;
43 import org.apache.tools.ant.DirectoryScanner;
44 import org.apache.tools.ant.Project;
45 import org.apache.tools.ant.Task;
46 import org.apache.tools.ant.types.Commandline;
47 import org.apache.tools.ant.types.FileSet;
48 import org.apache.tools.ant.types.Path;
49 import org.xml.sax.SAXParseException;
50
51
52 /** <p>An Ant task for running JaxMe, designed to be JAXB compatible.</p>
53 * <p>This task supports the following attributes:</p>
54 * <table border="1">
55 * <tr>
56 * <th>Name</th>
57 * <th>Description</th>
58 * <th>Required/Default</th>
59 * </tr>
60 * <tr>
61 * <td>schema</td>
62 * <td>Name of a schema file being compiled</td>
63 * <td>This or nested <schema> elements are required</td>
64 * </tr>
65 * <tr>
66 * <td>binding</td>
67 * <td>An external binding file being applied to the schema file</td>
68 * <td>No</td>
69 * </tr>
70 * <tr>
71 * <td>force</td>
72 * <td>Setting this option to true forces the up-to-date check to fail.
73 * This option is mainly useful while working on the JaxMe generator.
74 * For JaxMe users, which only change schema files, this option isn't of much
75 * use. It is designed for JaxMe developers.</td>
76 * <td>No, false</td>
77 * </tr>
78 * <tr>
79 * <td>package</td>
80 * <td>Specifies the generated Java sources package name. Overrides package specifications in
81 * the schema bindings, if any.</td>
82 * <td>No, a package may be specified in the schema bindings.</td>
83 * </tr>
84 * <tr>
85 * <td>target</td>
86 * <td>Specifies the target directory, where generated sources are being created. A package
87 * structure will be created below that directory. For example, with target="src" and
88 * package="org.acme", you will have files being created in "src/org/acme".</td>
89 * <td>No, defaults to the current directory</td>
90 * </tr>
91 * <tr>
92 * <td>readonly</td>
93 * <td>Generated Java source files are in read-only mode, if true is specified</td>
94 * <td>No, defaults to false</td>
95 * </tr>
96 * <tr>
97 * <td>extension</td>
98 * <td>If set to true, the XJC binding compiler will run in the extension mode.
99 * Otherwise, it will run in the strict conformance mode.</td>
100 * <td>No, defaults to false</td>
101 * </tr>
102 * <tr>
103 * <td>stackSize</td>
104 * <td>Specify the thread stack size for the XJC binding compiler (J2SE SDK v1.4 or higher).
105 * The XJC binding compiler can fail to compile large schemas with StackOverflowError and,
106 * in that case, this option can be used to extend the stack size. If unspecified, the default
107 * VM size is used. The format is equivalent to the -Xss command-line argument for Sun Microsystems JVM.
108 * This value can be specified in bytes (stackSize="2097152"), kilobytes (stackSize="2048kb"),
109 * or megabytes (stackSize="2mb").<br>
110 * This attribute is ignored by the JaxMe ant task and present for compatibility reasons only.</td>
111 * <td>No, defaults to false</td>
112 * </tr>
113 * <tr>
114 * <td>removeOldOutput</td>
115 * <td>If one or more nested <produces> elements are specified and this attribute is
116 * set to true, then the Ant task will ensure that only generated files will remain. In other
117 * words, if you had removed an element named "Foo" from the previous schema version, then the
118 * Ant task will remove "Foo.java".</td>
119 * <td>No, defaults to false</td>
120 * </tr>
121 * <tr>
122 * <td>validating</td>
123 * <td>Sets whether the XML schema parser is validating. By default it isn't.</td>
124 * <td>No, defaults to false</td>
125 * </tr>
126 * </table>
127 * <p>Besides the attributes, the ant task also supports the following nested elements:</p>
128 * <table border="1">
129 * <tr>
130 * <th>Name</th>
131 * <th>Description</th>
132 * <th>Required/Multiplicity</th>
133 * </tr>
134 * <tr>
135 * <td>schema</td>
136 * <td>Multiple schema files may be compiled in one or more nested <schema>
137 * elements. The element syntax is equivalent to a nested <fileset>.
138 * Use of a nested <schema> element is mutually exclusive with the use
139 * of a "schema" attribute.</td>
140 * <td>0 - Unbounded</td>
141 * </tr>
142 * <tr>
143 * <td>binding</td>
144 * <td>Multiple external binding files may be specified. The element syntax is equivalent
145 * to a nested <fileset>. Use of a nested <binding> element is
146 * mutually exclusive with the use of a "binding" attribute.</td>
147 * <td>0 - Unbounded</td>
148 * </tr>
149 * <tr>
150 * <td>classpath</td>
151 * <td>This nested element is ignored by the JaxMe ant task and exists for compatibility
152 * to the JAXB ant task only. In the case of JAXB it specifies a classpath for loading
153 * user defined types (required in the case of a <javaType> customization)
154 * </td>
155 * <td>0 - Unbounded</td>
156 * </tr>
157 * <tr>
158 * <td>arg</td>
159 * <td>This nested element is ignored by the JaxMe ant task and exists for compatibility
160 * to the JAXB ant task only. In the case of JAXB it specifies additional command line
161 * arguments being passed to the XJC. For details about the syntax, see the relevant
162 * section in the Ant manual.<br>
163 * This nested element can be used to specify various options not natively supported in
164 * the xjc Ant task. For example, currently there is no native support for the following
165 * xjc command-line options:
166 * <ul>
167 * <li>-nv</li>
168 * <li>-catalog</li>
169 * <li>-use-runtime</li>
170 * <li>-schema</li>
171 * <li>-dtd</li>
172 * <li>-relaxng</li>
173 * </ul>
174 * </td>
175 * <td>0 - Unbounded</td>
176 * </tr>
177 * <tr>
178 * <td>dtd</td>
179 * <td>If this nested element is used to specify, that the input files
180 * aren't instances of XML Schema, but DTD's. The nested element may
181 * have an attribute "targetNamespace", which specifies an optional
182 * target namespace.
183 * </td>
184 * <td>No</td>
185 * </tr>
186 * <tr>
187 * <td>depends</td>
188 * <td>By default the JaxMe Ant tasks up-to-date check considers the specified schema
189 * and binding files only. This is insufficient, if other schema files are included,
190 * imported or redefined.<br>
191 * The nested <depends> element allows to specify additional files to consider
192 * for the up-to-date check. Typically these are the additional schema files.<br>
193 * Syntactically the <depends> element specifies a nested <fileset>.</td>
194 * <td>0 - Unbounded</td>
195 * </tr>
196 * <tr>
197 * <td>produces</td>
198 * <td>Specifies the set of files being created by the JaxMe ant task. These files are
199 * considered as targets for the up-to-date check. The syntax of the <produces>
200 * element is equivalent to a nested <fileset>. However, you typically do not
201 * need to set the "dir" attribute, because it defaults to the target directory.</td>
202 * <td>0 - Unbounded</td>
203 * </tr>
204 * <tr>
205 * <td>property</td>
206 * <td>Sets a property value. These properties may be used by the various source
207 * generators to configure the behaviour. For example, the JDBC schema reader uses
208 * the options "jdbc.driver", "jdbc.url", "jdbc.user", and "jdbc.password" to
209 * configure the database connection. Each property must have attributes "name" (the
210 * property name) and "value" (the property value).</td>
211 * <td>0 - Unbounded</td>
212 * </tr>
213 * <tr>
214 * <td>schemaReader</td>
215 * <td>Configures the schema reader to use. Defaults to
216 * "org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader", which is the JAXB compliant
217 * schema reader. An alternative schema readers is, for example,
218 * "org.apache.ws.jaxme.generator.sg.impl.JaxMeSchemaReader" (a subclass of JAXBSchemaReader
219 * with JaxMe specific extensions).</td>
220 * <td>0 - 1</td>
221 * </tr>
222 * <tr>
223 * <td>sgFactoryChain</td>
224 * <td>If the schema reader is an instance of
225 * {@link org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader}, then you may
226 * add instances of {@link org.apache.ws.jaxme.generator.sg.SGFactoryChain} to
227 * the schema generation process. For example, such chains are used to create
228 * the persistency layer. The best example is the
229 * {@link org.apache.ws.jaxme.pm.generator.jdbc.JaxMeJdbcSG}, which is able to
230 * populate the schema with tables and columns read from a database via
231 * JDBC metadata.</td>
232 * <td>0 - Unbounded</td>
233 * </tr>
234 * </table>
235 * <p>By default, the JaxMe ant task will always run the generator and create new files. This
236 * is typically inappropriate for an ant script where your desire is to have as little
237 * modifications as possible, because new files also need to be recompiled, which is slow
238 * and time consuming.</p>
239 * <p>To achieve a better behaviour, use the nested <produces> and <depends> elements.
240 * If one or more <produces> element is specified, then an up-to-date check is performed
241 * as follows:
242 * <ol>
243 * <li>If either of the filesets specified by the <produces> elements is empty,
244 * then the binding compiler will run.</li>
245 * <li>Otherwise the sets of source and target files will be created. The set of source
246 * files is specified by the "schema" and "binding" attributes, and by the nested
247 * <schema>, <binding>, and <depends> elements. If any of the files
248 * in the source set is newer than any of the files in the target set, then the
249 * binding comoiler will run.</li>
250 * </ol>
251 *
252 * @author <a href="mailto:joe@ispsoft.de">Jochen Wiedmann</a>
253 */
254 public class XJCTask extends Task {
255 /** This class is used to store the nested element "dtd".
256 */
257 public static class Dtd {
258 private String targetNamespace;
259 /** Sets the target namespace being used.
260 */
261 public void setTargetNamespace(String pTargetNamespace) {
262 targetNamespace = pTargetNamespace;
263 }
264 /** Returns the target namespace being used.
265 */
266 public String getTargetNamespace() {
267 return targetNamespace;
268 }
269 }
270
271 public static class Property {
272 private String name;
273 private String value;
274 public void setName(String pName) { name = pName; }
275 public void setValue(String pValue) { value = pValue; }
276 public String getName() { return name; }
277 public String getValue() { return value; }
278 public void finish() {
279 if (name == null) {
280 throw new NullPointerException("Missing attribute: 'name'");
281 }
282 if (value == null) {
283 throw new NullPointerException("Missing attribute: 'value'");
284 }
285 }
286 }
287
288 public static class ClassType {
289 private String className;
290 public void setClassName(String pClassName) {
291 className = pClassName;
292 }
293 public String getClassName() {
294 return className;
295 }
296 public Object getInstance(Class pInstanceClass) {
297 if (className == null) {
298 throw new NullPointerException("Missing attribute: 'class'");
299 }
300 Class cl;
301 try {
302 cl = ClassLoader.getClass(className, pInstanceClass);
303 } catch (ClassNotFoundException e) {
304 throw new BuildException("Could not load class " + className, e);
305 } catch (IllegalArgumentException e) {
306 throw new BuildException(e);
307 }
308
309 try {
310 return cl.newInstance();
311 } catch (Exception e) {
312 throw new BuildException("The class " + className +
313 " could not be instantiated: " +
314 e.getMessage(), e);
315 }
316 }
317 }
318
319 private File binding, schema, target;
320 private String packageName;
321 private boolean readOnly, extension, removeOldOutput, force, isValidating;
322 private boolean isSettingLoggerFactory = true;
323 private String stackSize;
324 private List bindings = new ArrayList(), schemas = new ArrayList();
325 private List depends = new ArrayList(), produces = new ArrayList();
326 private List sgFactoryChains = new ArrayList();
327 private ClassType schemaReader;
328 private List properties = new ArrayList();
329 private Dtd dtd;
330
331 /** <p>Sets a property value. These properties may be used by the various source
332 * generators to configure the behaviour. For example, the JDBC schema reader uses
333 * the options "jdbc.driver", "jdbc.url", "jdbc.user", and "jdbc.password" to
334 * configure the database connection. Each property must have attributes "name" (the
335 * property name) and "value" (the property value).</p>
336 */
337 public Property createProperty() {
338 Property property = new Property();
339 properties.add(property);
340 return property;
341 }
342
343 /** <p>Returns the configured property values. These properties may be used by the various source
344 * generators to configure the behaviour. For example, the JDBC schema reader uses
345 * the options "jdbc.driver", "jdbc.url", "jdbc.user", and "jdbc.password" to
346 * configure the database connection. Each property must have attributes "name" (the
347 * property name) and "value" (the property value).</p>
348 */
349 public Property[] getProperties() {
350 return (Property[]) properties.toArray(new Property[properties.size()]);
351 }
352
353 /** <p>Configures the schema reader to use. Defaults to
354 * "org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader", which is the JAXB compliant
355 * schema reader. An alternative schema readers is, for example,
356 * "org.apache.ws.jaxme.generator.sg.impl.JaxMeSchemaReader" (a subclass of JAXBSchemaReader
357 * with JaxMe specific extensions).</p>
358 */
359 public ClassType createSchemaReader() {
360 if (schemaReader != null) {
361 throw new BuildException("Only one SchemaReader may be configured");
362 }
363 schemaReader = new ClassType();
364 return schemaReader;
365 }
366
367 /** <p>Returns the configured schema reader to use. Defaults to
368 * "org.apache.ws.jaxme.generator.sg.impl.JAXBSchemaReader", which is the JAXB compliant
369 * schema reader. An alternative schema readers is, for example,
370 * "org.apache.ws.jaxme.generator.sg.impl.JaxMeSchemaReader" (a subclass of JAXBSchemaReader
371 * with JaxMe specific extensions).</p>
372 */
373 public SchemaReader getSchemaReader() {
374 if (schemaReader == null) {
375 if (isExtension()) {
376 return new JaxMeSchemaReader();
377 } else {
378 return new JAXBSchemaReader();
379 }
380 } else {
381 return (SchemaReader) schemaReader.getInstance(SchemaReader.class);
382 }
383 }
384
385 /** <p>Configures a new instance of
386 * {@link org.apache.ws.jaxme.generator.sg.SGFactoryChain} being included into
387 * the schema generation process. This option is valid only, if the schema reader
388 * is an instance of {@link JAXBSchemaReader}, because its method
389 * {@link JAXBSchemaReader#addSGFactoryChain(Class)} must be invoked.</p>
390 * <p>The order of the chain elements may be significant. The schema reader
391 * itself will always be the last element in the chain.</p>
392 */
393 public ClassType createSGFactoryChain() {
394 ClassType result = new ClassType();
395 sgFactoryChains.add(result);
396 return result;
397 }
398
399 /** <p>Returns the array of configured instances of
400 * {@link org.apache.ws.jaxme.generator.sg.SGFactoryChain}. The order of
401 * the array is significant. The schema reader itself will always be the
402 * last element in the chain. Therefore, it is not present in the array.</p>
403 */
404 public ClassType[] getSGFactoryChains() {
405 return (ClassType[]) sgFactoryChains.toArray(new ClassType[sgFactoryChains.size()]);
406 }
407
408 /** <p>Returns the ant tasks description.</p>
409 */
410 public String getDescription() {
411 return "A JaxMe generator task converting XML schemata into Java source files.";
412 }
413
414 /** <p>Sets whether the XML schema parser is validating.</p>
415 */
416 public void setValidating(boolean pValidating) {
417 isValidating = pValidating;
418 }
419
420 /** <p>Returns whether the XML schema parser is validating.</p>
421 */
422 public boolean isValidating() {
423 return isValidating;
424 }
425
426
427 /** <p>Setting this option to true forces the up-to-date check to fail.
428 * This option is mainly useful while working on the JaxMe generator.
429 * For JaxMe users, which only change schema files, this option isn't of much
430 * use. It is designed for JaxMe developers.</p>
431 */
432 public boolean isForce() {
433 return force;
434 }
435
436 /** <p>Setting this option to true forces the up-to-date check to fail.
437 * This option is mainly useful while working on the JaxMe generator.
438 * For JaxMe users, which only change schema files, this option isn't of much
439 * use. It is designed for JaxMe developers.</p>
440 */
441 public void setForce(boolean pForce) {
442 force = pForce;
443 }
444
445 /** <p>Returns whether the ant task is setting the {@link LoggerFactory}. This
446 * option is only useful, if you are using the Ant task from another Java class
447 * and not from within Ant.</p>
448 */
449 public boolean isSettingLoggerFactory() {
450 return isSettingLoggerFactory;
451 }
452
453 /** <p>Sets whether the ant task is setting the {@link LoggerFactory}. This
454 * option is only useful, if you are using the Ant task from another Java class
455 * and not from within Ant.</p>
456 */
457 public void setSettingLoggerFactory(boolean pIsSettingLoggerFactory) {
458 isSettingLoggerFactory = pIsSettingLoggerFactory;
459 }
460
461 /** <p>Returns an external binding file being applied to the schema file.</p>
462 */
463 public File getBinding() {
464 return binding;
465 }
466
467 /** <p>Sets an external binding file being applied to the schema file.</p>
468 */
469 public void setBinding(File pBinding) {
470 binding = pBinding;
471 }
472
473 /** <p>Returns, whether the XJC binding compiler will run in the extension mode.
474 * By default, it will run in the strict conformance mode.</p>
475 */
476 public boolean isExtension() {
477 return extension;
478 }
479
480 /** <p>Sets, whether the XJC binding compiler will run in the extension mode.
481 * By default, it will run in the strict conformance mode.</p>
482 */
483 public void setExtension(boolean pExtension) {
484 extension = pExtension;
485 }
486
487 /** <p>Returns the generated Java sources package name. A non-null package specification
488 * overrides package specifications in the schema bindings, if any.</p>
489 */
490 public String getPackage() {
491 return packageName;
492 }
493
494 /** <p>Sets the generated Java sources package name. A non-null package specification
495 * overrides package specifications in the schema bindings, if any.</p>
496 */
497 public void setPackage(String pPackageName) {
498 packageName = pPackageName;
499 }
500
501 /** @deprecated Use {@link #setPackage(String)}.
502 */
503 public void setPackageName(String pPackageName) {
504 log("Warning: The 'packageName' attribute is updated to 'package', for compatibility reasons. Please update your build script.", Project.MSG_WARN);
505 setPackage(pPackageName);
506 }
507
508 /** <p>Returns, whether generated Java source files are in read-only mode.</p>
509 */
510 public boolean isReadOnly() {
511 return readOnly;
512 }
513
514 /** <p>Sets, whether generated Java source files are in read-only mode.</p>
515 */
516 public void setReadOnly(boolean pReadOnly) {
517 readOnly = pReadOnly;
518 }
519
520 /** <p>If one or more nested <produces> elements are specified and
521 * this attribute is set to true, then the Ant task will ensure that only
522 * generated files will remain. In other words, if you had removed an element
523 * named "Foo" from the previous schema version, then the Ant task will remove
524 * "Foo.java".</p>
525 */
526 public boolean isRemoveOldOutput() {
527 return removeOldOutput;
528 }
529
530 /** <p>If one or more nested <produces> elements are specified and
531 * this attribute is set to true, then the Ant task will ensure that only
532 * generated files will remain. In other words, if you had removed an element
533 * named "Foo" from the previous schema version, then the Ant task will remove
534 * "Foo.java".</p>
535 */
536 public void setRemoveOldOutput(boolean pRemoveOldOutput) {
537 removeOldOutput = pRemoveOldOutput;
538 }
539
540 /** <p>Returns the name of the schema file being compiled.</p>
541 */
542 public File getSchema() {
543 return schema;
544 }
545
546 /** <p>Sets the name of the schema file being compiled.</p>
547 */
548 public void setSchema(File pSchema) {
549 schema = pSchema;
550 }
551
552 /** <p>Returns the thread stack size for the XJC binding compiler (J2SE SDK v1.4 or higher).
553 * The XJC binding compiler can fail to compile large schemas with StackOverflowError and,
554 * in that case, this option can be used to extend the stack size. If unspecified, the default
555 * VM size is used. The format is equivalent to the -Xss command-line argument for Sun Microsystems JVM.
556 * This value can be specified in bytes (stackSize="2097152"), kilobytes (stackSize="2048kb"),
557 * or megabytes (stackSize="2mb").</p>
558 * <p>This attribute is ignored by the JaxMe ant task and present for compatibility reasons only.</p>
559 */
560 public String getStackSize() {
561 return stackSize;
562 }
563
564 /** <p>Sets the thread stack size for the XJC binding compiler (J2SE SDK v1.4 or higher).
565 * The XJC binding compiler can fail to compile large schemas with StackOverflowError and,
566 * in that case, this option can be used to extend the stack size. If unspecified, the default
567 * VM size is used. The format is equivalent to the -Xss command-line argument for Sun Microsystems JVM.
568 * This value can be specified in bytes (stackSize="2097152"), kilobytes (stackSize="2048kb"),
569 * or megabytes (stackSize="2mb").</p>
570 * <p>This attribute is ignored by the JaxMe ant task and present for compatibility reasons only.</p>
571 */
572 public void setStackSize(String pStackSize) {
573 stackSize = pStackSize;
574 log("The 'stackSize' attribute is ignored by the JaxMe ant task.", Project.MSG_WARN);
575 }
576
577 /** <p>Returns the target directory, where generated sources are being created. A package
578 * structure will be created below that directory. For example, with target="src" and
579 * package="org.acme", you will have files being created in "src/org/acme".</p>
580 */
581 public File getTarget() {
582 return target;
583 }
584
585 /** <p>Sets the target directory, where generated sources are being created. A package
586 * structure will be created below that directory. For example, with target="src" and
587 * package="org.acme", you will have files being created in "src/org/acme".</p>
588 */
589 public void setTarget(File pTarget) {
590 target = pTarget;
591 }
592
593 /** <p>Multiple schema files may be compiled in one or more nested <schema>
594 * elements. The element syntax is equivalent to a nested <fileset>.
595 * Use of a nested <schema> element is mutually exclusive with the use
596 * of a "schema" attribute.</p>
597 */
598 public void addSchema(FileSet pSchemas) {
599 if (getSchema() != null) {
600 throw new BuildException("The 'schema' attribute and the nested 'schema' element are mutually exclusive.");
601 }
602 schemas.add(pSchemas);
603 }
604
605 /** <p>Multiple schema files may be compiled in one or more nested <schema>
606 * elements. The element syntax is equivalent to a nested <fileset>.
607 * Use of a nested <schema> element is mutually exclusive with the use
608 * of a "schema" attribute.</p>
609 */
610 public FileSet[] getSchemas() {
611 return (FileSet[]) schemas.toArray(new FileSet[schemas.size()]);
612 }
613
614 /** <p>Multiple external binding files may be specified. The element syntax is equivalent
615 * to a nested <fileset>. Use of a nested <binding> element is
616 * mutually exclusive with the use of a "binding" attribute.</p>
617 */
618 public void addBinding(FileSet pBindings) {
619 if (getBinding() != null) {
620 throw new BuildException("The 'binding' attribute and the nested 'binding' element are mutually exclusive.");
621 }
622 bindings.add(pBindings);
623 }
624
625 /** <p>Multiple external binding files may be specified. The element syntax is equivalent
626 * to a nested <fileset>. Use of a nested <binding> element is
627 * mutually exclusive with the use of a "binding" attribute.</p>
628 */
629 public FileSet[] getBindings() {
630 return (FileSet[]) bindings.toArray(new FileSet[bindings.size()]);
631 }
632
633 /** <p>This nested element is ignored by the JaxMe ant task and exists for compatibility
634 * to the JAXB ant task only. In the case of JAXB it specifies a classpath for loading
635 * user defined types (required in the case of a <javaType> customization)</p>
636 */
637 public void addClasspath(Path pClasspath) {
638 log("The 'classpath' attribute is ignored by the JaxMe ant task.", Project.MSG_WARN);
639 }
640
641 /** <p>This nested element is ignored by the JaxMe ant task and exists for compatibility
642 * to the JAXB ant task only. In the case of JAXB it specifies additional command line
643 * arguments being passed to the XJC. For details about the syntax, see the relevant
644 * section in the Ant manual.<br>
645 * This nested element can be used to specify various options not natively supported in
646 * the xjc Ant task. For example, currently there is no native support for the following
647 * xjc command-line options:
648 * <ul>
649 * <li>-nv</li>
650 * <li>-catalog</li>
651 * <li>-use-runtime</li>
652 * <li>-schema</li>
653 * <li>-dtd</li>
654 * <li>-relaxng</li>
655 * </ul></p>
656 */
657 public void addArg(Commandline.Argument pArg) {
658 log("The 'arg' attribute is ignored by the JaxMe ant task.", Project.MSG_WARN);
659 }
660
661 /** <p>By default the JaxMe Ant tasks up-to-date check considers the specified schema
662 * and binding files only. This is insufficient, if other schema files are included,
663 * imported or redefined.<br>
664 * The nested <depends> element allows to specify additional files to consider
665 * for the up-to-date check. Typically these are the additional schema files.<br>
666 * Syntactically the <depends> element specifies a nested <fileset>.</p>
667 */
668 public void addDepends(FileSet pDepends) {
669 depends.add(pDepends);
670 }
671
672 /** <p>By default the JaxMe Ant tasks up-to-date check considers the specified schema
673 * and binding files only. This is insufficient, if other schema files are included,
674 * imported or redefined.<br>
675 * The nested <depends> element allows to specify additional files to consider
676 * for the up-to-date check. Typically these are the additional schema files.<br>
677 * Syntactically the <depends> element specifies a nested <fileset>.</p>
678 */
679 public FileSet[] getDepends() {
680 return (FileSet[]) depends.toArray(new FileSet[depends.size()]);
681 }
682
683 /** <p>Specifies the set of files being created by the JaxMe ant task. These files are
684 * considered as targets for the up-to-date check. The syntax of the <produces>
685 * element is equivalent to a nested <fileset>.</p>
686 */
687 public FileSet createProduces() {
688 FileSet result = new FileSet();
689 produces.add(result);
690 return result;
691 }
692
693 /** <p>Returns the set of files being created by the JaxMe ant task. These files are
694 * considered as targets for the up-to-date check. The syntax of the <produces>
695 * element is equivalent to a nested <fileset>.</p>
696 */
697 public FileSet[] getProduces() {
698 return (FileSet[]) produces.toArray(new FileSet[produces.size()]);
699 }
700
701 /** Creates a nested element "dtd".
702 */
703 public Dtd createDtd() {
704 if (dtd == null) {
705 dtd = new Dtd();
706 return dtd;
707 } else {
708 throw new BuildException("Multiple nested 'dtd' elements are forbidden.",
709 getLocation());
710 }
711 }
712
713 /** Returns the nested element "dtd".
714 */
715 public Dtd getDtd() {
716 return dtd;
717 }
718
719 public void finish() {
720 if (getSchema() == null && getSchemas().length == 0) {
721 throw new BuildException("Either of the 'schema' attribute or the nested 'schema' elements must be given.",
722 getLocation());
723 }
724 }
725
726 private File[] getFiles(FileSet[] pFileSets) {
727 List list = new ArrayList();
728 for (int i = 0; i < pFileSets.length; i++) {
729 FileSet fileSet = pFileSets[i];
730 DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
731 scanner.scan();
732 String[] files = scanner.getIncludedFiles();
733 for (int j = 0; j < files.length; j++) {
734 list.add(new File(fileSet.getDir(getProject()), files[j]));
735 }
736 }
737 return (File[]) list.toArray(new File[list.size()]);
738 }
739
740 private File[] getSchemaFiles() {
741 if (getSchema() != null) {
742 return new File[]{getSchema()};
743 } else {
744 return getFiles(getSchemas());
745 }
746 }
747
748 private File[] getBindingFiles() {
749 if (getBinding() != null) {
750 return new File[]{getBinding()};
751 } else {
752 return getFiles(getBindings());
753 }
754 }
755
756 private File[] getDependsFiles() {
757 return getFiles(getDepends());
758 }
759
760 public boolean isUpToDate(File[] pSchemaFiles, File[] pBindingFiles, File[] pDependsFiles, List pProducesList) {
761 FileSet[] myProduces = getProduces();
762 if (myProduces.length == 0) {
763 log("No nested 'produces' elements, up-to-date check returns false", Project.MSG_VERBOSE);
764 return false;
765 }
766
767 boolean result = true;
768 long firstTarget = 0;
769 File firstTargetFile = null;
770 for (int i = 0; i < myProduces.length; i++) {
771 File dir = myProduces[i].getDir(getProject());
772 if (dir == null) {
773 dir = getTarget();
774 if (dir == null) {
775 dir = getProject().getBaseDir();
776 }
777 myProduces[i].setDir(dir);
778 }
779 if (!dir.exists()) {
780 log("The directory specified by the nested 'produces' element #" + i + " does not exist, up-to-date check returns false",
781 Project.MSG_VERBOSE);
782 result = false;
783 continue;
784 }
785 DirectoryScanner scanner = myProduces[i].getDirectoryScanner(getProject());
786 scanner.scan();
787 String[] files = scanner.getIncludedFiles();
788 if (files.length == 0) {
789 log("The fileset specified by the nested 'produces' element #" + i + " is empty, up-to-date check returns false",
790 Project.MSG_VERBOSE);
791 result = false;
792 }
793 for (int j = 0; j < files.length; j++) {
794 File f = new File(dir, files[j]).getAbsoluteFile();
795 if (pProducesList != null) {
796 pProducesList.add(f);
797 }
798 long l = f.lastModified();
799 if (l == -1) {
800 log("Unable to determine timestamp of target file " + f + ", up-to-date check returns false.", Project.MSG_VERBOSE);
801 result = false;
802 }
803 if (firstTargetFile == null || firstTarget > l) {
804 firstTargetFile = f;
805 firstTarget = l;
806 }
807 }
808 }
809
810 if (isForce()) {
811 log("Force option is set, up-to-date check returns false", Project.MSG_VERBOSE);
812 result = false;
813 }
814
815 if (!result) {
816 return false;
817 }
818
819 List sourceFiles = new ArrayList();
820 for (int i = 0; i < pSchemaFiles.length; i++) {
821 sourceFiles.add(pSchemaFiles[i]);
822 }
823 for (int i = 0; i < pBindingFiles.length; i++) {
824 sourceFiles.add(pBindingFiles[i]);
825 }
826 for (int i = 0; i < pDependsFiles.length; i++) {
827 sourceFiles.add(pDependsFiles[i]);
828 }
829
830 long lastSource = 0;
831 File lastSourceFile = null;
832 for (Iterator iter = sourceFiles.iterator(); iter.hasNext(); ) {
833 File f = (File) iter.next();
834 long l = f.lastModified();
835 if (l == -1) {
836 log("Unable to determine timestamp of source file " + f + ", up-to-date check returns false.", Project.MSG_VERBOSE);
837 result = false;
838 }
839 if (lastSourceFile == null || lastSource < l) {
840 lastSource = l;
841 lastSourceFile = f;
842 }
843 }
844
845 if (lastSourceFile == null) {
846 log("No source files found, up-to-date check returns false.", Project.MSG_VERBOSE);
847 return false;
848 }
849
850 if (!result) {
851 return false;
852 }
853
854 try {
855 URL url = Generator.class.getClassLoader().getResource(Generator.class.getName().replace('.', '/') + ".class");
856 if (url != null) {
857 long l = url.openConnection().getLastModified();
858 if (l != 0 && lastSource < l) {
859 log("Generator class is newer than any schema files, using Generator classes timestamp as schema timestamp.", Project.MSG_DEBUG);
860 lastSource = l;
861 }
862 }
863 } catch (IOException e) {
864 }
865
866 if (lastSource >= firstTarget) {
867 log("Source file " + lastSourceFile + " is more recent than target file " + firstTargetFile + ", up-to-date check returns false", Project.MSG_VERBOSE);
868 return false;
869 }
870
871 log("All target files are up-to-date.", Project.MSG_VERBOSE);
872 return true;
873 }
874
875 public class MyClassLoader extends java.lang.ClassLoader {
876 private java.lang.ClassLoader parent;
877 public java.lang.ClassLoader getMyParent() {
878 return parent;
879 }
880 public MyClassLoader(java.lang.ClassLoader pParent) {
881 super(XJCTask.this.getClass().getClassLoader());
882 parent = pParent;
883 }
884 public Class findClass(String name) throws ClassNotFoundException {
885 return parent.loadClass(name);
886 }
887 public URL findResource(String resource) {
888 return parent.getResource(resource);
889 }
890 public Enumeration findResources(String resource) throws IOException {
891 return parent.getResources(resource);
892 }
893 }
894
895 public void stopLogging(LoggerFactory pFactory) {
896 if (pFactory != null) {
897 LoggerAccess.setLoggerFactory(pFactory);
898 }
899 }
900
901 public LoggerFactory initLogging() {
902 if (!isSettingLoggerFactory()) {
903 return null;
904 }
905 LoggerFactory loggerFactory = LoggerAccess.getLoggerFactory();
906 if (!(loggerFactory instanceof AntProjectLoggerFactory)) {
907 loggerFactory = new AntProjectLoggerFactory(this);
908 LoggerAccess.setLoggerFactory(loggerFactory);
909 return loggerFactory;
910 }
911 return null;
912 }
913
914 public void execute() {
915 java.lang.ClassLoader parent = Thread.currentThread().getContextClassLoader();
916 MyClassLoader cl = new MyClassLoader(parent == null ? getClass().getClassLoader() : parent);
917 LoggerFactory loggerFactory = initLogging();
918 try {
919 Thread.currentThread().setContextClassLoader(cl);
920
921 File[] schemaFiles = getSchemaFiles();
922 if (schemaFiles.length == 0) {
923 log("No schema files specified", Project.MSG_WARN);
924 return;
925 }
926
927 File[] bindingFiles = getBindingFiles();
928 if (bindingFiles.length > 0) {
929 throw new BuildException("External schema bindings are still unsupported by JaxMe.", getLocation());
930 }
931
932 File[] dependFiles = getDependsFiles();
933 List producesFiles = isRemoveOldOutput() ? new ArrayList() : null;
934 if (isUpToDate(schemaFiles, bindingFiles, dependFiles, producesFiles)) {
935 return;
936 }
937
938 Set producesFilesSet = null;
939 if (producesFiles != null) {
940 producesFilesSet = new HashSet();
941 for (Iterator iter = producesFiles.iterator(); iter.hasNext(); ) {
942 File f = ((File) iter.next());
943 producesFilesSet.add(f);
944 }
945 }
946
947
948 Generator generator = new GeneratorImpl();
949 generator.setForcingOverwrite(isForce());
950 generator.setSettingReadOnly(isReadOnly());
951 generator.setValidating(isValidating());
952 if (getPackage() != null) {
953 generator.setProperty("jaxme.package.name", getPackage());
954 }
955 Dtd myDtd = getDtd();
956 if (myDtd != null) {
957 generator.setProperty("jaxme.dtd.input", "true");
958 if (myDtd.getTargetNamespace() != null) {
959 generator.setProperty("jaxme.dtd.targetNamespace", myDtd.getTargetNamespace());
960 }
961 }
962 Property[] myProperties = getProperties();
963 for (int i = 0; i < myProperties.length; i++) {
964 Property ot = myProperties[i];
965 log("Option " + ot.getName() + "=" + ot.getValue(), Project.MSG_VERBOSE);
966 generator.setProperty(ot.getName(), ot.getValue());
967 }
968
969 SchemaReader reader = getSchemaReader();
970 if (reader instanceof JAXBSchemaReader) {
971 ((JAXBSchemaReader) reader).setSupportingExtensions(isExtension());
972 }
973 generator.setSchemaReader(reader);
974 reader.setGenerator(generator);
975 generator.setTargetDirectory(getTarget());
976
977 ClassType[] mySgFactoryChains = getSGFactoryChains();
978 if (mySgFactoryChains.length > 0) {
979 if (!(reader instanceof JAXBSchemaReader)) {
980 throw new BuildException("The nested child element 'sgFactoryChain' is valid only, if the schema reader is an instance of "
981 + JAXBSchemaReader.class.getName(), getLocation());
982 }
983 for (int i = 0; i < mySgFactoryChains.length; i++) {
984 ClassType ct = mySgFactoryChains[i];
985 Class c;
986 try {
987 c = cl.loadClass(ct.getClassName());
988 } catch (ClassNotFoundException e) {
989 throw new BuildException("Failed to load SGFactoryChain implementation class " + ct.getClassName(),
990 getLocation());
991 }
992 if (!SGFactoryChain.class.isAssignableFrom(c)) {
993 throw new BuildException("The SGFactoryChain class " + c.getName() +
994 " is not implementing " + SGFactoryChain.class.getName(),
995 getLocation());
996 }
997 reader.addSGFactoryChain(c);
998 }
999 }
1000
1001 for (int i = 0; i < schemaFiles.length; i++) {
1002 log("Reading schema file " + schemaFiles[i], Project.MSG_VERBOSE);
1003 try {
1004 SchemaSG schemaSG = generator.generate(schemaFiles[i]);
1005 if (producesFilesSet != null) {
1006 JavaSourceFactory jsf = schemaSG.getJavaSourceFactory();
1007 File targetDirectory = getTarget();
1008 for (Iterator iter = jsf.getJavaSources(); iter.hasNext(); ) {
1009 JavaSource js = (JavaSource) iter.next();
1010 File f = jsf.getLocation(targetDirectory, js).getAbsoluteFile();
1011 producesFilesSet.remove(f);
1012 }
1013 for (Iterator iter = jsf.getTextFiles(); iter.hasNext(); ) {
1014 TextFile tf = (TextFile) iter.next();
1015 File f = jsf.getLocation(targetDirectory, tf).getAbsoluteFile();
1016 producesFilesSet.remove(f);
1017 }
1018 }
1019 } catch (SAXParseException e) {
1020 String msg = LocSAXException.formatMsg(e.getMessage() == null ? e.getClass().getName() : e.getMessage(),
1021 e.getPublicId(),
1022 e.getSystemId(),
1023 e.getLineNumber(),
1024 e.getColumnNumber());
1025 throw new BuildException(msg, e, getLocation());
1026 } catch (Exception e) {
1027 String msg = e.getMessage();
1028 if (msg == null) {
1029 msg = e.getClass().getName();
1030 }
1031 throw new BuildException(schemaFiles[i] + ": " + msg, e, getLocation());
1032 }
1033 }
1034
1035 if (producesFilesSet != null) {
1036 for (Iterator iter = producesFilesSet.iterator(); iter.hasNext(); ) {
1037 File f = (File) iter.next();
1038 log("Removing orphan file " + f, Project.MSG_VERBOSE);
1039 if (!f.delete()) {
1040 throw new BuildException("Unable to delete file " + f);
1041 }
1042 }
1043 }
1044 } finally {
1045 Thread.currentThread().setContextClassLoader(parent);
1046 stopLogging(loggerFactory);
1047 }
1048 }
1049}