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