001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.tools.xjc;
037:
038: import java.io.FileOutputStream;
039: import java.io.IOException;
040: import java.io.OutputStream;
041: import java.io.OutputStreamWriter;
042: import java.io.PrintStream;
043: import java.util.Iterator;
044:
045: import com.sun.codemodel.CodeWriter;
046: import com.sun.codemodel.JCodeModel;
047: import com.sun.codemodel.writer.ZipCodeWriter;
048: import com.sun.istack.NotNull;
049: import com.sun.istack.Nullable;
050: import com.sun.tools.xjc.generator.bean.BeanGenerator;
051: import com.sun.tools.xjc.model.Model;
052: import com.sun.tools.xjc.outline.Outline;
053: import com.sun.tools.xjc.reader.gbind.Expression;
054: import com.sun.tools.xjc.reader.gbind.Graph;
055: import com.sun.tools.xjc.reader.internalizer.DOMForest;
056: import com.sun.tools.xjc.reader.xmlschema.ExpressionBuilder;
057: import com.sun.tools.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic;
058: import com.sun.tools.xjc.util.ErrorReceiverFilter;
059: import com.sun.tools.xjc.util.NullStream;
060: import com.sun.tools.xjc.util.Util;
061: import com.sun.tools.xjc.writer.SignatureWriter;
062: import com.sun.xml.xsom.XSComplexType;
063: import com.sun.xml.xsom.XSParticle;
064: import com.sun.xml.xsom.XSSchemaSet;
065:
066: import org.xml.sax.SAXException;
067: import org.xml.sax.SAXParseException;
068:
069: /**
070: * CUI of XJC.
071: */
072: public class Driver {
073:
074: public static void main(final String[] args) throws Exception {
075: // use the platform default proxy if available.
076: // see sun.net.spi.DefaultProxySelector for details.
077: try {
078: System.setProperty("java.net.useSystemProxies", "true");
079: } catch (SecurityException e) {
080: // failing to set this property isn't fatal
081: }
082:
083: if (Util.getSystemProperty(Driver.class, "noThreadSwap") != null)
084: _main(args); // for the ease of debugging
085:
086: // run all the work in another thread so that the -Xss option
087: // will take effect when compiling a large schema. See
088: // http://developer.java.sun.com/developer/bugParade/bugs/4362291.html
089: final Throwable[] ex = new Throwable[1];
090:
091: Thread th = new Thread() {
092: public void run() {
093: try {
094: _main(args);
095: } catch (Throwable e) {
096: ex[0] = e;
097: }
098: }
099: };
100: th.start();
101: th.join();
102:
103: if (ex[0] != null) {
104: // re-throw
105: if (ex[0] instanceof Exception)
106: throw (Exception) ex[0];
107: else
108: throw (Error) ex[0];
109: }
110: }
111:
112: private static void _main(String[] args) throws Exception {
113: try {
114: System.exit(run(args, System.err, System.out));
115: } catch (BadCommandLineException e) {
116: // there was an error in the command line.
117: // print usage and abort.
118: if (e.getMessage() != null) {
119: System.out.println(e.getMessage());
120: System.out.println();
121: }
122:
123: usage(e.getOptions(), false);
124: System.exit(-1);
125: }
126: }
127:
128: /**
129: * Performs schema compilation and prints the status/error into the
130: * specified PrintStream.
131: *
132: * <p>
133: * This method could be used to trigger XJC from other tools,
134: * such as Ant or IDE.
135: *
136: * @param args
137: * specified command line parameters. If there is an error
138: * in the parameters, {@link BadCommandLineException} will
139: * be thrown.
140: * @param status
141: * Status report of the compilation will be sent to this object.
142: * Useful to update users so that they will know something is happening.
143: * Only ignorable messages should be sent to this stream.
144: *
145: * This parameter can be null to suppress messages.
146: *
147: * @param out
148: * Various non-ignorable output (error messages, etc)
149: * will go to this stream.
150: *
151: * @return
152: * If the compiler runs successfully, this method returns 0.
153: * All non-zero values indicate an error. The error message
154: * will be sent to the specified PrintStream.
155: */
156: public static int run(String[] args, final PrintStream status,
157: final PrintStream out) throws Exception {
158:
159: class Listener extends XJCListener {
160: ConsoleErrorReporter cer = new ConsoleErrorReporter(
161: out == null ? new PrintStream(new NullStream())
162: : out);
163:
164: public void generatedFile(String fileName, int count,
165: int total) {
166: message(fileName);
167: }
168:
169: public void message(String msg) {
170: if (status != null)
171: status.println(msg);
172: }
173:
174: public void error(SAXParseException exception) {
175: cer.error(exception);
176: }
177:
178: public void fatalError(SAXParseException exception) {
179: cer.fatalError(exception);
180: }
181:
182: public void warning(SAXParseException exception) {
183: cer.warning(exception);
184: }
185:
186: public void info(SAXParseException exception) {
187: cer.info(exception);
188: }
189: }
190:
191: return run(args, new Listener());
192: }
193:
194: /**
195: * Performs schema compilation and prints the status/error into the
196: * specified PrintStream.
197: *
198: * <p>
199: * This method could be used to trigger XJC from other tools,
200: * such as Ant or IDE.
201: *
202: * @param args
203: * specified command line parameters. If there is an error
204: * in the parameters, {@link BadCommandLineException} will
205: * be thrown.
206: * @param listener
207: * Receives messages from XJC reporting progress/errors.
208: *
209: * @return
210: * If the compiler runs successfully, this method returns 0.
211: * All non-zero values indicate an error. The error message
212: * will be sent to the specified PrintStream.
213: */
214: public static int run(String[] args, @NotNull
215: final XJCListener listener) throws BadCommandLineException {
216:
217: // recognize those special options before we start parsing options.
218: for (String arg : args) {
219: if (arg.equals("-version")) {
220: listener.message(Messages.format(Messages.VERSION));
221: return -1;
222: }
223: }
224:
225: final OptionsEx opt = new OptionsEx();
226: opt.setSchemaLanguage(Language.XMLSCHEMA); // disable auto-guessing
227: try {
228: opt.parseArguments(args);
229: } catch (WeAreDone _) {
230: return -1;
231: } catch (BadCommandLineException e) {
232: e.initOptions(opt);
233: throw e;
234: }
235:
236: // display a warning if the user specified the default package
237: // this should work, but is generally a bad idea
238: if (opt.defaultPackage != null
239: && opt.defaultPackage.length() == 0) {
240: listener.message(Messages.format(Messages.WARNING_MSG,
241: Messages.format(Messages.DEFAULT_PACKAGE_WARNING)));
242: }
243:
244: // set up the context class loader so that the user-specified classes
245: // can be loaded from there
246: final ClassLoader contextClassLoader = Thread.currentThread()
247: .getContextClassLoader();
248: Thread.currentThread().setContextClassLoader(
249: opt.getUserClassLoader(contextClassLoader));
250:
251: // parse a grammar file
252: //-----------------------------------------
253: try {
254: if (!opt.quiet) {
255: listener.message(Messages
256: .format(Messages.PARSING_SCHEMA));
257: }
258:
259: ErrorReceiver receiver = new ErrorReceiverFilter(listener) {
260: public void info(SAXParseException exception) {
261: if (opt.verbose)
262: super .info(exception);
263: }
264:
265: public void warning(SAXParseException exception) {
266: if (!opt.quiet)
267: super .warning(exception);
268: }
269:
270: @Override
271: public void pollAbort() throws AbortException {
272: if (listener.isCanceled())
273: throw new AbortException();
274: }
275: };
276:
277: if (opt.mode == Mode.FOREST) {
278: // dump DOM forest and quit
279: ModelLoader loader = new ModelLoader(opt,
280: new JCodeModel(), receiver);
281: try {
282: DOMForest forest = loader
283: .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,
298: new JCodeModel(), receiver).loadXMLSchema();
299: Iterator<XSComplexType> it = xss
300: .iterateComplexTypes();
301: while (it.hasNext()) {
302: XSComplexType ct = it.next();
303: XSParticle p = ct.getContentType().asParticle();
304: if (p == null)
305: continue;
306:
307: Expression tree = ExpressionBuilder
308: .createTree(p);
309: System.out.println("Graph for " + ct.getName());
310: System.out.println(tree.toString());
311: Graph g = new Graph(tree);
312: System.out.println(g.toString());
313: System.out.println();
314: }
315: return 0;
316: } catch (SAXException e) {
317: // the error should have already been reported
318: }
319: return -1;
320: }
321:
322: Model model = ModelLoader.load(opt, new JCodeModel(),
323: receiver);
324:
325: if (model == null) {
326: listener
327: .message(Messages.format(Messages.PARSE_FAILED));
328: return -1;
329: }
330:
331: if (!opt.quiet) {
332: listener.message(Messages
333: .format(Messages.COMPILING_SCHEMA));
334: }
335:
336: switch (opt.mode) {
337: case SIGNATURE:
338: try {
339: SignatureWriter.write(BeanGenerator.generate(model,
340: receiver), new OutputStreamWriter(
341: System.out));
342: return 0;
343: } catch (IOException e) {
344: receiver.error(e);
345: return -1;
346: }
347:
348: case CODE:
349: case DRYRUN:
350: case ZIP: {
351: // generate actual code
352: receiver.debug("generating code");
353: {// don't want to hold outline in memory for too long.
354: Outline outline = model.generateCode(opt, receiver);
355: if (outline == null) {
356: listener
357: .message(Messages
358: .format(Messages.FAILED_TO_GENERATE_CODE));
359: return -1;
360: }
361:
362: listener.compiled(outline);
363: }
364:
365: if (opt.mode == Mode.DRYRUN)
366: break; // enough
367:
368: // then print them out
369: try {
370: CodeWriter cw;
371: if (opt.mode == Mode.ZIP) {
372: OutputStream os;
373: if (opt.targetDir.getPath().equals("."))
374: os = System.out;
375: else
376: os = new FileOutputStream(opt.targetDir);
377:
378: cw = opt
379: .createCodeWriter(new ZipCodeWriter(os));
380: } else
381: cw = opt.createCodeWriter();
382:
383: if (!opt.quiet) {
384: cw = new ProgressCodeWriter(cw, listener,
385: model.codeModel.countArtifacts());
386: }
387: model.codeModel.build(cw);
388: } catch (IOException e) {
389: receiver.error(e);
390: return -1;
391: }
392:
393: break;
394: }
395: default:
396: assert false;
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
409: .format(Messages.STACK_OVERFLOW));
410: return -1;
411: }
412: }
413: }
414:
415: public static String getBuildID() {
416: return Messages.format(Messages.BUILD_ID);
417: }
418:
419: /**
420: * Operation mode.
421: */
422: private static enum Mode {
423: // normal mode. compile the code
424: CODE,
425:
426: // dump the signature of the generated code
427: SIGNATURE,
428:
429: // dump DOMForest
430: FOREST,
431:
432: // same as CODE but don't produce any Java source code
433: DRYRUN,
434:
435: // same as CODE but pack all the outputs into a zip and dumps to stdout
436: ZIP,
437:
438: // testing a new binding mode
439: GBIND
440: }
441:
442: /**
443: * Command-line arguments processor.
444: *
445: * <p>
446: * This class contains options that only make sense
447: * for the command line interface.
448: */
449: static class OptionsEx extends Options {
450: /** Operation mode. */
451: protected Mode mode = Mode.CODE;
452:
453: /** A switch that determines the behavior in the BGM mode. */
454: public boolean noNS = false;
455:
456: /** Parse XJC-specific options. */
457: public int parseArgument(String[] args, int i)
458: throws BadCommandLineException {
459: if (args[i].equals("-noNS")) {
460: noNS = true;
461: return 1;
462: }
463: if (args[i].equals("-mode")) {
464: i++;
465: if (i == args.length)
466: throw new BadCommandLineException(Messages
467: .format(Messages.MISSING_MODE_OPERAND));
468:
469: String mstr = args[i].toLowerCase();
470:
471: for (Mode m : Mode.values()) {
472: if (m.name().toLowerCase().startsWith(mstr)
473: && mstr.length() > 2) {
474: mode = m;
475: return 2;
476: }
477: }
478:
479: throw new BadCommandLineException(Messages.format(
480: Messages.UNRECOGNIZED_MODE, args[i]));
481: }
482: if (args[i].equals("-help")) {
483: usage(this , false);
484: throw new WeAreDone();
485: }
486: if (args[i].equals("-private")) {
487: usage(this , true);
488: throw new WeAreDone();
489: }
490:
491: return super .parseArgument(args, i);
492: }
493: }
494:
495: /**
496: * Used to signal that we've finished processing.
497: */
498: private static final class WeAreDone extends
499: BadCommandLineException {
500: }
501:
502: /**
503: * Prints the usage screen and exits the process.
504: *
505: * @param opts
506: * If the parsing of options have started, set a partly populated
507: * {@link Options} object.
508: */
509: public static void usage(@Nullable
510: Options opts, boolean privateUsage) {
511: if (privateUsage) {
512: System.out.println(Messages
513: .format(Messages.DRIVER_PRIVATE_USAGE));
514: } else {
515: System.out.println(Messages
516: .format(Messages.DRIVER_PUBLIC_USAGE));
517: }
518:
519: if (opts != null && opts.getAllPlugins().size() != 0) {
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: }
|