001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.xjc.api.impl.s2j;
027:
028: import java.net.MalformedURLException;
029: import java.net.URI;
030: import java.net.URISyntaxException;
031: import java.net.URL;
032:
033: import javax.xml.XMLConstants;
034: import javax.xml.stream.XMLStreamException;
035: import javax.xml.stream.XMLStreamReader;
036: import javax.xml.validation.SchemaFactory;
037:
038: import com.sun.codemodel.internal.JCodeModel;
039: import com.sun.istack.internal.NotNull;
040: import com.sun.istack.internal.SAXParseException2;
041: import com.sun.tools.internal.xjc.ErrorReceiver;
042: import com.sun.tools.internal.xjc.ModelLoader;
043: import com.sun.tools.internal.xjc.Options;
044: import com.sun.tools.internal.xjc.api.ClassNameAllocator;
045: import com.sun.tools.internal.xjc.api.ErrorListener;
046: import com.sun.tools.internal.xjc.api.SchemaCompiler;
047: import com.sun.tools.internal.xjc.model.Model;
048: import com.sun.tools.internal.xjc.outline.Outline;
049: import com.sun.tools.internal.xjc.reader.internalizer.DOMForest;
050: import com.sun.tools.internal.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic;
051: import com.sun.xml.internal.bind.unmarshaller.DOMScanner;
052: import com.sun.xml.internal.xsom.XSSchemaSet;
053:
054: import org.w3c.dom.Element;
055: import org.xml.sax.ContentHandler;
056: import org.xml.sax.EntityResolver;
057: import org.xml.sax.InputSource;
058: import org.xml.sax.SAXException;
059: import org.xml.sax.SAXParseException;
060: import org.xml.sax.helpers.LocatorImpl;
061:
062: /**
063: * {@link SchemaCompiler} implementation.
064: *
065: * This class builds a {@link DOMForest} until the {@link #bind()} method,
066: * then this method does the rest of the hard work.
067: *
068: * @see ModelLoader
069: *
070: * @author
071: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
072: */
073: public final class SchemaCompilerImpl extends ErrorReceiver implements
074: SchemaCompiler {
075:
076: /**
077: * User-specified error receiver.
078: * This field can be null, in which case errors need to be discarded.
079: */
080: private ErrorListener errorListener;
081:
082: protected final Options opts = new Options();
083:
084: protected final DOMForest forest;
085:
086: /**
087: * Set to true once an error is found.
088: */
089: private boolean hadError;
090:
091: public SchemaCompilerImpl() {
092: forest = new DOMForest(new XMLSchemaInternalizationLogic());
093: opts.compatibilityMode = Options.EXTENSION;
094: forest.setErrorHandler(this );
095:
096: if (System.getProperty("xjc-api.test") != null) {
097: opts.debugMode = true;
098: opts.verbose = true;
099: }
100: }
101:
102: @NotNull
103: public Options getOptions() {
104: return opts;
105: }
106:
107: public ContentHandler getParserHandler(String systemId) {
108: return forest.getParserHandler(systemId, true);
109: }
110:
111: public void parseSchema(String systemId, Element element) {
112: checkAbsoluteness(systemId);
113: try {
114: DOMScanner scanner = new DOMScanner();
115:
116: // use a locator that sets the system ID correctly
117: // so that we can resolve relative URLs in most of the case.
118: // it still doesn't handle xml:base and XInclude and all those things
119: // correctly. There's just no way to make all those things work with DOM!
120: LocatorImpl loc = new LocatorImpl();
121: loc.setSystemId(systemId);
122: scanner.setLocator(loc);
123:
124: scanner.setContentHandler(getParserHandler(systemId));
125: scanner.scan(element);
126: } catch (SAXException e) {
127: // since parsing DOM shouldn't cause a SAX exception
128: // and our handler will never throw it, it's not clear
129: // if this will ever happen.
130: fatalError(new SAXParseException2(e.getMessage(), null,
131: systemId, -1, -1, e));
132: }
133: }
134:
135: public void parseSchema(InputSource source) {
136: checkAbsoluteness(source.getSystemId());
137: try {
138: forest.parse(source, true);
139: } catch (SAXException e) {
140: // parsers are required to report an error to ErrorHandler,
141: // so we should never see this error.
142: e.printStackTrace();
143: }
144: }
145:
146: public void parseSchema(String systemId, XMLStreamReader reader)
147: throws XMLStreamException {
148: checkAbsoluteness(systemId);
149: forest.parse(systemId, reader, true);
150: }
151:
152: /**
153: * Checks if the system ID is absolute.
154: */
155: private void checkAbsoluteness(String systemId) {
156: // we need to be able to handle system IDs like "urn:foo", which java.net.URL can't process,
157: // but OTOH we also need to be able to process system IDs like "file://a b c/def.xsd",
158: // which java.net.URI can't process. So for now, let's fail only if both of them fail.
159: // eventually we need a proper URI class that works for us.
160: try {
161: new URL(systemId);
162: } catch (MalformedURLException _) {
163: try {
164: new URI(systemId);
165: } catch (URISyntaxException e) {
166: throw new IllegalArgumentException("system ID '"
167: + systemId + "' isn't absolute", e);
168: }
169: }
170: }
171:
172: public void setEntityResolver(EntityResolver entityResolver) {
173: forest.setEntityResolver(entityResolver);
174: opts.entityResolver = entityResolver;
175: }
176:
177: public void setDefaultPackageName(String packageName) {
178: opts.defaultPackage2 = packageName;
179: }
180:
181: public void forcePackageName(String packageName) {
182: opts.defaultPackage = packageName;
183: }
184:
185: public void setClassNameAllocator(ClassNameAllocator allocator) {
186: opts.classNameAllocator = allocator;
187: }
188:
189: public JAXBModelImpl bind() {
190: // this has been problematic. turn it off.
191: // if(!forest.checkSchemaCorrectness(this))
192: // return null;
193:
194: // internalization
195: forest.transform();
196:
197: if (!NO_CORRECTNESS_CHECK) {
198: // correctness check
199: SchemaFactory sf = SchemaFactory
200: .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
201: sf.setErrorHandler(new DowngradingErrorHandler(this ));
202: forest.weakSchemaCorrectnessCheck(sf);
203: if (hadError)
204: return null; // error in the correctness check. abort now
205: }
206:
207: JCodeModel codeModel = new JCodeModel();
208:
209: ModelLoader gl = new ModelLoader(opts, codeModel, this );
210:
211: try {
212: XSSchemaSet result = gl.createXSOM(forest);
213: if (result == null)
214: return null;
215:
216: // we need info about each field, so we go ahead and generate the
217: // skeleton at this point.
218: // REVISIT: we should separate FieldRenderer and FieldAccessor
219: // so that accessors can be used before we build the code.
220: Model model = gl.annotateXMLSchema(result);
221: if (model == null)
222: return null;
223:
224: if (hadError)
225: return null; // if we have any error by now, abort
226:
227: Outline context = model.generateCode(opts, this );
228: if (context == null)
229: return null;
230:
231: if (hadError)
232: return null;
233:
234: return new JAXBModelImpl(context);
235: } catch (SAXException e) {
236: // since XSOM uses our parser that scans DOM,
237: // no parser error is possible.
238: // all the other errors will be directed to ErrorReceiver
239: // before it's thrown, so when the exception is thrown
240: // the error should have already been reported.
241:
242: // thus ignore.
243: return null;
244: }
245: }
246:
247: public void setErrorListener(ErrorListener errorListener) {
248: this .errorListener = errorListener;
249: }
250:
251: public void info(SAXParseException exception) {
252: if (errorListener != null)
253: errorListener.info(exception);
254: }
255:
256: public void warning(SAXParseException exception) {
257: if (errorListener != null)
258: errorListener.warning(exception);
259: }
260:
261: public void error(SAXParseException exception) {
262: hadError = true;
263: if (errorListener != null)
264: errorListener.error(exception);
265: }
266:
267: public void fatalError(SAXParseException exception) {
268: hadError = true;
269: if (errorListener != null)
270: errorListener.fatalError(exception);
271: }
272:
273: /**
274: * We use JAXP 1.3 to do a schema correctness check, but we know
275: * it doesn't always work. So in case some people hit the problem,
276: * this switch is here so that they can turn it off as a workaround.
277: */
278: private static boolean NO_CORRECTNESS_CHECK = false;
279:
280: static {
281: try {
282: NO_CORRECTNESS_CHECK = Boolean
283: .getBoolean(SchemaCompilerImpl.class
284: + ".noCorrectnessCheck");
285: } catch (Throwable t) {
286: // ignore
287: }
288: }
289: }
|