001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.xjc;
027:
028: import java.io.BufferedReader;
029: import java.io.File;
030: import java.io.FileInputStream;
031: import java.io.IOException;
032: import java.io.InputStreamReader;
033: import java.lang.reflect.Array;
034: import java.net.MalformedURLException;
035: import java.net.URL;
036: import java.net.URLClassLoader;
037: import java.text.SimpleDateFormat;
038: import java.util.ArrayList;
039: import java.util.Date;
040: import java.util.Enumeration;
041: import java.util.HashSet;
042: import java.util.List;
043: import java.util.Set;
044: import java.util.regex.Matcher;
045: import java.util.regex.Pattern;
046:
047: import com.sun.codemodel.internal.CodeWriter;
048: import com.sun.codemodel.internal.writer.FileCodeWriter;
049: import com.sun.codemodel.internal.writer.PrologCodeWriter;
050: import com.sun.org.apache.xml.internal.resolver.CatalogManager;
051: import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver;
052: import com.sun.tools.internal.xjc.api.ClassNameAllocator;
053: import com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory;
054: import com.sun.tools.internal.xjc.model.Model;
055: import com.sun.tools.internal.xjc.reader.Util;
056: import com.sun.xml.internal.bind.api.impl.NameConverter;
057:
058: import org.xml.sax.EntityResolver;
059: import org.xml.sax.InputSource;
060:
061: /**
062: * Global options.
063: *
064: * <p>
065: * This class stores invocation configuration for XJC.
066: * The configuration in this class shoule be abstract enough so that
067: * it could be parsed from both command-line or Ant.
068: */
069: public class Options {
070: /** If "-debug" is specified. */
071: public boolean debugMode;
072:
073: /** If the "-verbose" option is specified. */
074: public boolean verbose;
075:
076: /** If the "-quiet" option is specified. */
077: public boolean quiet;
078:
079: /** If the -readOnly option is specified. */
080: public boolean readOnly;
081:
082: /** No file header comment (to be more friendly with diff.) */
083: public boolean noFileHeader;
084:
085: /**
086: * Check the source schemas with extra scrutiny.
087: * The exact meaning depends on the schema language.
088: */
089: public boolean strictCheck = true;
090:
091: /**
092: * If -explicit-annotation option is specified.
093: * <p>
094: * This generates code that works around issues specific to 1.4 runtime.
095: */
096: public boolean runtime14 = false;
097:
098: /**
099: * strictly follow the compatibility rules and reject schemas that
100: * contain features from App. E.2, use vendor binding extensions
101: */
102: public static final int STRICT = 1;
103: /**
104: * loosely follow the compatibility rules and allow the use of vendor
105: * binding extensions
106: */
107: public static final int EXTENSION = 2;
108:
109: /**
110: * this switch determines how carefully the compiler will follow
111: * the compatibility rules in the spec. Either <code>STRICT</code>
112: * or <code>EXTENSION</code>.
113: */
114: public int compatibilityMode = STRICT;
115:
116: /** Target direcoty when producing files. */
117: public File targetDir = new File(".");
118:
119: /**
120: * Actually stores {@link CatalogResolver}, but the field
121: * type is made to {@link EntityResolver} so that XJC can be
122: * used even if resolver.jar is not available in the classpath.
123: */
124: public EntityResolver entityResolver = null;
125:
126: /**
127: * Type of input schema language. One of the <code>SCHEMA_XXX</code>
128: * constants.
129: */
130: private Language schemaLanguage = null;
131:
132: /**
133: * The -p option that should control the default Java package that
134: * will contain the generated code. Null if unspecified.
135: */
136: public String defaultPackage = null;
137:
138: /**
139: * Similar to the -p option, but this one works with a lower priority,
140: * and customizations overrides this. Used by JAX-RPC.
141: */
142: public String defaultPackage2 = null;
143:
144: /**
145: * Input schema files as a list of {@link InputSource}s.
146: */
147: private final List<InputSource> grammars = new ArrayList<InputSource>();
148:
149: private final List<InputSource> bindFiles = new ArrayList<InputSource>();
150:
151: // Proxy setting.
152: private String proxyHost = null;
153: private String proxyPort = null;
154: private String proxyUser = null;
155: private String proxyPassword = null;
156:
157: /**
158: * {@link Plugin}s that are enabled in this compilation.
159: */
160: public final List<Plugin> activePlugins = new ArrayList<Plugin>();
161:
162: /**
163: * All discovered {@link Plugin}s.
164: * This is lazily parsed, so that we can take '-cp' option into account.
165: *
166: * @see #getAllPlugins()
167: */
168: private List<Plugin> allPlugins;
169:
170: /**
171: * Set of URIs that plug-ins recognize as extension bindings.
172: */
173: public final Set<String> pluginURIs = new HashSet<String>();
174:
175: /**
176: * This allocator has the final say on deciding the class name.
177: */
178: public ClassNameAllocator classNameAllocator;
179:
180: /**
181: * This switch controls whether or not xjc will generate package level annotations
182: */
183: public boolean packageLevelAnnotations = true;
184:
185: /**
186: * This {@link FieldRendererFactory} determines how the fields are generated.
187: */
188: private FieldRendererFactory fieldRendererFactory = new FieldRendererFactory();
189: /**
190: * Used to detect if two {@link Plugin}s try to overwrite {@link #fieldRendererFactory}.
191: */
192: private Plugin fieldRendererFactoryOwner = null;
193:
194: /**
195: * If this is non-null, we use this {@link NameConverter} over the one
196: * given in the schema/binding.
197: */
198: private NameConverter nameConverter = null;
199: /**
200: * Used to detect if two {@link Plugin}s try to overwrite {@link #nameConverter}.
201: */
202: private Plugin nameConverterOwner = null;
203:
204: /**
205: * Gets the active {@link FieldRendererFactory} that shall be used to build {@link Model}.
206: *
207: * @return always non-null.
208: */
209: public FieldRendererFactory getFieldRendererFactory() {
210: return fieldRendererFactory;
211: }
212:
213: /**
214: * Sets the {@link FieldRendererFactory}.
215: *
216: * <p>
217: * This method is for plugins to call to set a custom {@link FieldRendererFactory}.
218: *
219: * @param frf
220: * The {@link FieldRendererFactory} to be installed. Must not be null.
221: * @param owner
222: * Identifies the plugin that owns this {@link FieldRendererFactory}.
223: * When two {@link Plugin}s try to call this method, this allows XJC
224: * to report it as a user-friendly error message.
225: *
226: * @throws BadCommandLineException
227: * If a conflit happens, this exception carries a user-friendly error
228: * message, indicating a conflict.
229: */
230: public void setFieldRendererFactory(FieldRendererFactory frf,
231: Plugin owner) throws BadCommandLineException {
232: // since this method is for plugins, make it bit more fool-proof than usual
233: if (frf == null)
234: throw new IllegalArgumentException();
235: if (fieldRendererFactoryOwner != null) {
236: throw new BadCommandLineException(Messages.format(
237: Messages.FIELD_RENDERER_CONFLICT,
238: fieldRendererFactoryOwner.getOptionName(), owner
239: .getOptionName()));
240: }
241: this .fieldRendererFactoryOwner = owner;
242: this .fieldRendererFactory = frf;
243: }
244:
245: /**
246: * Gets the active {@link NameConverter} that shall be used to build {@link Model}.
247: *
248: * @return can be null, in which case it's up to the binding.
249: */
250: public NameConverter getNameConverter() {
251: return nameConverter;
252: }
253:
254: /**
255: * Sets the {@link NameConverter}.
256: *
257: * <p>
258: * This method is for plugins to call to set a custom {@link NameConverter}.
259: *
260: * @param nc
261: * The {@link NameConverter} to be installed. Must not be null.
262: * @param owner
263: * Identifies the plugin that owns this {@link NameConverter}.
264: * When two {@link Plugin}s try to call this method, this allows XJC
265: * to report it as a user-friendly error message.
266: *
267: * @throws BadCommandLineException
268: * If a conflit happens, this exception carries a user-friendly error
269: * message, indicating a conflict.
270: */
271: public void setNameConverter(NameConverter nc, Plugin owner)
272: throws BadCommandLineException {
273: // since this method is for plugins, make it bit more fool-proof than usual
274: if (nc == null)
275: throw new IllegalArgumentException();
276: if (nameConverter != null) {
277: throw new BadCommandLineException(Messages.format(
278: Messages.NAME_CONVERTER_CONFLICT,
279: nameConverterOwner.getOptionName(), owner
280: .getOptionName()));
281: }
282: this .nameConverterOwner = owner;
283: this .nameConverter = nc;
284: }
285:
286: /**
287: * Gets all the {@link Plugin}s discovered so far.
288: *
289: * <p>
290: * A plugins are enumerated when this method is called for the first time,
291: * by taking {@link #classpaths} into account. That means
292: * "-cp plugin.jar" has to come before you specify options to enable it.
293: */
294: public List<Plugin> getAllPlugins() {
295: if (allPlugins == null) {
296: allPlugins = new ArrayList<Plugin>();
297: ClassLoader ucl = getUserClassLoader(getClass()
298: .getClassLoader());
299: for (Plugin aug : findServices(Plugin.class, ucl))
300: allPlugins.add(aug);
301: }
302:
303: return allPlugins;
304: }
305:
306: public Language getSchemaLanguage() {
307: if (schemaLanguage == null)
308: schemaLanguage = guessSchemaLanguage();
309: return schemaLanguage;
310: }
311:
312: public void setSchemaLanguage(Language _schemaLanguage) {
313: this .schemaLanguage = _schemaLanguage;
314: }
315:
316: /** Input schema files. */
317: public InputSource[] getGrammars() {
318: return grammars.toArray(new InputSource[grammars.size()]);
319: }
320:
321: /**
322: * Adds a new input schema.
323: */
324: public void addGrammar(InputSource is) {
325: grammars.add(absolutize(is));
326: }
327:
328: private InputSource fileToInputSource(File source) {
329: try {
330: String url = source.toURL().toExternalForm();
331: return new InputSource(Util.escapeSpace(url));
332: } catch (MalformedURLException e) {
333: return new InputSource(source.getPath());
334: }
335: }
336:
337: public void addGrammar(File source) {
338: addGrammar(fileToInputSource(source));
339: }
340:
341: /**
342: * Recursively scan directories and add all XSD files in it.
343: */
344: public void addGrammarRecursive(File dir) {
345: addRecursive(dir, ".xsd", grammars);
346: }
347:
348: private void addRecursive(File dir, String suffix,
349: List<InputSource> result) {
350: File[] files = dir.listFiles();
351: if (files == null)
352: return; // work defensively
353:
354: for (File f : files) {
355: if (f.isDirectory())
356: addRecursive(f, suffix, result);
357: else if (f.getPath().endsWith(suffix))
358: result.add(absolutize(fileToInputSource(f)));
359: }
360: }
361:
362: private InputSource absolutize(InputSource is) {
363: // absolutize all the system IDs in the input,
364: // so that we can map system IDs to DOM trees.
365: try {
366: URL baseURL = new File(".").getCanonicalFile().toURL();
367: is.setSystemId(new URL(baseURL, is.getSystemId())
368: .toExternalForm());
369: } catch (IOException e) {
370: // ignore
371: }
372: return is;
373: }
374:
375: /** Input external binding files. */
376: public InputSource[] getBindFiles() {
377: return bindFiles.toArray(new InputSource[bindFiles.size()]);
378: }
379:
380: /**
381: * Adds a new input schema.
382: */
383: public void addBindFile(InputSource is) {
384: bindFiles.add(absolutize(is));
385: }
386:
387: /**
388: * Recursively scan directories and add all ".xjb" files in it.
389: */
390: public void addBindFileRecursive(File dir) {
391: addRecursive(dir, ".xjb", bindFiles);
392: }
393:
394: public final List<URL> classpaths = new ArrayList<URL>();
395:
396: /**
397: * Gets a classLoader that can load classes specified via the
398: * -classpath option.
399: */
400: public URLClassLoader getUserClassLoader(ClassLoader parent) {
401: return new URLClassLoader(classpaths.toArray(new URL[classpaths
402: .size()]), parent);
403: }
404:
405: /**
406: * Parses an option <code>args[i]</code> and return
407: * the number of tokens consumed.
408: *
409: * @return
410: * 0 if the argument is not understood. Returning 0
411: * will let the caller report an error.
412: * @exception BadCommandLineException
413: * If the callee wants to provide a custom message for an error.
414: */
415: protected int parseArgument(String[] args, int i)
416: throws BadCommandLineException {
417: if (args[i].equals("-classpath") || args[i].equals("-cp")) {
418: if (i == args.length - 1)
419: throw new BadCommandLineException(Messages
420: .format(Messages.MISSING_CLASSPATH));
421: File file = new File(args[++i]);
422: try {
423: classpaths.add(file.toURL());
424: } catch (MalformedURLException e) {
425: throw new BadCommandLineException(Messages.format(
426: Messages.NOT_A_VALID_FILENAME, file), e);
427: }
428: return 2;
429: }
430: if (args[i].equals("-d")) {
431: if (i == args.length - 1)
432: throw new BadCommandLineException(Messages
433: .format(Messages.MISSING_DIR));
434: targetDir = new File(args[++i]);
435: if (!targetDir.exists())
436: throw new BadCommandLineException(Messages.format(
437: Messages.NON_EXISTENT_DIR, targetDir));
438: return 2;
439: }
440: if (args[i].equals("-readOnly")) {
441: readOnly = true;
442: return 1;
443: }
444: if (args[i].equals("-p")) {
445: if (i == args.length - 1)
446: throw new BadCommandLineException(Messages
447: .format(Messages.MISSING_PACKAGENAME));
448: defaultPackage = args[++i];
449: if (defaultPackage.length() == 0) { // user specified default package
450: // there won't be any package to annotate, so disable them
451: // automatically as a usability feature
452: packageLevelAnnotations = false;
453: }
454: return 2;
455: }
456: if (args[i].equals("-debug")) {
457: debugMode = true;
458: verbose = true;
459: return 1;
460: }
461: if (args[i].equals("-nv")) {
462: strictCheck = false;
463: return 1;
464: }
465: if (args[i].equals("-npa")) {
466: packageLevelAnnotations = false;
467: return 1;
468: }
469: if (args[i].equals("-no-header")) {
470: noFileHeader = true;
471: return 1;
472: }
473: if (args[i].equals("-verbose")) {
474: verbose = true;
475: return 1;
476: }
477: if (args[i].equals("-quiet")) {
478: quiet = true;
479: return 1;
480: }
481: if (args[i].equals("-XexplicitAnnotation")) {
482: runtime14 = true;
483: return 1;
484: }
485: if (args[i].equals("-b")) {
486: if (i == args.length - 1 || args[i + 1].startsWith("-"))
487: throw new BadCommandLineException(Messages
488: .format(Messages.MISSING_FILENAME));
489:
490: addFile(args[i + 1], bindFiles, ".xjb");
491: return 2;
492: }
493: if (args[i].equals("-dtd")) {
494: schemaLanguage = Language.DTD;
495: return 1;
496: }
497: if (args[i].equals("-relaxng")) {
498: schemaLanguage = Language.RELAXNG;
499: return 1;
500: }
501: if (args[i].equals("-relaxng-compact")) {
502: schemaLanguage = Language.RELAXNG_COMPACT;
503: return 1;
504: }
505: if (args[i].equals("-xmlschema")) {
506: schemaLanguage = Language.XMLSCHEMA;
507: return 1;
508: }
509: if (args[i].equals("-wsdl")) {
510: schemaLanguage = Language.WSDL;
511: return 1;
512: }
513: if (args[i].equals("-extension")) {
514: compatibilityMode = EXTENSION;
515: return 1;
516: }
517: if (args[i].equals("-httpproxyfile")) {
518: if (i == args.length - 1 || args[i + 1].startsWith("-")) {
519: throw new BadCommandLineException(Messages
520: .format(Messages.MISSING_PROXYFILE));
521: }
522:
523: File file = new File(args[++i]);
524: if (!file.exists()) {
525: throw new BadCommandLineException(Messages.format(
526: Messages.NO_SUCH_FILE, file));
527: }
528:
529: try {
530: BufferedReader in = new BufferedReader(
531: new InputStreamReader(
532: new FileInputStream(file), "UTF-8"));
533: parseProxy(in.readLine());
534: in.close();
535: } catch (IOException e) {
536: throw new BadCommandLineException(
537: Messages.format(Messages.FAILED_TO_PARSE, file,
538: e.getMessage()), e);
539: }
540:
541: return 2;
542: }
543: if (args[i].equals("-httpproxy")) {
544: if (i == args.length - 1 || args[i + 1].startsWith("-")) {
545: throw new BadCommandLineException(Messages
546: .format(Messages.MISSING_PROXY));
547: }
548:
549: parseProxy(args[++i]);
550: return 2;
551: }
552: if (args[i].equals("-host")) {
553: // legacy option. we use -httpproxy for more control
554: if (i == args.length - 1 || args[i + 1].startsWith("-")) {
555: throw new BadCommandLineException(Messages
556: .format(Messages.MISSING_PROXYHOST));
557: }
558: proxyHost = args[++i];
559: return 2;
560: }
561: if (args[i].equals("-port")) {
562: // legacy option. we use -httpproxy for more control
563: if (i == args.length - 1 || args[i + 1].startsWith("-")) {
564: throw new BadCommandLineException(Messages
565: .format(Messages.MISSING_PROXYPORT));
566: }
567: proxyPort = args[++i];
568: return 2;
569: }
570: if (args[i].equals("-catalog")) {
571: // use Sun's "XML Entity and URI Resolvers" by Norman Walsh
572: // to resolve external entities.
573: // http://www.sun.com/xml/developers/resolver/
574: if (i == args.length - 1)
575: throw new BadCommandLineException(Messages
576: .format(Messages.MISSING_CATALOG));
577:
578: File catalogFile = new File(args[++i]);
579: try {
580: addCatalog(catalogFile);
581: } catch (IOException e) {
582: throw new BadCommandLineException(Messages.format(
583: Messages.FAILED_TO_PARSE, catalogFile, e
584: .getMessage()), e);
585: }
586: return 2;
587: }
588: if (args[i].equals("-source")) {
589: if (i == args.length - 1)
590: throw new BadCommandLineException(Messages
591: .format(Messages.MISSING_VERSION));
592: String version = args[++i];
593: //For source 1.0 the 1.0 Driver is loaded
594: //Hence anything other than 2.0 is defaulted to
595: //2.0
596: if (!version.equals("2.0"))
597: throw new BadCommandLineException(Messages
598: .format(Messages.DEFAULT_VERSION));
599: return 2;
600: }
601: if (args[i].equals("-Xtest-class-name-allocator")) {
602: classNameAllocator = new ClassNameAllocator() {
603: public String assignClassName(String packageName,
604: String className) {
605: System.out.printf("assignClassName(%s,%s)\n",
606: packageName, className);
607: return className + "_Type";
608: }
609: };
610: return 1;
611: }
612:
613: // see if this is one of the extensions
614: for (Plugin plugin : getAllPlugins()) {
615: if (('-' + plugin.getOptionName()).equals(args[i])) {
616: activePlugins.add(plugin);
617: plugin.onActivated(this );
618: pluginURIs.addAll(plugin.getCustomizationURIs());
619: return 1;
620: }
621:
622: try {
623: int r = plugin.parseArgument(this , args, i);
624: if (r != 0)
625: return r;
626: } catch (IOException e) {
627: throw new BadCommandLineException(e.getMessage(), e);
628: }
629: }
630:
631: return 0; // unrecognized
632: }
633:
634: private void parseProxy(String text) throws BadCommandLineException {
635: // syntax is [user[:password]@]proxyHost:proxyPort
636: String token = "([^@:]+)";
637: Pattern p = Pattern.compile("(?:" + token + "(?:\\:" + token
638: + ")?\\@)?" + token + "(?:\\:" + token + ")");
639:
640: Matcher matcher = p.matcher(text);
641: if (!matcher.matches())
642: throw new BadCommandLineException(Messages.format(
643: Messages.ILLEGAL_PROXY, text));
644:
645: proxyUser = matcher.group(1);
646: proxyPassword = matcher.group(2);
647: proxyHost = matcher.group(3);
648: proxyPort = matcher.group(4);
649: try {
650: Integer.valueOf(proxyPort);
651: } catch (NumberFormatException e) {
652: throw new BadCommandLineException(Messages.format(
653: Messages.ILLEGAL_PROXY, text));
654: }
655: }
656:
657: /**
658: * Parses a token to a file (or a set of files)
659: * and add them as {@link InputSource} to the specified list.
660: *
661: * @param suffix
662: * If the given token is a directory name, we do a recusive search
663: * and find all files that have the given suffix.
664: */
665: private void addFile(String name, List<InputSource> target,
666: String suffix) throws BadCommandLineException {
667: Object src;
668: try {
669: src = Util.getFileOrURL(name);
670: } catch (IOException e) {
671: throw new BadCommandLineException(Messages.format(
672: Messages.NOT_A_FILE_NOR_URL, name));
673: }
674: if (src instanceof URL) {
675: target.add(absolutize(new InputSource(Util
676: .escapeSpace(((URL) src).toExternalForm()))));
677: } else {
678: File fsrc = (File) src;
679: if (fsrc.isDirectory()) {
680: addRecursive(fsrc, suffix, target);
681: } else {
682: target.add(absolutize(fileToInputSource(fsrc)));
683: }
684: }
685: }
686:
687: /**
688: * Adds a new catalog file.
689: */
690: public void addCatalog(File catalogFile) throws IOException {
691: if (entityResolver == null) {
692: CatalogManager.getStaticManager()
693: .setIgnoreMissingProperties(true);
694: entityResolver = new CatalogResolver(true);
695: }
696: ((CatalogResolver) entityResolver).getCatalog().parseCatalog(
697: catalogFile.getPath());
698: }
699:
700: /**
701: * Parses arguments and fill fields of this object.
702: *
703: * @exception BadCommandLineException
704: * thrown when there's a problem in the command-line arguments
705: */
706: public void parseArguments(String[] args)
707: throws BadCommandLineException {
708:
709: for (int i = 0; i < args.length; i++) {
710: if (args[i].length() == 0)
711: throw new BadCommandLineException();
712: if (args[i].charAt(0) == '-') {
713: int j = parseArgument(args, i);
714: if (j == 0)
715: throw new BadCommandLineException(Messages.format(
716: Messages.UNRECOGNIZED_PARAMETER, args[i]));
717: i += (j - 1);
718: } else {
719: addFile(args[i], grammars, ".xsd");
720: }
721: }
722:
723: // configure proxy
724: if (proxyHost != null || proxyPort != null) {
725: if (proxyHost != null && proxyPort != null) {
726: System.setProperty("http.proxyHost", proxyHost);
727: System.setProperty("http.proxyPort", proxyPort);
728: System.setProperty("https.proxyHost", proxyHost);
729: System.setProperty("https.proxyPort", proxyPort);
730: } else if (proxyHost == null) {
731: throw new BadCommandLineException(Messages
732: .format(Messages.MISSING_PROXYHOST));
733: } else {
734: throw new BadCommandLineException(Messages
735: .format(Messages.MISSING_PROXYPORT));
736: }
737: if (proxyUser != null)
738: System.setProperty("http.proxyUser", proxyUser);
739: if (proxyPassword != null)
740: System.setProperty("http.proxyPassword", proxyPassword);
741:
742: }
743:
744: if (grammars.size() == 0)
745: throw new BadCommandLineException(Messages
746: .format(Messages.MISSING_GRAMMAR));
747:
748: if (schemaLanguage == null)
749: schemaLanguage = guessSchemaLanguage();
750: }
751:
752: /**
753: * Guesses the schema language.
754: */
755: public Language guessSchemaLanguage() {
756: // otherwise, use the file extension.
757: // not a good solution, but very easy.
758: String name = grammars.get(0).getSystemId().toLowerCase();
759:
760: if (name.endsWith(".rng"))
761: return Language.RELAXNG;
762: if (name.endsWith(".rnc"))
763: return Language.RELAXNG_COMPACT;
764: if (name.endsWith(".dtd"))
765: return Language.DTD;
766: if (name.endsWith(".wsdl"))
767: return Language.WSDL;
768:
769: // by default, assume XML Schema
770: return Language.XMLSCHEMA;
771: }
772:
773: /**
774: * Creates a configured CodeWriter that produces files into the specified directory.
775: */
776: public CodeWriter createCodeWriter() throws IOException {
777: return createCodeWriter(new FileCodeWriter(targetDir, readOnly));
778: }
779:
780: /**
781: * Creates a configured CodeWriter that produces files into the specified directory.
782: */
783: public CodeWriter createCodeWriter(CodeWriter core) {
784: if (noFileHeader)
785: return core;
786:
787: // generate format syntax: <date> 'at' <time>
788: String format = Messages.format(Messages.DATE_FORMAT) + " '"
789: + Messages.format(Messages.AT) + "' "
790: + Messages.format(Messages.TIME_FORMAT);
791: SimpleDateFormat dateFormat = new SimpleDateFormat(format);
792:
793: return new PrologCodeWriter(core, Messages.format(
794: Messages.FILE_PROLOG_COMMENT, dateFormat
795: .format(new Date())));
796: }
797:
798: /**
799: * Looks for all "META-INF/services/[className]" files and
800: * create one instance for each class name found inside this file.
801: */
802: private static <T> T[] findServices(Class<T> clazz,
803: ClassLoader classLoader) {
804: // if true, print debug output
805: final boolean debug = com.sun.tools.internal.xjc.util.Util
806: .getSystemProperty(Options.class, "findServices") != null;
807:
808: String serviceId = "META-INF/services/" + clazz.getName();
809:
810: // used to avoid creating the same instance twice
811: Set<String> classNames = new HashSet<String>();
812:
813: if (debug) {
814: System.out.println("Looking for " + serviceId
815: + " for add-ons");
816: }
817:
818: // try to find services in CLASSPATH
819: try {
820: Enumeration<URL> e = classLoader.getResources(serviceId);
821: if (e == null)
822: return (T[]) Array.newInstance(clazz, 0);
823:
824: ArrayList<T> a = new ArrayList<T>();
825: while (e.hasMoreElements()) {
826: URL url = e.nextElement();
827: BufferedReader reader = null;
828:
829: if (debug) {
830: System.out.println("Checking " + url
831: + " for an add-on");
832: }
833:
834: try {
835: reader = new BufferedReader(new InputStreamReader(
836: url.openStream()));
837: String impl;
838: while ((impl = reader.readLine()) != null) {
839: // try to instanciate the object
840: impl = impl.trim();
841: if (classNames.add(impl)) {
842: Class implClass = classLoader
843: .loadClass(impl);
844: if (!clazz.isAssignableFrom(implClass)) {
845: if (debug) {
846: System.out.println(impl
847: + " is not a subclass of "
848: + clazz + ". Skipping");
849: }
850: continue;
851: }
852: if (debug) {
853: System.out
854: .println("Attempting to instanciate "
855: + impl);
856: }
857: a.add(clazz.cast(implClass.newInstance()));
858: }
859: }
860: reader.close();
861: } catch (Exception ex) {
862: // let it go.
863: if (debug) {
864: ex.printStackTrace(System.out);
865: }
866: if (reader != null) {
867: try {
868: reader.close();
869: } catch (IOException ex2) {
870: // ignore
871: }
872: }
873: }
874: }
875:
876: return a.toArray((T[]) Array.newInstance(clazz, a.size()));
877: } catch (Throwable e) {
878: // ignore any error
879: if (debug) {
880: e.printStackTrace(System.out);
881: }
882: return (T[]) Array.newInstance(clazz, 0);
883: }
884: }
885:
886: // this is a convenient place to expose the build version to xjc plugins
887: public static String getBuildID() {
888: return Messages.format(Messages.BUILD_ID);
889: }
890: }
|