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.File; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.io.OutputStreamWriter; 33 import java.io.PrintStream; 34 import java.util.Iterator; 35 36 import com.sun.codemodel.internal.CodeWriter; 37 import com.sun.codemodel.internal.JCodeModel; 38 import com.sun.codemodel.internal.writer.ZipCodeWriter; 39 import com.sun.istack.internal.NotNull; 40 import com.sun.istack.internal.Nullable; 41 import com.sun.istack.internal.tools.DefaultAuthenticator; 42 import com.sun.tools.internal.xjc.generator.bean.BeanGenerator; 43 import com.sun.tools.internal.xjc.model.Model; 44 import com.sun.tools.internal.xjc.outline.Outline; 45 import com.sun.tools.internal.xjc.reader.gbind.Expression; 46 import com.sun.tools.internal.xjc.reader.gbind.Graph; 47 import com.sun.tools.internal.xjc.reader.internalizer.DOMForest; 48 import com.sun.tools.internal.xjc.reader.xmlschema.ExpressionBuilder; 49 import com.sun.tools.internal.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic; 50 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter; 51 import com.sun.tools.internal.xjc.util.NullStream; 52 import com.sun.tools.internal.xjc.util.Util; 53 import com.sun.tools.internal.xjc.writer.SignatureWriter; 54 import com.sun.xml.internal.xsom.XSComplexType; 55 import com.sun.xml.internal.xsom.XSParticle; 56 import com.sun.xml.internal.xsom.XSSchemaSet; 57 58 import org.xml.sax.SAXException; 59 import org.xml.sax.SAXParseException; 60 61 62 /** 63 * Command Line Interface of XJC. 64 */ 65 public class Driver { 66 67 public static void main(final String[] args) throws Exception { 68 // use the platform default proxy if available. 69 // see sun.net.spi.DefaultProxySelector for details. 70 try { 71 System.setProperty("java.net.useSystemProxies","true"); 72 } catch (SecurityException e) { 73 // failing to set this property isn't fatal 74 } 75 76 if( Util.getSystemProperty(Driver.class,"noThreadSwap")!=null ) 77 _main(args); // for the ease of debugging 78 79 // run all the work in another thread so that the -Xss option 80 // will take effect when compiling a large schema. See 81 // http://developer.java.sun.com/developer/bugParade/bugs/4362291.html 82 final Throwable[] ex = new Throwable[1]; 83 84 Thread th = new Thread() { 85 @Override 86 public void run() { 87 try { 88 _main(args); 89 } catch( Throwable e ) { 90 ex[0]=e; 91 } 92 } 93 }; 94 th.start(); 95 th.join(); 96 97 if(ex[0]!=null) { 98 // re-throw 99 if( ex[0] instanceof Exception ) 100 throw (Exception)ex[0]; 101 else 102 throw (Error)ex[0]; 103 } 104 } 105 106 private static void _main( String[] args ) throws Exception { 107 try { 108 System.exit(run( args, System.out, System.out )); 109 } catch (BadCommandLineException e) { 110 // there was an error in the command line. 111 // print usage and abort. 112 if(e.getMessage()!=null) { 113 System.out.println(e.getMessage()); 114 System.out.println(); 115 } 116 117 usage(e.getOptions(),false); 118 System.exit(-1); 119 } 120 } 121 122 123 124 /** 125 * Performs schema compilation and prints the status/error into the 126 * specified PrintStream. 127 * 128 * <p> 129 * This method could be used to trigger XJC from other tools, 130 * such as Ant or IDE. 131 * 132 * @param args 133 * specified command line parameters. If there is an error 134 * in the parameters, {@link BadCommandLineException} will 135 * be thrown. 136 * @param status 137 * Status report of the compilation will be sent to this object. 138 * Useful to update users so that they will know something is happening. 139 * Only ignorable messages should be sent to this stream. 140 * 141 * This parameter can be null to suppress messages. 142 * 143 * @param out 144 * Various non-ignorable output (error messages, etc) 145 * will go to this stream. 146 * 147 * @return 148 * If the compiler runs successfully, this method returns 0. 149 * All non-zero values indicate an error. The error message 150 * will be sent to the specified PrintStream. 151 */ 152 public static int run(String[] args, final PrintStream status, final PrintStream out) 153 throws Exception { 154 155 class Listener extends XJCListener { 156 ConsoleErrorReporter cer = new ConsoleErrorReporter(out==null?new PrintStream(new NullStream()):out); 157 158 @Override 159 public void generatedFile(String fileName, int count, int total) { 160 message(fileName); 161 } 162 @Override 163 public void message(String msg) { 164 if(status!=null) 165 status.println(msg); 166 } 167 168 public void error(SAXParseException exception) { 169 cer.error(exception); 170 } 171 172 public void fatalError(SAXParseException exception) { 173 cer.fatalError(exception); 174 } 175 176 public void warning(SAXParseException exception) { 177 cer.warning(exception); 178 } 179 180 public void info(SAXParseException exception) { 181 cer.info(exception); 182 } 183 } 184 185 return run(args,new Listener()); 186 } 187 188 /** 189 * Performs schema compilation and prints the status/error into the 190 * specified PrintStream. 191 * 192 * <p> 193 * This method could be used to trigger XJC from other tools, 194 * such as Ant or IDE. 195 * 196 * @param args 197 * specified command line parameters. If there is an error 198 * in the parameters, {@link BadCommandLineException} will 199 * be thrown. 200 * @param listener 201 * Receives messages from XJC reporting progress/errors. 202 * 203 * @return 204 * If the compiler runs successfully, this method returns 0. 205 * All non-zero values indicate an error. The error message 206 * will be sent to the specified PrintStream. 207 */ 208 public static int run(String[] args, @NotNull final XJCListener listener) throws BadCommandLineException { 209 210 // recognize those special options before we start parsing options. 211 for (String arg : args) { 212 if (arg.equals("-version")) { 213 listener.message(Messages.format(Messages.VERSION)); 214 return -1; 215 } 216 if (arg.equals("-fullversion")) { 217 listener.message(Messages.format(Messages.FULLVERSION)); 218 return -1; 219 } 220 } 221 222 final OptionsEx opt = new OptionsEx(); 223 opt.setSchemaLanguage(Language.XMLSCHEMA); // disable auto-guessing 224 try { 225 opt.parseArguments(args); 226 } catch (WeAreDone e) { 227 if (opt.proxyAuth != null) { 228 DefaultAuthenticator.reset(); 229 } 230 return -1; 231 } catch(BadCommandLineException e) { 232 if (opt.proxyAuth != null) { 233 DefaultAuthenticator.reset(); 234 } 235 e.initOptions(opt); 236 throw e; 237 } 238 239 // display a warning if the user specified the default package 240 // this should work, but is generally a bad idea 241 if(opt.defaultPackage != null && opt.defaultPackage.length()==0) { 242 listener.message(Messages.format(Messages.WARNING_MSG, Messages.format(Messages.DEFAULT_PACKAGE_WARNING))); 243 } 244 245 246 // set up the context class loader so that the user-specified classes 247 // can be loaded from there 248 final ClassLoader contextClassLoader = SecureLoader.getContextClassLoader(); 249 SecureLoader.setContextClassLoader(opt.getUserClassLoader(contextClassLoader)); 250 251 // parse a grammar file 252 //----------------------------------------- 253 try { 254 if( !opt.quiet ) { 255 listener.message(Messages.format(Messages.PARSING_SCHEMA)); 256 } 257 258 final boolean[] hadWarning = new boolean[1]; 259 260 ErrorReceiver receiver = new ErrorReceiverFilter(listener) { 261 @Override 262 public void info(SAXParseException exception) { 263 if(opt.verbose) 264 super.info(exception); 265 } 266 @Override 267 public void warning(SAXParseException exception) { 268 hadWarning[0] = true; 269 if(!opt.quiet) 270 super.warning(exception); 271 } 272 @Override 273 public void pollAbort() throws AbortException { 274 if(listener.isCanceled()) 275 throw new AbortException(); 276 } 277 }; 278 279 if( opt.mode==Mode.FOREST ) { 280 // dump DOM forest and quit 281 ModelLoader loader = new ModelLoader( opt, new JCodeModel(), receiver ); 282 try { 283 DOMForest forest = loader.buildDOMForest(new XMLSchemaInternalizationLogic()); 284 forest.dump(System.out); 285 return 0; 286 } catch (SAXException e) { 287 // the error should have already been reported 288 } catch (IOException e) { 289 receiver.error(e); 290 } 291 292 return -1; 293 } 294 295 if( opt.mode==Mode.GBIND ) { 296 try { 297 XSSchemaSet xss = new ModelLoader(opt, new JCodeModel(), receiver).loadXMLSchema(); 298 Iterator<XSComplexType> it = xss.iterateComplexTypes(); 299 while (it.hasNext()) { 300 XSComplexType ct = it.next(); 301 XSParticle p = ct.getContentType().asParticle(); 302 if(p==null) continue; 303 304 Expression tree = ExpressionBuilder.createTree(p); 305 System.out.println("Graph for "+ct.getName()); 306 System.out.println(tree.toString()); 307 Graph g = new Graph(tree); 308 System.out.println(g.toString()); 309 System.out.println(); 310 } 311 return 0; 312 } catch (SAXException e) { 313 // the error should have already been reported 314 } 315 return -1; 316 } 317 318 Model model = ModelLoader.load( opt, new JCodeModel(), receiver ); 319 320 if (model == null) { 321 listener.message(Messages.format(Messages.PARSE_FAILED)); 322 return -1; 323 } 324 325 if( !opt.quiet ) { 326 listener.message(Messages.format(Messages.COMPILING_SCHEMA)); 327 } 328 329 switch (opt.mode) { 330 case SIGNATURE : 331 try { 332 SignatureWriter.write( 333 BeanGenerator.generate(model,receiver), 334 new OutputStreamWriter(System.out)); 335 return 0; 336 } catch (IOException e) { 337 receiver.error(e); 338 return -1; 339 } 340 341 case CODE : 342 case DRYRUN : 343 case ZIP : 344 { 345 // generate actual code 346 receiver.debug("generating code"); 347 {// don't want to hold outline in memory for too long. 348 Outline outline = model.generateCode(opt,receiver); 349 if(outline==null) { 350 listener.message( 351 Messages.format(Messages.FAILED_TO_GENERATE_CODE)); 352 return -1; 353 } 354 355 listener.compiled(outline); 356 } 357 358 if( opt.mode == Mode.DRYRUN ) 359 break; // enough 360 361 // then print them out 362 try { 363 CodeWriter cw; 364 if( opt.mode==Mode.ZIP ) { 365 OutputStream os; 366 if(opt.targetDir.getPath().equals(".")) 367 os = System.out; 368 else 369 os = new FileOutputStream(opt.targetDir); 370 371 cw = opt.createCodeWriter(new ZipCodeWriter(os)); 372 } else 373 cw = opt.createCodeWriter(); 374 375 if( !opt.quiet ) { 376 cw = new ProgressCodeWriter(cw,listener, model.codeModel.countArtifacts()); 377 } 378 model.codeModel.build(cw); 379 } catch (IOException e) { 380 receiver.error(e); 381 return -1; 382 } 383 384 break; 385 } 386 default : 387 assert false; 388 } 389 390 if(opt.debugMode) { 391 try { 392 new FileOutputStream(new File(opt.targetDir,hadWarning[0]?"hadWarning":"noWarning")).close(); 393 } catch (IOException e) { 394 receiver.error(e); 395 return -1; 396 } 397 } 398 399 return 0; 400 } catch( StackOverflowError e ) { 401 if(opt.verbose) 402 // in the debug mode, propagate the error so that 403 // the full stack trace will be dumped to the screen. 404 throw e; 405 else { 406 // otherwise just print a suggested workaround and 407 // quit without filling the user's screen 408 listener.message(Messages.format(Messages.STACK_OVERFLOW)); 409 return -1; 410 } 411 } finally { 412 if (opt.proxyAuth != null) { 413 DefaultAuthenticator.reset(); 414 } 415 } 416 } 417 418 public static String getBuildID() { 419 return Messages.format(Messages.BUILD_ID); 420 } 421 422 423 /** 424 * Operation mode. 425 */ 426 private static enum Mode { 427 // normal mode. compile the code 428 CODE, 429 430 // dump the signature of the generated code 431 SIGNATURE, 432 433 // dump DOMForest 434 FOREST, 435 436 // same as CODE but don't produce any Java source code 437 DRYRUN, 438 439 // same as CODE but pack all the outputs into a zip and dumps to stdout 440 ZIP, 441 442 // testing a new binding mode 443 GBIND 444 } 445 446 447 /** 448 * Command-line arguments processor. 449 * 450 * <p> 451 * This class contains options that only make sense 452 * for the command line interface. 453 */ 454 static class OptionsEx extends Options 455 { 456 /** Operation mode. */ 457 protected Mode mode = Mode.CODE; 458 459 /** A switch that determines the behavior in the BGM mode. */ 460 public boolean noNS = false; 461 462 /** Parse XJC-specific options. */ 463 @Override 464 public int parseArgument(String[] args, int i) throws BadCommandLineException { 465 if (args[i].equals("-noNS")) { 466 noNS = true; 467 return 1; 468 } 469 if (args[i].equals("-mode")) { 470 i++; 471 if (i == args.length) 472 throw new BadCommandLineException( 473 Messages.format(Messages.MISSING_MODE_OPERAND)); 474 475 String mstr = args[i].toLowerCase(); 476 477 for( Mode m : Mode.values() ) { 478 if(m.name().toLowerCase().startsWith(mstr) && mstr.length()>2) { 479 mode = m; 480 return 2; 481 } 482 } 483 484 throw new BadCommandLineException( 485 Messages.format(Messages.UNRECOGNIZED_MODE, args[i])); 486 } 487 if (args[i].equals("-help")) { 488 usage(this,false); 489 throw new WeAreDone(); 490 } 491 if (args[i].equals("-private")) { 492 usage(this,true); 493 throw new WeAreDone(); 494 } 495 496 return super.parseArgument(args, i); 497 } 498 } 499 500 /** 501 * Used to signal that we've finished processing. 502 */ 503 private static final class WeAreDone extends BadCommandLineException {} 504 505 506 /** 507 * Prints the usage screen and exits the process. 508 * 509 * @param opts 510 * If the parsing of options have started, set a partly populated 511 * {@link Options} object. 512 */ 513 public static void usage( @Nullable Options opts, boolean privateUsage ) { 514 System.out.println(Messages.format(Messages.DRIVER_PUBLIC_USAGE)); 515 if (privateUsage) { 516 System.out.println(Messages.format(Messages.DRIVER_PRIVATE_USAGE)); 517 } 518 519 if( opts!=null && !opts.getAllPlugins().isEmpty()) { 520 System.out.println(Messages.format(Messages.ADDON_USAGE)); 521 for (Plugin p : opts.getAllPlugins()) { 522 System.out.println(p.getUsage()); 523 } 524 } 525 } 526 }