src/share/jaxws_classes/com/sun/tools/internal/xjc/Options.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
1 /*
2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.internal.xjc;
27
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.io.PrintWriter;
34 import java.io.StringWriter;
35 import java.lang.reflect.Array;
36 import java.lang.reflect.InvocationTargetException;
37 import java.net.MalformedURLException;
38 import java.net.URL;
39 import java.net.URLClassLoader;
40 import java.text.SimpleDateFormat;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Date;
44 import java.util.Enumeration;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50
51 import com.sun.codemodel.internal.CodeWriter;
52 import com.sun.codemodel.internal.JPackage;
53 import com.sun.codemodel.internal.writer.FileCodeWriter;
54 import com.sun.codemodel.internal.writer.PrologCodeWriter;
55 import com.sun.org.apache.xml.internal.resolver.CatalogManager;
56 import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver;
57 import com.sun.tools.internal.xjc.api.ClassNameAllocator;
58 import com.sun.tools.internal.xjc.api.SpecVersion;
59 import com.sun.tools.internal.xjc.generator.bean.field.FieldRendererFactory;
60 import com.sun.tools.internal.xjc.model.Model;
61 import com.sun.tools.internal.xjc.reader.Util;
62 import com.sun.xml.internal.bind.api.impl.NameConverter;
63 import java.net.URI;
64 import java.net.URISyntaxException;
65 import java.nio.charset.Charset;
66 import java.nio.charset.IllegalCharsetNameException;
67 import java.util.Locale;
68 import java.util.logging.Level;
69 import java.util.logging.Logger;
70
71 import org.xml.sax.EntityResolver;
72 import org.xml.sax.InputSource;
73
74 /**
75 * Global options.
76 *
77 * <p>
78 * This class stores invocation configuration for XJC.
79 * The configuration in this class should be abstract enough so that
80 * it could be parsed from both command-line or Ant.
81 */
82 public class Options
83 {
84 /** If "-debug" is specified. */
85 public boolean debugMode;
86
87 /** If the "-verbose" option is specified. */
88 public boolean verbose;
89
90 /** If the "-quiet" option is specified. */
91 public boolean quiet;
92
93 /** If the -readOnly option is specified. */
94 public boolean readOnly;
95
96 /** No file header comment (to be more friendly with diff.) */
97 public boolean noFileHeader;
98
99 /** When on, fixes getter/setter generation to match the Bean Introspection API */
100 public boolean enableIntrospection;
101
102 /** When on, generates content property for types with multiple xs:any derived elements (which is supposed to be correct behaviour) */
103 public boolean contentForWildcard;
104
105 /** Encoding to be used by generated java sources, null for platform default. */
106 public String encoding;
107
108 /**
109 * Check the source schemas with extra scrutiny.
110 * The exact meaning depends on the schema language.
111 */
112 public boolean strictCheck =true;
113
114 /**
115 * If -explicit-annotation option is specified.
116 * <p>
117 * This generates code that works around issues specific to 1.4 runtime.
118 */
119 public boolean runtime14 = false;
120
121 /**
122 * If true, try to resolve name conflicts automatically by assigning mechanical numbers.
123 */
124 public boolean automaticNameConflictResolution = false;
125
126 /**
127 * strictly follow the compatibility rules and reject schemas that
128 * contain features from App. E.2, use vendor binding extensions
129 */
130 public static final int STRICT = 1;
131 /**
132 * loosely follow the compatibility rules and allow the use of vendor
133 * binding extensions
134 */
135 public static final int EXTENSION = 2;
136
137 /**
138 * this switch determines how carefully the compiler will follow
139 * the compatibility rules in the spec. Either <code>STRICT</code>
140 * or <code>EXTENSION</code>.
141 */
142 public int compatibilityMode = STRICT;
143
144 public boolean isExtensionMode() {
145 return compatibilityMode==EXTENSION;
146 }
147
148 private static final Logger logger = com.sun.xml.internal.bind.Util.getClassLogger();
149
150 /**
151 * Generates output for the specified version of the runtime.
152 */
153 public SpecVersion target = SpecVersion.LATEST;
154
155 private boolean is2_2 = true;
156
157 public Options() {
158 if (is2_2) {
159 try {
160 Class.forName("javax.xml.bind.JAXBPermission");
161 } catch (ClassNotFoundException cnfe) {
162 is2_2 = false;
163 }
164 if (!is2_2) {
165 target = SpecVersion.V2_1;
166 } else {
167 target = SpecVersion.LATEST;
168 }
169 }
170 }
171
172 /**
173 * Target directory when producing files.
174 * <p>
175 * This field is not used when XJC is driven through the XJC API.
176 * Plugins that need to generate extra files should do so by using
177 * {@link JPackage#addResourceFile(JResourceFile)}.
178 */
179 public File targetDir = new File(".");
180
181 /**
182 * Actually stores {@link CatalogResolver}, but the field
183 * type is made to {@link EntityResolver} so that XJC can be
184 * used even if resolver.jar is not available in the classpath.
185 */
186 public EntityResolver entityResolver = null;
187
188 /**
189 * Type of input schema language. One of the <code>SCHEMA_XXX</code>
190 * constants.
191 */
192 private Language schemaLanguage = null;
193
194 /**
195 * The -p option that should control the default Java package that
196 * will contain the generated code. Null if unspecified.
197 */
198 public String defaultPackage = null;
199
200 /**
201 * Similar to the -p option, but this one works with a lower priority,
202 * and customizations overrides this. Used by JAX-RPC.
203 */
204 public String defaultPackage2 = null;
205
206 /**
207 * Input schema files as a list of {@link InputSource}s.
208 */
209 private final List<InputSource> grammars = new ArrayList<InputSource>();
210
211 private final List<InputSource> bindFiles = new ArrayList<InputSource>();
212
213 // Proxy setting.
214 private String proxyHost = null;
215 private String proxyPort = null;
216 private String proxyUser = null;
217 private String proxyPassword = null;
218
219 /**
220 * {@link Plugin}s that are enabled in this compilation.
221 */
222 public final List<Plugin> activePlugins = new ArrayList<Plugin>();
223
224 /**
225 * All discovered {@link Plugin}s.
226 * This is lazily parsed, so that we can take '-cp' option into account.
227 *
228 * @see #getAllPlugins()
229 */
230 private List<Plugin> allPlugins;
231
232 /**
233 * Set of URIs that plug-ins recognize as extension bindings.
234 */
235 public final Set<String> pluginURIs = new HashSet<String>();
236
237 /**
238 * This allocator has the final say on deciding the class name.
239 */
240 public ClassNameAllocator classNameAllocator;
241
242 /**
243 * This switch controls whether or not xjc will generate package level annotations
244 */
245 public boolean packageLevelAnnotations = true;
246
247 /**
248 * This {@link FieldRendererFactory} determines how the fields are generated.
249 */
250 private FieldRendererFactory fieldRendererFactory = new FieldRendererFactory();
251 /**
252 * Used to detect if two {@link Plugin}s try to overwrite {@link #fieldRendererFactory}.
253 */
254 private Plugin fieldRendererFactoryOwner = null;
255
256 /**
257 * If this is non-null, we use this {@link NameConverter} over the one
258 * given in the schema/binding.
259 */
260 private NameConverter nameConverter = null;
261 /**
262 * Used to detect if two {@link Plugin}s try to overwrite {@link #nameConverter}.
263 */
264 private Plugin nameConverterOwner = null;
265
266 /**
267 * Gets the active {@link FieldRendererFactory} that shall be used to build {@link Model}.
268 *
269 * @return always non-null.
270 */
271 public FieldRendererFactory getFieldRendererFactory() {
272 return fieldRendererFactory;
273 }
274
275 /**
276 * Sets the {@link FieldRendererFactory}.
277 *
278 * <p>
279 * This method is for plugins to call to set a custom {@link FieldRendererFactory}.
280 *
281 * @param frf
282 * The {@link FieldRendererFactory} to be installed. Must not be null.
283 * @param owner
284 * Identifies the plugin that owns this {@link FieldRendererFactory}.
285 * When two {@link Plugin}s try to call this method, this allows XJC
286 * to report it as a user-friendly error message.
287 *
288 * @throws BadCommandLineException
289 * If a conflit happens, this exception carries a user-friendly error
290 * message, indicating a conflict.
291 */
292 public void setFieldRendererFactory(FieldRendererFactory frf, Plugin owner) throws BadCommandLineException {
293 // since this method is for plugins, make it bit more fool-proof than usual
294 if(frf==null)
295 throw new IllegalArgumentException();
296 if(fieldRendererFactoryOwner!=null) {
297 throw new BadCommandLineException(
298 Messages.format(Messages.FIELD_RENDERER_CONFLICT,
299 fieldRendererFactoryOwner.getOptionName(),
300 owner.getOptionName() ));
301 }
302 this.fieldRendererFactoryOwner = owner;
303 this.fieldRendererFactory = frf;
304 }
305
306
307 /**
308 * Gets the active {@link NameConverter} that shall be used to build {@link Model}.
309 *
310 * @return can be null, in which case it's up to the binding.
311 */
312 public NameConverter getNameConverter() {
313 return nameConverter;
314 }
315
316 /**
317 * Sets the {@link NameConverter}.
318 *
319 * <p>
320 * This method is for plugins to call to set a custom {@link NameConverter}.
321 *
322 * @param nc
323 * The {@link NameConverter} to be installed. Must not be null.
324 * @param owner
325 * Identifies the plugin that owns this {@link NameConverter}.
326 * When two {@link Plugin}s try to call this method, this allows XJC
327 * to report it as a user-friendly error message.
328 *
329 * @throws BadCommandLineException
330 * If a conflit happens, this exception carries a user-friendly error
331 * message, indicating a conflict.
332 */
333 public void setNameConverter(NameConverter nc, Plugin owner) throws BadCommandLineException {
334 // since this method is for plugins, make it bit more fool-proof than usual
335 if(nc==null)
336 throw new IllegalArgumentException();
337 if(nameConverter!=null) {
338 throw new BadCommandLineException(
339 Messages.format(Messages.NAME_CONVERTER_CONFLICT,
340 nameConverterOwner.getOptionName(),
341 owner.getOptionName() ));
342 }
343 this.nameConverterOwner = owner;
344 this.nameConverter = nc;
345 }
346
347 /**
348 * Gets all the {@link Plugin}s discovered so far.
349 *
350 * <p>
351 * A plugins are enumerated when this method is called for the first time,
352 * by taking {@link #classpaths} into account. That means
353 * "-cp plugin.jar" has to come before you specify options to enable it.
354 */
355 public List<Plugin> getAllPlugins() {
356 if(allPlugins==null) {
357 allPlugins = new ArrayList<Plugin>();
358 ClassLoader ucl = getUserClassLoader(SecureLoader.getClassClassLoader(getClass()));
359 allPlugins.addAll(Arrays.asList(findServices(Plugin.class,ucl)));
360 }
361
362 return allPlugins;
363 }
364
365 public Language getSchemaLanguage() {
366 if( schemaLanguage==null)
367 schemaLanguage = guessSchemaLanguage();
368 return schemaLanguage;
369 }
370 public void setSchemaLanguage(Language _schemaLanguage) {
371 this.schemaLanguage = _schemaLanguage;
372 }
373
374 /** Input schema files. */
375 public InputSource[] getGrammars() {
376 return grammars.toArray(new InputSource[grammars.size()]);
377 }
378
379 /**
380 * Adds a new input schema.
381 */
382 public void addGrammar( InputSource is ) {
383 grammars.add(absolutize(is));
384 }
385
386 private InputSource fileToInputSource( File source ) {
387 try {
388 String url = source.toURL().toExternalForm();
389 return new InputSource(Util.escapeSpace(url));
390 } catch (MalformedURLException e) {
391 return new InputSource(source.getPath());
392 }
393 }
394
395 public void addGrammar( File source ) {
396 addGrammar(fileToInputSource(source));
397 }
398
399 /**
400 * Recursively scan directories and add all XSD files in it.
401 */
402 public void addGrammarRecursive( File dir ) {
403 addRecursive(dir,".xsd",grammars);
404 }
405
406 private void addRecursive( File dir, String suffix, List<InputSource> result ) {
407 File[] files = dir.listFiles();
408 if(files==null) return; // work defensively
409
410 for( File f : files ) {
411 if(f.isDirectory())
412 addRecursive(f,suffix,result);
413 else
414 if(f.getPath().endsWith(suffix))
415 result.add(absolutize(fileToInputSource(f)));
416 }
417 }
418
419
420 private InputSource absolutize(InputSource is) {
421 // absolutize all the system IDs in the input, so that we can map system IDs to DOM trees.
422 try {
423 URL baseURL = new File(".").getCanonicalFile().toURL();
424 is.setSystemId( new URL(baseURL,is.getSystemId()).toExternalForm() );
425 } catch( IOException e ) {
426 logger.log(Level.FINE, "{0}, {1}", new Object[]{is.getSystemId(), e.getLocalizedMessage()});
427 }
428 return is;
429 }
430
431 /** Input external binding files. */
432 public InputSource[] getBindFiles() {
433 return bindFiles.toArray(new InputSource[bindFiles.size()]);
434 }
435
436 /**
437 * Adds a new binding file.
438 */
439 public void addBindFile( InputSource is ) {
440 bindFiles.add(absolutize(is));
441 }
442
443 /**
444 * Adds a new binding file.
445 */
446 public void addBindFile( File bindFile ) {
447 bindFiles.add(fileToInputSource(bindFile));
448 }
449
450 /**
451 * Recursively scan directories and add all ".xjb" files in it.
452 */
453 public void addBindFileRecursive( File dir ) {
454 addRecursive(dir,".xjb",bindFiles);
455 }
456
457 public final List<URL> classpaths = new ArrayList<URL>();
458 /**
459 * Gets a classLoader that can load classes specified via the
460 * -classpath option.
461 */
462 public URLClassLoader getUserClassLoader( ClassLoader parent ) {
463 return new URLClassLoader(
464 classpaths.toArray(new URL[classpaths.size()]),parent);
465 }
466
467
468 /**
469 * Parses an option <code>args[i]</code> and return
470 * the number of tokens consumed.
471 *
472 * @return
473 * 0 if the argument is not understood. Returning 0
474 * will let the caller report an error.
475 * @exception BadCommandLineException
476 * If the callee wants to provide a custom message for an error.
477 */
478 public int parseArgument( String[] args, int i ) throws BadCommandLineException {
479 if (args[i].equals("-classpath") || args[i].equals("-cp")) {
480 String a = requireArgument(args[i], args, ++i);
481 for (String p : a.split(File.pathSeparator)) {
482 File file = new File(p);
483 try {
484 classpaths.add(file.toURL());
485 } catch (MalformedURLException e) {
486 throw new BadCommandLineException(
487 Messages.format(Messages.NOT_A_VALID_FILENAME,file),e);
488 }
489 }
490 return 2;
491 }
492 if (args[i].equals("-d")) {
493 targetDir = new File(requireArgument("-d",args,++i));
494 if( !targetDir.exists() )
495 throw new BadCommandLineException(
496 Messages.format(Messages.NON_EXISTENT_DIR,targetDir));
497 return 2;
498 }
499 if (args[i].equals("-readOnly")) {
500 readOnly = true;
501 return 1;
502 }
503 if (args[i].equals("-p")) {
504 defaultPackage = requireArgument("-p",args,++i);
505 if(defaultPackage.length()==0) { // user specified default package
506 // there won't be any package to annotate, so disable them
507 // automatically as a usability feature
508 packageLevelAnnotations = false;
509 }
510 return 2;
511 }
512 if (args[i].equals("-debug")) {
513 debugMode = true;
514 verbose = true;
515 return 1;
516 }
517 if (args[i].equals("-nv")) {
518 strictCheck = false;
519 return 1;
520 }
521 if( args[i].equals("-npa")) {
522 packageLevelAnnotations = false;
523 return 1;
524 }
525 if( args[i].equals("-no-header")) {
526 noFileHeader = true;
527 return 1;
528 }
529 if (args[i].equals("-verbose")) {
530 verbose = true;
531 return 1;
532 }
533 if (args[i].equals("-quiet")) {
534 quiet = true;
535 return 1;
536 }
537 if (args[i].equals("-XexplicitAnnotation")) {
538 runtime14 = true;
539 return 1;
540 }
541 if (args[i].equals("-enableIntrospection")) {
542 enableIntrospection = true;
543 return 1;
544 }
545 if (args[i].equals("-contentForWildcard")) {
546 contentForWildcard = true;
547 return 1;
548 }
549 if (args[i].equals("-XautoNameResolution")) {
550 automaticNameConflictResolution = true;
551 return 1;
552 }
553 if (args[i].equals("-b")) {
554 addFile(requireArgument("-b",args,++i),bindFiles,".xjb");
555 return 2;
556 }
557 if (args[i].equals("-dtd")) {
558 schemaLanguage = Language.DTD;
559 return 1;
560 }
561 if (args[i].equals("-relaxng")) {
562 schemaLanguage = Language.RELAXNG;
563 return 1;
564 }
565 if (args[i].equals("-relaxng-compact")) {
566 schemaLanguage = Language.RELAXNG_COMPACT;
567 return 1;
568 }
569 if (args[i].equals("-xmlschema")) {
570 schemaLanguage = Language.XMLSCHEMA;
571 return 1;
572 }
573 if (args[i].equals("-wsdl")) {
574 schemaLanguage = Language.WSDL;
575 return 1;
576 }
577 if (args[i].equals("-extension")) {
578 compatibilityMode = EXTENSION;
579 return 1;
580 }
581 if (args[i].equals("-target")) {
582 String token = requireArgument("-target",args,++i);
583 target = SpecVersion.parse(token);
584 if(target==null)
585 throw new BadCommandLineException(Messages.format(Messages.ILLEGAL_TARGET_VERSION,token));
586 return 2;
587 }
588 if (args[i].equals("-httpproxyfile")) {
589 if (i == args.length - 1 || args[i + 1].startsWith("-")) {
590 throw new BadCommandLineException(
591 Messages.format(Messages.MISSING_PROXYFILE));
592 }
593
594 File file = new File(args[++i]);
595 if(!file.exists()) {
596 throw new BadCommandLineException(
597 Messages.format(Messages.NO_SUCH_FILE,file));
598 }
599
600 try {
601 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
602 parseProxy(in.readLine());
603 in.close();
604 } catch (IOException e) {
605 throw new BadCommandLineException(
606 Messages.format(Messages.FAILED_TO_PARSE,file,e.getMessage()),e);
607 }
608
609 return 2;
610 }
611 if (args[i].equals("-httpproxy")) {
612 if (i == args.length - 1 || args[i + 1].startsWith("-")) {
613 throw new BadCommandLineException(
614 Messages.format(Messages.MISSING_PROXY));
615 }
616
617 parseProxy(args[++i]);
618 return 2;
619 }
620 if (args[i].equals("-host")) {
621 proxyHost = requireArgument("-host",args,++i);
622 return 2;
623 }
624 if (args[i].equals("-port")) {
625 proxyPort = requireArgument("-port",args,++i);
626 return 2;
627 }
628 if( args[i].equals("-catalog") ) {
629 // use Sun's "XML Entity and URI Resolvers" by Norman Walsh
630 // to resolve external entities.
631 // http://www.sun.com/xml/developers/resolver/
632
633 File catalogFile = new File(requireArgument("-catalog",args,++i));
634 try {
635 addCatalog(catalogFile);
636 } catch (IOException e) {
637 throw new BadCommandLineException(
638 Messages.format(Messages.FAILED_TO_PARSE,catalogFile,e.getMessage()),e);
639 }
640 return 2;
641 }
642 if (args[i].equals("-source")) {
643 String version = requireArgument("-source",args,++i);
644 //For source 1.0 the 1.0 Driver is loaded
645 //Hence anything other than 2.0 is defaulted to
646 //2.0
647 if( !version.equals("2.0") && !version.equals("2.1") )
648 throw new BadCommandLineException(
649 Messages.format(Messages.DEFAULT_VERSION));
650 return 2;
651 }
652 if( args[i].equals("-Xtest-class-name-allocator") ) {
653 classNameAllocator = new ClassNameAllocator() {
654 public String assignClassName(String packageName, String className) {
655 System.out.printf("assignClassName(%s,%s)\n",packageName,className);
656 return className+"_Type";
657 }
658 };
659 return 1;
660 }
661
662 if (args[i].equals("-encoding")) {
663 encoding = requireArgument("-encoding", args, ++i);
664 try {
665 if (!Charset.isSupported(encoding)) {
666 throw new BadCommandLineException(
667 Messages.format(Messages.UNSUPPORTED_ENCODING, encoding));
668 }
669 } catch (IllegalCharsetNameException icne) {
670 throw new BadCommandLineException(
671 Messages.format(Messages.UNSUPPORTED_ENCODING, encoding));
672 }
673 return 2;
674 }
675
676 // see if this is one of the extensions
677 for( Plugin plugin : getAllPlugins() ) {
678 try {
679 if( ('-'+plugin.getOptionName()).equals(args[i]) ) {
680 activePlugins.add(plugin);
681 plugin.onActivated(this);
682 pluginURIs.addAll(plugin.getCustomizationURIs());
683
684 // give the plugin a chance to parse arguments to this option.
685 // this is new in 2.1, and due to the backward compatibility reason,
686 // if plugin didn't understand it, we still return 1 to indicate
687 // that this option is consumed.
688 int r = plugin.parseArgument(this,args,i);
689 if(r!=0)
690 return r;
691 else
692 return 1;
693 }
694
695 int r = plugin.parseArgument(this,args,i);
696 if(r!=0) return r;
697 } catch (IOException e) {
698 throw new BadCommandLineException(e.getMessage(),e);
699 }
700 }
701
702 return 0; // unrecognized
703 }
704
705 private void parseProxy(String text) throws BadCommandLineException {
706 // syntax is [user[:password]@]proxyHost:proxyPort
707 String token = "([^@:]+)";
708 Pattern p = Pattern.compile("(?:"+token+"(?:\\:"+token+")?\\@)?"+token+"(?:\\:"+token+")");
709
710 Matcher matcher = p.matcher(text);
711 if(!matcher.matches())
712 throw new BadCommandLineException(Messages.format(Messages.ILLEGAL_PROXY,text));
713
714 proxyUser = matcher.group(1);
715 proxyPassword = matcher.group(2);
716 proxyHost = matcher.group(3);
717 proxyPort = matcher.group(4);
718 try {
719 Integer.valueOf(proxyPort);
720 } catch (NumberFormatException e) {
721 throw new BadCommandLineException(Messages.format(Messages.ILLEGAL_PROXY,text));
722 }
723 }
724
725 /**
726 * Obtains an operand and reports an error if it's not there.
727 */
728 public String requireArgument(String optionName, String[] args, int i) throws BadCommandLineException {
729 if (i == args.length || args[i].startsWith("-")) {
730 throw new BadCommandLineException(
731 Messages.format(Messages.MISSING_OPERAND,optionName));
732 }
733 return args[i];
734 }
735
736 /**
737 * Parses a token to a file (or a set of files)
738 * and add them as {@link InputSource} to the specified list.
739 *
740 * @param suffix
741 * If the given token is a directory name, we do a recusive search
742 * and find all files that have the given suffix.
743 */
744 private void addFile(String name, List<InputSource> target, String suffix) throws BadCommandLineException {
745 Object src;
746 try {
747 src = Util.getFileOrURL(name);
748 } catch (IOException e) {
749 throw new BadCommandLineException(
750 Messages.format(Messages.NOT_A_FILE_NOR_URL,name));
751 }
752 if(src instanceof URL) {
753 target.add(absolutize(new InputSource(Util.escapeSpace(((URL)src).toExternalForm()))));
754 } else {
755 File fsrc = (File)src;
756 if(fsrc.isDirectory()) {
757 addRecursive(fsrc,suffix,target);
758 } else {
759 target.add(absolutize(fileToInputSource(fsrc)));
760 }
761 }
762 }
763
764 /**
765 * Adds a new catalog file.
766 */
767 public void addCatalog(File catalogFile) throws IOException {
768 if(entityResolver==null) {
769 CatalogManager.getStaticManager().setIgnoreMissingProperties(true);
770 entityResolver = new CatalogResolver(true);
771 }
772 ((CatalogResolver)entityResolver).getCatalog().parseCatalog(catalogFile.getPath());
773 }
774
775 /**
776 * Parses arguments and fill fields of this object.
777 *
778 * @exception BadCommandLineException
779 * thrown when there's a problem in the command-line arguments
780 */
781 public void parseArguments( String[] args ) throws BadCommandLineException {
782
783 for (int i = 0; i < args.length; i++) {
784 if(args[i].length()==0)
785 throw new BadCommandLineException();
786 if (args[i].charAt(0) == '-') {
787 int j = parseArgument(args,i);
788 if(j==0)
789 throw new BadCommandLineException(
790 Messages.format(Messages.UNRECOGNIZED_PARAMETER, args[i]));
791 i += (j-1);
792 } else {
793 if(args[i].endsWith(".jar"))
794 scanEpisodeFile(new File(args[i]));
795 else
796 addFile(args[i],grammars,".xsd");
797 }
798 }
799
800 // configure proxy
801 if (proxyHost != null || proxyPort != null) {
802 if (proxyHost != null && proxyPort != null) {
803 System.setProperty("http.proxyHost", proxyHost);
804 System.setProperty("http.proxyPort", proxyPort);
805 System.setProperty("https.proxyHost", proxyHost);
806 System.setProperty("https.proxyPort", proxyPort);
807 } else if (proxyHost == null) {
808 throw new BadCommandLineException(
809 Messages.format(Messages.MISSING_PROXYHOST));
810 } else {
811 throw new BadCommandLineException(
812 Messages.format(Messages.MISSING_PROXYPORT));
813 }
814 if(proxyUser!=null)
815 System.setProperty("http.proxyUser", proxyUser);
816 if(proxyPassword!=null)
817 System.setProperty("http.proxyPassword", proxyPassword);
818
819 }
820
821 if (grammars.isEmpty())
822 throw new BadCommandLineException(
823 Messages.format(Messages.MISSING_GRAMMAR));
824
825 if( schemaLanguage==null )
826 schemaLanguage = guessSchemaLanguage();
827
828 // if(target==SpecVersion.V2_2 && !isExtensionMode())
829 // throw new BadCommandLineException(
830 // "Currently 2.2 is still not finalized yet, so using it requires the -extension switch." +
831 // "NOTE THAT 2.2 SPEC MAY CHANGE BEFORE IT BECOMES FINAL.");
832
833 if(pluginLoadFailure!=null)
834 throw new BadCommandLineException(
835 Messages.format(Messages.PLUGIN_LOAD_FAILURE,pluginLoadFailure));
836 }
837
838 /**
839 * Finds the <tt>META-INF/sun-jaxb.episode</tt> file to add as a binding customization.
840 */
841 public void scanEpisodeFile(File jar) throws BadCommandLineException {
842 try {
843 URLClassLoader ucl = new URLClassLoader(new URL[]{jar.toURL()});
844 Enumeration<URL> resources = ucl.findResources("META-INF/sun-jaxb.episode");
845 while (resources.hasMoreElements()) {
846 URL url = resources.nextElement();
847 addBindFile(new InputSource(url.toExternalForm()));
848 }
849 } catch (IOException e) {
850 throw new BadCommandLineException(
851 Messages.format(Messages.FAILED_TO_LOAD,jar,e.getMessage()), e);
852 }
853 }
854
855
856 /**
857 * Guesses the schema language.
858 */
859 public Language guessSchemaLanguage() {
860
861 // otherwise, use the file extension.
862 // not a good solution, but very easy.
863 if ((grammars != null) && (grammars.size() > 0)) {
864 String name = grammars.get(0).getSystemId().toLowerCase();
865
866 if (name.endsWith(".rng"))
867 return Language.RELAXNG;
868 if (name.endsWith(".rnc"))
869 return Language.RELAXNG_COMPACT;
870 if (name.endsWith(".dtd"))
871 return Language.DTD;
872 if (name.endsWith(".wsdl"))
873 return Language.WSDL;
874 }
875
876 // by default, assume XML Schema
877 return Language.XMLSCHEMA;
878 }
879
880 /**
881 * Creates a configured CodeWriter that produces files into the specified directory.
882 */
883 public CodeWriter createCodeWriter() throws IOException {
884 return createCodeWriter(new FileCodeWriter( targetDir, readOnly, encoding ));
885 }
886
887 /**
888 * Creates a configured CodeWriter that produces files into the specified directory.
889 */
890 public CodeWriter createCodeWriter( CodeWriter core ) {
891 if(noFileHeader)
892 return core;
893
894 return new PrologCodeWriter( core,getPrologComment() );
895 }
896
897 /**
898 * Gets the string suitable to be used as the prolog comment baked into artifacts.
899 * This is the string like "This file was generated by the JAXB RI on YYYY/mm/dd..."
900 */
901 public String getPrologComment() {
902 // generate format syntax: <date> 'at' <time>
903 String format =
904 Messages.format(Messages.DATE_FORMAT)
905 + " '"
906 + Messages.format(Messages.AT)
907 + "' "
908 + Messages.format(Messages.TIME_FORMAT);
909 SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.ENGLISH);
910
911 return Messages.format(
912 Messages.FILE_PROLOG_COMMENT,
913 dateFormat.format(new Date()));
914 }
915
916 /**
917 * If a plugin failed to load, report.
918 */
919 private static String pluginLoadFailure;
920
921 /**
922 * Looks for all "META-INF/services/[className]" files and
923 * create one instance for each class name found inside this file.
924 */
925 private static <T> T[] findServices( Class<T> clazz, ClassLoader classLoader ) {
926 // if true, print debug output
927 final boolean debug = com.sun.tools.internal.xjc.util.Util.getSystemProperty(Options.class,"findServices")!=null;
928
929 // if we are running on Mustang or Dolphin, use ServiceLoader
930 // so that we can take advantage of JSR-277 module system.
931 try {
932 Class<?> serviceLoader = Class.forName("java.util.ServiceLoader");
933 if(debug)
934 System.out.println("Using java.util.ServiceLoader");
935 Iterable<T> itr = (Iterable<T>)serviceLoader.getMethod("load",Class.class,ClassLoader.class).invoke(null,clazz,classLoader);
936 List<T> r = new ArrayList<T>();
937 for (T t : itr)
938 r.add(t);
939 return r.toArray((T[])Array.newInstance(clazz,r.size()));
940 } catch (ClassNotFoundException e) {
941 // fall through
942 } catch (IllegalAccessException e) {
943 Error x = new IllegalAccessError();
944 x.initCause(e);
945 throw x;
946 } catch (InvocationTargetException e) {
947 Throwable x = e.getTargetException();
948 if (x instanceof RuntimeException)
949 throw (RuntimeException) x;
950 if (x instanceof Error)
951 throw (Error) x;
952 throw new Error(x);
953 } catch (NoSuchMethodException e) {
954 Error x = new NoSuchMethodError();
955 x.initCause(e);
956 throw x;
957 }
958
959 String serviceId = "META-INF/services/" + clazz.getName();
960
961 // used to avoid creating the same instance twice
962 Set<String> classNames = new HashSet<String>();
963
964 if(debug) {
965 System.out.println("Looking for "+serviceId+" for add-ons");
966 }
967
968 // try to find services in CLASSPATH
969 try {
970 Enumeration<URL> e = classLoader.getResources(serviceId);
971 if(e==null) return (T[])Array.newInstance(clazz,0);
972
973 ArrayList<T> a = new ArrayList<T>();
974 while(e.hasMoreElements()) {
975 URL url = e.nextElement();
976 BufferedReader reader=null;
977
978 if(debug) {
979 System.out.println("Checking "+url+" for an add-on");
980 }
981
982 try {
983 reader = new BufferedReader(new InputStreamReader(url.openStream()));
984 String impl;
985 while((impl = reader.readLine())!=null ) {
986 // try to instanciate the object
987 impl = impl.trim();
988 if(classNames.add(impl)) {
989 Class implClass = classLoader.loadClass(impl);
990 if(!clazz.isAssignableFrom(implClass)) {
991 pluginLoadFailure = impl+" is not a subclass of "+clazz+". Skipping";
992 if(debug)
993 System.out.println(pluginLoadFailure);
994 continue;
995 }
996 if(debug) {
997 System.out.println("Attempting to instanciate "+impl);
998 }
999 a.add(clazz.cast(implClass.newInstance()));
1000 }
1001 }
1002 reader.close();
1003 } catch( Exception ex ) {
1004 // let it go.
1005 StringWriter w = new StringWriter();
1006 ex.printStackTrace(new PrintWriter(w));
1007 pluginLoadFailure = w.toString();
1008 if(debug) {
1009 System.out.println(pluginLoadFailure);
1010 }
1011 if( reader!=null ) {
1012 try {
1013 reader.close();
1014 } catch( IOException ex2 ) {
1015 // ignore
1016 }
1017 }
1018 }
1019 }
1020
1021 return a.toArray((T[])Array.newInstance(clazz,a.size()));
1022 } catch( Throwable e ) {
1023 // ignore any error
1024 StringWriter w = new StringWriter();
1025 e.printStackTrace(new PrintWriter(w));
1026 pluginLoadFailure = w.toString();
1027 if(debug) {
1028 System.out.println(pluginLoadFailure);
1029 }
1030 return (T[])Array.newInstance(clazz,0);
1031 }
1032 }
1033
1034 // this is a convenient place to expose the build version to xjc plugins
1035 public static String getBuildID() {
1036 return Messages.format(Messages.BUILD_ID);
1037 }
1038
1039 public static String normalizeSystemId(String systemId) {
1040 try {
1041 systemId = new URI(systemId).normalize().toString();
1042 } catch (URISyntaxException e) {
1043 // leave the system ID untouched. In my experience URI is often too strict
1044 }
1045 return systemId;
1046 }
1047
1048 }

mercurial