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