1 /* 2 * Copyright (c) 1997, 2014, 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.net.MalformedURLException; 36 import java.net.URL; 37 import java.net.URLClassLoader; 38 import java.security.AccessController; 39 import java.security.PrivilegedAction; 40 import java.text.SimpleDateFormat; 41 import java.util.ArrayList; 42 import java.util.Date; 43 import java.util.Enumeration; 44 import java.util.HashSet; 45 import java.util.List; 46 import java.util.ServiceLoader; 47 import java.util.Set; 48 49 import com.sun.codemodel.internal.CodeWriter; 50 import com.sun.codemodel.internal.JPackage; 51 import com.sun.codemodel.internal.JResourceFile; 52 import com.sun.codemodel.internal.writer.FileCodeWriter; 53 import com.sun.codemodel.internal.writer.PrologCodeWriter; 54 import com.sun.istack.internal.tools.DefaultAuthenticator; 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 * If true XML security features when parsing XML documents will be disabled. 110 * The default value is false. 111 * 112 * Boolean 113 * @since 2.2.6 114 */ 115 public boolean disableXmlSecurity; 116 117 /** 118 * Check the source schemas with extra scrutiny. 119 * The exact meaning depends on the schema language. 120 */ 121 public boolean strictCheck =true; 122 123 /** 124 * If -explicit-annotation option is specified. 125 * <p> 126 * This generates code that works around issues specific to 1.4 runtime. 127 */ 128 public boolean runtime14 = false; 129 130 /** 131 * If true, try to resolve name conflicts automatically by assigning mechanical numbers. 132 */ 133 public boolean automaticNameConflictResolution = false; 134 135 /** 136 * strictly follow the compatibility rules and reject schemas that 137 * contain features from App. E.2, use vendor binding extensions 138 */ 139 public static final int STRICT = 1; 140 /** 141 * loosely follow the compatibility rules and allow the use of vendor 142 * binding extensions 143 */ 144 public static final int EXTENSION = 2; 145 146 /** 147 * this switch determines how carefully the compiler will follow 148 * the compatibility rules in the spec. Either <code>STRICT</code> 149 * or <code>EXTENSION</code>. 150 */ 151 public int compatibilityMode = STRICT; 152 153 public boolean isExtensionMode() { 154 return compatibilityMode==EXTENSION; 155 } 156 157 private static final Logger logger = com.sun.xml.internal.bind.Util.getClassLogger(); 158 159 /** 160 * Generates output for the specified version of the runtime. 161 */ 162 public SpecVersion target = SpecVersion.LATEST; 163 164 165 public Options() { 166 try { 167 Class.forName("javax.xml.bind.JAXBPermission"); 168 } catch (ClassNotFoundException cnfe) { 169 target = SpecVersion.V2_1; 170 } 171 } 172 173 /** 174 * Target directory when producing files. 175 * <p> 176 * This field is not used when XJC is driven through the XJC API. 177 * Plugins that need to generate extra files should do so by using 178 * {@link JPackage#addResourceFile(JResourceFile)}. 179 */ 180 public File targetDir = new File("."); 181 182 /** 183 * Actually stores {@link CatalogResolver}, but the field 184 * type is made to {@link EntityResolver} so that XJC can be 185 * used even if resolver.jar is not available in the classpath. 186 */ 187 public EntityResolver entityResolver = null; 188 189 /** 190 * Type of input schema language. One of the <code>SCHEMA_XXX</code> 191 * constants. 192 */ 193 private Language schemaLanguage = null; 194 195 /** 196 * The -p option that should control the default Java package that 197 * will contain the generated code. Null if unspecified. 198 */ 199 public String defaultPackage = null; 200 201 /** 202 * Similar to the -p option, but this one works with a lower priority, 203 * and customizations overrides this. Used by JAX-RPC. 204 */ 205 public String defaultPackage2 = null; 206 207 /** 208 * Input schema files as a list of {@link InputSource}s. 209 */ 210 private final List<InputSource> grammars = new ArrayList<InputSource>(); 211 212 private final List<InputSource> bindFiles = new ArrayList<InputSource>(); 213 214 // Proxy setting. 215 private String proxyHost = null; 216 private String proxyPort = null; 217 public String proxyAuth = 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 = findServices(Plugin.class); 358 } 359 360 return allPlugins; 361 } 362 363 public Language getSchemaLanguage() { 364 if( schemaLanguage==null) 365 schemaLanguage = guessSchemaLanguage(); 366 return schemaLanguage; 367 } 368 public void setSchemaLanguage(Language _schemaLanguage) { 369 this.schemaLanguage = _schemaLanguage; 370 } 371 372 /** Input schema files. */ 373 public InputSource[] getGrammars() { 374 return grammars.toArray(new InputSource[grammars.size()]); 375 } 376 377 /** 378 * Adds a new input schema. 379 */ 380 public void addGrammar( InputSource is ) { 381 grammars.add(absolutize(is)); 382 } 383 384 private InputSource fileToInputSource( File source ) { 385 try { 386 String url = source.toURL().toExternalForm(); 387 return new InputSource(Util.escapeSpace(url)); 388 } catch (MalformedURLException e) { 389 return new InputSource(source.getPath()); 390 } 391 } 392 393 public void addGrammar( File source ) { 394 addGrammar(fileToInputSource(source)); 395 } 396 397 /** 398 * Recursively scan directories and add all XSD files in it. 399 */ 400 public void addGrammarRecursive( File dir ) { 401 addRecursive(dir,".xsd",grammars); 402 } 403 404 private void addRecursive( File dir, String suffix, List<InputSource> result ) { 405 File[] files = dir.listFiles(); 406 if(files==null) return; // work defensively 407 408 for( File f : files ) { 409 if(f.isDirectory()) 410 addRecursive(f,suffix,result); 411 else 412 if(f.getPath().endsWith(suffix)) 413 result.add(absolutize(fileToInputSource(f))); 414 } 415 } 416 417 418 private InputSource absolutize(InputSource is) { 419 // absolutize all the system IDs in the input, so that we can map system IDs to DOM trees. 420 try { 421 URL baseURL = new File(".").getCanonicalFile().toURL(); 422 is.setSystemId( new URL(baseURL,is.getSystemId()).toExternalForm() ); 423 } catch( IOException e ) { 424 logger.log(Level.FINE, "{0}, {1}", new Object[]{is.getSystemId(), e.getLocalizedMessage()}); 425 } 426 return is; 427 } 428 429 /** Input external binding files. */ 430 public InputSource[] getBindFiles() { 431 return bindFiles.toArray(new InputSource[bindFiles.size()]); 432 } 433 434 /** 435 * Adds a new binding file. 436 */ 437 public void addBindFile( InputSource is ) { 438 bindFiles.add(absolutize(is)); 439 } 440 441 /** 442 * Adds a new binding file. 443 */ 444 public void addBindFile( File bindFile ) { 445 bindFiles.add(fileToInputSource(bindFile)); 446 } 447 448 /** 449 * Recursively scan directories and add all ".xjb" files in it. 450 */ 451 public void addBindFileRecursive( File dir ) { 452 addRecursive(dir,".xjb",bindFiles); 453 } 454 455 public final List<URL> classpaths = new ArrayList<URL>(); 456 /** 457 * Gets a classLoader that can load classes specified via the 458 * -classpath option. 459 */ 460 public ClassLoader getUserClassLoader( ClassLoader parent ) { 461 if (classpaths.isEmpty()) 462 return 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("-disableXmlSecurity")) { 546 disableXmlSecurity = true; 547 return 1; 548 } 549 if (args[i].equals("-contentForWildcard")) { 550 contentForWildcard = true; 551 return 1; 552 } 553 if (args[i].equals("-XautoNameResolution")) { 554 automaticNameConflictResolution = true; 555 return 1; 556 } 557 if (args[i].equals("-b")) { 558 addFile(requireArgument("-b",args,++i),bindFiles,".xjb"); 559 return 2; 560 } 561 if (args[i].equals("-dtd")) { 562 schemaLanguage = Language.DTD; 563 return 1; 564 } 565 if (args[i].equals("-xmlschema")) { 566 schemaLanguage = Language.XMLSCHEMA; 567 return 1; 568 } 569 if (args[i].equals("-wsdl")) { 570 schemaLanguage = Language.WSDL; 571 return 1; 572 } 573 if (args[i].equals("-extension")) { 574 compatibilityMode = EXTENSION; 575 return 1; 576 } 577 if (args[i].equals("-target")) { 578 String token = requireArgument("-target",args,++i); 579 target = SpecVersion.parse(token); 580 if(target==null) 581 throw new BadCommandLineException(Messages.format(Messages.ILLEGAL_TARGET_VERSION,token)); 582 return 2; 583 } 584 if (args[i].equals("-httpproxyfile")) { 585 if (i == args.length - 1 || args[i + 1].startsWith("-")) { 586 throw new BadCommandLineException( 587 Messages.format(Messages.MISSING_PROXYFILE)); 588 } 589 590 File file = new File(args[++i]); 591 if(!file.exists()) { 592 throw new BadCommandLineException( 593 Messages.format(Messages.NO_SUCH_FILE,file)); 594 } 595 596 try { 597 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8")); 598 parseProxy(in.readLine()); 599 in.close(); 600 } catch (IOException e) { 601 throw new BadCommandLineException( 602 Messages.format(Messages.FAILED_TO_PARSE,file,e.getMessage()),e); 603 } 604 605 return 2; 606 } 607 if (args[i].equals("-httpproxy")) { 608 if (i == args.length - 1 || args[i + 1].startsWith("-")) { 609 throw new BadCommandLineException( 610 Messages.format(Messages.MISSING_PROXY)); 611 } 612 613 parseProxy(args[++i]); 614 return 2; 615 } 616 if (args[i].equals("-host")) { 617 proxyHost = requireArgument("-host",args,++i); 618 return 2; 619 } 620 if (args[i].equals("-port")) { 621 proxyPort = requireArgument("-port",args,++i); 622 return 2; 623 } 624 if( args[i].equals("-catalog") ) { 625 // use Sun's "XML Entity and URI Resolvers" by Norman Walsh 626 // to resolve external entities. 627 // http://www.sun.com/xml/developers/resolver/ 628 629 File catalogFile = new File(requireArgument("-catalog",args,++i)); 630 try { 631 addCatalog(catalogFile); 632 } catch (IOException e) { 633 throw new BadCommandLineException( 634 Messages.format(Messages.FAILED_TO_PARSE,catalogFile,e.getMessage()),e); 635 } 636 return 2; 637 } 638 if( args[i].equals("-Xtest-class-name-allocator") ) { 639 classNameAllocator = new ClassNameAllocator() { 640 public String assignClassName(String packageName, String className) { 641 System.out.printf("assignClassName(%s,%s)\n",packageName,className); 642 return className+"_Type"; 643 } 644 }; 645 return 1; 646 } 647 648 if (args[i].equals("-encoding")) { 649 encoding = requireArgument("-encoding", args, ++i); 650 try { 651 if (!Charset.isSupported(encoding)) { 652 throw new BadCommandLineException( 653 Messages.format(Messages.UNSUPPORTED_ENCODING, encoding)); 654 } 655 } catch (IllegalCharsetNameException icne) { 656 throw new BadCommandLineException( 657 Messages.format(Messages.UNSUPPORTED_ENCODING, encoding)); 658 } 659 return 2; 660 } 661 662 // see if this is one of the extensions 663 for( Plugin plugin : getAllPlugins() ) { 664 try { 665 if( ('-'+plugin.getOptionName()).equals(args[i]) ) { 666 activePlugins.add(plugin); 667 plugin.onActivated(this); 668 pluginURIs.addAll(plugin.getCustomizationURIs()); 669 670 // give the plugin a chance to parse arguments to this option. 671 // this is new in 2.1, and due to the backward compatibility reason, 672 // if plugin didn't understand it, we still return 1 to indicate 673 // that this option is consumed. 674 int r = plugin.parseArgument(this,args,i); 675 if(r!=0) 676 return r; 677 else 678 return 1; 679 } 680 681 int r = plugin.parseArgument(this,args,i); 682 if(r!=0) return r; 683 } catch (IOException e) { 684 throw new BadCommandLineException(e.getMessage(),e); 685 } 686 } 687 688 return 0; // unrecognized 689 } 690 691 private void parseProxy(String text) throws BadCommandLineException { 692 int i = text.lastIndexOf('@'); 693 int j = text.lastIndexOf(':'); 694 695 if (i > 0) { 696 proxyAuth = text.substring(0, i); 697 if (j > i) { 698 proxyHost = text.substring(i + 1, j); 699 proxyPort = text.substring(j + 1); 700 } else { 701 proxyHost = text.substring(i + 1); 702 proxyPort = "80"; 703 } 704 } else { 705 //no auth info 706 if (j < 0) { 707 //no port 708 proxyHost = text; 709 proxyPort = "80"; 710 } else { 711 proxyHost = text.substring(0, j); 712 proxyPort = text.substring(j + 1); 713 } 714 } 715 try { 716 Integer.valueOf(proxyPort); 717 } catch (NumberFormatException e) { 718 throw new BadCommandLineException(Messages.format(Messages.ILLEGAL_PROXY,text)); 719 } 720 } 721 722 /** 723 * Obtains an operand and reports an error if it's not there. 724 */ 725 public String requireArgument(String optionName, String[] args, int i) throws BadCommandLineException { 726 if (i == args.length || args[i].startsWith("-")) { 727 throw new BadCommandLineException( 728 Messages.format(Messages.MISSING_OPERAND,optionName)); 729 } 730 return args[i]; 731 } 732 733 /** 734 * Parses a token to a file (or a set of files) 735 * and add them as {@link InputSource} to the specified list. 736 * 737 * @param suffix 738 * If the given token is a directory name, we do a recusive search 739 * and find all files that have the given suffix. 740 */ 741 private void addFile(String name, List<InputSource> target, String suffix) throws BadCommandLineException { 742 Object src; 743 try { 744 src = Util.getFileOrURL(name); 745 } catch (IOException e) { 746 throw new BadCommandLineException( 747 Messages.format(Messages.NOT_A_FILE_NOR_URL,name)); 748 } 749 if(src instanceof URL) { 750 target.add(absolutize(new InputSource(Util.escapeSpace(((URL)src).toExternalForm())))); 751 } else { 752 File fsrc = (File)src; 753 if(fsrc.isDirectory()) { 754 addRecursive(fsrc,suffix,target); 755 } else { 756 target.add(absolutize(fileToInputSource(fsrc))); 757 } 758 } 759 } 760 761 /** 762 * Adds a new catalog file. 763 */ 764 public void addCatalog(File catalogFile) throws IOException { 765 if(entityResolver==null) { 766 final CatalogManager staticManager = CatalogManager.getStaticManager(); 767 // hack to force initialization so catalog manager system properties take effect 768 staticManager.getVerbosity(); 769 staticManager.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 (proxyAuth != null) { 815 DefaultAuthenticator.getAuthenticator().setProxyAuth(proxyAuth); 816 } 817 } 818 819 if (grammars.isEmpty()) 820 throw new BadCommandLineException( 821 Messages.format(Messages.MISSING_GRAMMAR)); 822 823 if( schemaLanguage==null ) 824 schemaLanguage = guessSchemaLanguage(); 825 826 // if(target==SpecVersion.V2_2 && !isExtensionMode()) 827 // throw new BadCommandLineException( 828 // "Currently 2.2 is still not finalized yet, so using it requires the -extension switch." + 829 // "NOTE THAT 2.2 SPEC MAY CHANGE BEFORE IT BECOMES FINAL."); 830 831 if(pluginLoadFailure!=null) 832 throw new BadCommandLineException( 833 Messages.format(Messages.PLUGIN_LOAD_FAILURE,pluginLoadFailure)); 834 } 835 836 /** 837 * Finds the <tt>META-INF/sun-jaxb.episode</tt> file to add as a binding customization. 838 */ 839 public void scanEpisodeFile(File jar) throws BadCommandLineException { 840 try { 841 URLClassLoader ucl = new URLClassLoader(new URL[]{jar.toURL()}); 842 Enumeration<URL> resources = ucl.findResources("META-INF/sun-jaxb.episode"); 843 while (resources.hasMoreElements()) { 844 URL url = resources.nextElement(); 845 addBindFile(new InputSource(url.toExternalForm())); 846 } 847 } catch (IOException e) { 848 throw new BadCommandLineException( 849 Messages.format(Messages.FAILED_TO_LOAD,jar,e.getMessage()), e); 850 } 851 } 852 853 854 /** 855 * Guesses the schema language. 856 */ 857 public Language guessSchemaLanguage() { 858 859 // otherwise, use the file extension. 860 // not a good solution, but very easy. 861 if ((grammars != null) && (grammars.size() > 0)) { 862 String name = grammars.get(0).getSystemId().toLowerCase(); 863 864 if (name.endsWith(".dtd")) 865 return Language.DTD; 866 if (name.endsWith(".wsdl")) 867 return Language.WSDL; 868 } 869 870 // by default, assume XML Schema 871 return Language.XMLSCHEMA; 872 } 873 874 /** 875 * Creates a configured CodeWriter that produces files into the specified directory. 876 */ 877 public CodeWriter createCodeWriter() throws IOException { 878 return createCodeWriter(new FileCodeWriter( targetDir, readOnly, encoding )); 879 } 880 881 /** 882 * Creates a configured CodeWriter that produces files into the specified directory. 883 */ 884 public CodeWriter createCodeWriter( CodeWriter core ) { 885 if(noFileHeader) 886 return core; 887 888 return new PrologCodeWriter( core,getPrologComment() ); 889 } 890 891 /** 892 * Gets the string suitable to be used as the prolog comment baked into artifacts. 893 * This is the string like "This file was generated by the JAXB RI on YYYY/mm/dd..." 894 */ 895 public String getPrologComment() { 896 // generate format syntax: <date> 'at' <time> 897 String format = 898 Messages.format(Messages.DATE_FORMAT) 899 + " '" 900 + Messages.format(Messages.AT) 901 + "' " 902 + Messages.format(Messages.TIME_FORMAT); 903 SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.ENGLISH); 904 905 return Messages.format( 906 Messages.FILE_PROLOG_COMMENT, 907 dateFormat.format(new Date())); 908 } 909 910 /** 911 * If a plugin failed to load, report. 912 */ 913 private String pluginLoadFailure; 914 915 /** 916 * Looks for all "META-INF/services/[className]" files and 917 * create one instance for each class name found inside this file. 918 */ 919 private <T> List<T> findServices( Class<T> clazz) { 920 final List<T> result = new ArrayList<T>(); 921 final boolean debug = getDebugPropertyValue(); 922 try { 923 // TCCL allows user plugins to be loaded even if xjc is in jdk 924 // We have to use our SecureLoader to obtain it because we are trying to avoid SecurityException 925 final ClassLoader tccl = SecureLoader.getContextClassLoader(); 926 final ServiceLoader<T> sl = ServiceLoader.load(clazz, tccl); 927 for (T t : sl) 928 result.add(t); 929 } catch( Throwable e ) { 930 // ignore any error 931 StringWriter w = new StringWriter(); 932 e.printStackTrace(new PrintWriter(w)); 933 pluginLoadFailure = w.toString(); 934 if(debug) 935 System.out.println(pluginLoadFailure); 936 } 937 return result; 938 } 939 940 private static boolean getDebugPropertyValue() { 941 final String debugPropertyName = Options.class.getName() + ".findServices"; 942 if (System.getSecurityManager() != null) { 943 return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 944 @Override 945 public Boolean run() { 946 return Boolean.getBoolean(debugPropertyName); 947 } 948 }); 949 } else { 950 return Boolean.getBoolean(debugPropertyName); 951 } 952 } 953 954 // this is a convenient place to expose the build version to xjc plugins 955 public static String getBuildID() { 956 return Messages.format(Messages.BUILD_ID); 957 } 958 959 public static String normalizeSystemId(String systemId) { 960 try { 961 systemId = new URI(systemId).normalize().toString(); 962 } catch (URISyntaxException e) { 963 // leave the system ID untouched. In my experience URI is often too strict 964 } 965 return systemId; 966 } 967 968 }