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.api.impl.s2j;
037:
038: import java.net.MalformedURLException;
039: import java.net.URI;
040: import java.net.URISyntaxException;
041: import java.net.URL;
042:
043: import javax.xml.XMLConstants;
044: import javax.xml.stream.XMLStreamException;
045: import javax.xml.stream.XMLStreamReader;
046: import javax.xml.validation.SchemaFactory;
047:
048: import com.sun.codemodel.JCodeModel;
049: import com.sun.istack.NotNull;
050: import com.sun.istack.SAXParseException2;
051: import com.sun.tools.xjc.ErrorReceiver;
052: import com.sun.tools.xjc.ModelLoader;
053: import com.sun.tools.xjc.Options;
054: import com.sun.tools.xjc.api.ClassNameAllocator;
055: import com.sun.tools.xjc.api.ErrorListener;
056: import com.sun.tools.xjc.api.SchemaCompiler;
057: import com.sun.tools.xjc.api.SpecVersion;
058: import com.sun.tools.xjc.model.Model;
059: import com.sun.tools.xjc.outline.Outline;
060: import com.sun.tools.xjc.reader.internalizer.DOMForest;
061: import com.sun.tools.xjc.reader.internalizer.SCDBasedBindingSet;
062: import com.sun.tools.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic;
063: import com.sun.xml.bind.unmarshaller.DOMScanner;
064: import com.sun.xml.xsom.XSSchemaSet;
065:
066: import org.w3c.dom.Element;
067: import org.xml.sax.ContentHandler;
068: import org.xml.sax.EntityResolver;
069: import org.xml.sax.InputSource;
070: import org.xml.sax.SAXException;
071: import org.xml.sax.SAXParseException;
072: import org.xml.sax.helpers.LocatorImpl;
073:
074: /**
075: * {@link SchemaCompiler} implementation.
076: *
077: * This class builds a {@link DOMForest} until the {@link #bind()} method,
078: * then this method does the rest of the hard work.
079: *
080: * @see ModelLoader
081: *
082: * @author
083: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
084: */
085: public final class SchemaCompilerImpl extends ErrorReceiver implements
086: SchemaCompiler {
087:
088: /**
089: * User-specified error receiver.
090: * This field can be null, in which case errors need to be discarded.
091: */
092: private ErrorListener errorListener;
093:
094: protected final Options opts = new Options();
095:
096: protected @NotNull
097: DOMForest forest;
098:
099: /**
100: * Set to true once an error is found.
101: */
102: private boolean hadError;
103:
104: public SchemaCompilerImpl() {
105: opts.compatibilityMode = Options.EXTENSION;
106: resetSchema();
107:
108: if (System.getProperty("xjc-api.test") != null) {
109: opts.debugMode = true;
110: opts.verbose = true;
111: }
112: }
113:
114: @NotNull
115: public Options getOptions() {
116: return opts;
117: }
118:
119: public ContentHandler getParserHandler(String systemId) {
120: return forest.getParserHandler(systemId, true);
121: }
122:
123: public void parseSchema(String systemId, Element element) {
124: checkAbsoluteness(systemId);
125: try {
126: DOMScanner scanner = new DOMScanner();
127:
128: // use a locator that sets the system ID correctly
129: // so that we can resolve relative URLs in most of the case.
130: // it still doesn't handle xml:base and XInclude and all those things
131: // correctly. There's just no way to make all those things work with DOM!
132: LocatorImpl loc = new LocatorImpl();
133: loc.setSystemId(systemId);
134: scanner.setLocator(loc);
135:
136: scanner.setContentHandler(getParserHandler(systemId));
137: scanner.scan(element);
138: } catch (SAXException e) {
139: // since parsing DOM shouldn't cause a SAX exception
140: // and our handler will never throw it, it's not clear
141: // if this will ever happen.
142: fatalError(new SAXParseException2(e.getMessage(), null,
143: systemId, -1, -1, e));
144: }
145: }
146:
147: public void parseSchema(InputSource source) {
148: checkAbsoluteness(source.getSystemId());
149: try {
150: forest.parse(source, true);
151: } catch (SAXException e) {
152: // parsers are required to report an error to ErrorHandler,
153: // so we should never see this error.
154: e.printStackTrace();
155: }
156: }
157:
158: public void setTargetVersion(SpecVersion version) {
159: if (version == null)
160: version = SpecVersion.LATEST;
161: opts.target = version;
162: }
163:
164: public void parseSchema(String systemId, XMLStreamReader reader)
165: throws XMLStreamException {
166: checkAbsoluteness(systemId);
167: forest.parse(systemId, reader, true);
168: }
169:
170: /**
171: * Checks if the system ID is absolute.
172: */
173: private void checkAbsoluteness(String systemId) {
174: // we need to be able to handle system IDs like "urn:foo", which java.net.URL can't process,
175: // but OTOH we also need to be able to process system IDs like "file://a b c/def.xsd",
176: // which java.net.URI can't process. So for now, let's fail only if both of them fail.
177: // eventually we need a proper URI class that works for us.
178: try {
179: new URL(systemId);
180: } catch (MalformedURLException _) {
181: try {
182: new URI(systemId);
183: } catch (URISyntaxException e) {
184: throw new IllegalArgumentException("system ID '"
185: + systemId + "' isn't absolute", e);
186: }
187: }
188: }
189:
190: public void setEntityResolver(EntityResolver entityResolver) {
191: forest.setEntityResolver(entityResolver);
192: opts.entityResolver = entityResolver;
193: }
194:
195: public void setDefaultPackageName(String packageName) {
196: opts.defaultPackage2 = packageName;
197: }
198:
199: public void forcePackageName(String packageName) {
200: opts.defaultPackage = packageName;
201: }
202:
203: public void setClassNameAllocator(ClassNameAllocator allocator) {
204: opts.classNameAllocator = allocator;
205: }
206:
207: public void resetSchema() {
208: forest = new DOMForest(new XMLSchemaInternalizationLogic());
209: forest.setErrorHandler(this );
210: forest.setEntityResolver(opts.entityResolver);
211: }
212:
213: public JAXBModelImpl bind() {
214: // this has been problematic. turn it off.
215: // if(!forest.checkSchemaCorrectness(this))
216: // return null;
217:
218: // internalization
219: SCDBasedBindingSet scdBasedBindingSet = forest.transform(opts
220: .isExtensionMode());
221:
222: if (!NO_CORRECTNESS_CHECK) {
223: // correctness check
224: SchemaFactory sf = SchemaFactory
225: .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
226: sf.setErrorHandler(new DowngradingErrorHandler(this ));
227: forest.weakSchemaCorrectnessCheck(sf);
228: if (hadError)
229: return null; // error in the correctness check. abort now
230: }
231:
232: JCodeModel codeModel = new JCodeModel();
233:
234: ModelLoader gl = new ModelLoader(opts, codeModel, this );
235:
236: try {
237: XSSchemaSet result = gl.createXSOM(forest,
238: scdBasedBindingSet);
239: if (result == null)
240: return null;
241:
242: // we need info about each field, so we go ahead and generate the
243: // skeleton at this point.
244: // REVISIT: we should separate FieldRenderer and FieldAccessor
245: // so that accessors can be used before we build the code.
246: Model model = gl.annotateXMLSchema(result);
247: if (model == null)
248: return null;
249:
250: if (hadError)
251: return null; // if we have any error by now, abort
252:
253: Outline context = model.generateCode(opts, this );
254: if (context == null)
255: return null;
256:
257: if (hadError)
258: return null;
259:
260: return new JAXBModelImpl(context);
261: } catch (SAXException e) {
262: // since XSOM uses our parser that scans DOM,
263: // no parser error is possible.
264: // all the other errors will be directed to ErrorReceiver
265: // before it's thrown, so when the exception is thrown
266: // the error should have already been reported.
267:
268: // thus ignore.
269: return null;
270: }
271: }
272:
273: public void setErrorListener(ErrorListener errorListener) {
274: this .errorListener = errorListener;
275: }
276:
277: public void info(SAXParseException exception) {
278: if (errorListener != null)
279: errorListener.info(exception);
280: }
281:
282: public void warning(SAXParseException exception) {
283: if (errorListener != null)
284: errorListener.warning(exception);
285: }
286:
287: public void error(SAXParseException exception) {
288: hadError = true;
289: if (errorListener != null)
290: errorListener.error(exception);
291: }
292:
293: public void fatalError(SAXParseException exception) {
294: hadError = true;
295: if (errorListener != null)
296: errorListener.fatalError(exception);
297: }
298:
299: /**
300: * We use JAXP 1.3 to do a schema correctness check, but we know
301: * it doesn't always work. So in case some people hit the problem,
302: * this switch is here so that they can turn it off as a workaround.
303: */
304: private static boolean NO_CORRECTNESS_CHECK = false;
305:
306: static {
307: try {
308: NO_CORRECTNESS_CHECK = Boolean
309: .getBoolean(SchemaCompilerImpl.class
310: + ".noCorrectnessCheck");
311: } catch (Throwable t) {
312: // ignore
313: }
314: }
315: }
|