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