src/share/jaxws_classes/com/sun/tools/internal/xjc/ModelLoader.java

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2013, 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
26 package com.sun.tools.internal.xjc;
27
28 import java.io.IOException;
29 import java.io.StringReader;
30
31 import com.sun.codemodel.internal.JCodeModel;
32 import com.sun.tools.internal.xjc.model.Model;
33 import com.sun.tools.internal.xjc.reader.Const;
34 import com.sun.tools.internal.xjc.reader.ExtensionBindingChecker;
35 import com.sun.tools.internal.xjc.reader.dtd.TDTDReader;
36 import com.sun.tools.internal.xjc.reader.internalizer.DOMForest;
37 import com.sun.tools.internal.xjc.reader.internalizer.DOMForestScanner;
38 import com.sun.tools.internal.xjc.reader.internalizer.InternalizationLogic;
39 import com.sun.tools.internal.xjc.reader.internalizer.SCDBasedBindingSet;
40 import com.sun.tools.internal.xjc.reader.internalizer.VersionChecker;
41 import com.sun.tools.internal.xjc.reader.relaxng.RELAXNGCompiler;
42 import com.sun.tools.internal.xjc.reader.relaxng.RELAXNGInternalizationLogic;
43 import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
44 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.AnnotationParserFactoryImpl;
45 import com.sun.tools.internal.xjc.reader.xmlschema.parser.CustomizationContextChecker;
46 import com.sun.tools.internal.xjc.reader.xmlschema.parser.IncorrectNamespaceURIChecker;
47 import com.sun.tools.internal.xjc.reader.xmlschema.parser.SchemaConstraintChecker;
48 import com.sun.tools.internal.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic;
49 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
50 import com.sun.xml.internal.bind.v2.util.XmlFactory;
51 import com.sun.xml.internal.xsom.XSSchemaSet;
52 import com.sun.xml.internal.xsom.parser.JAXPParser;
53 import com.sun.xml.internal.xsom.parser.XMLParser;
54 import com.sun.xml.internal.xsom.parser.XSOMParser;
55 import javax.xml.XMLConstants;
56
57 import com.sun.xml.internal.rngom.ast.builder.SchemaBuilder;
58 import com.sun.xml.internal.rngom.ast.util.CheckingSchemaBuilder;
59 import com.sun.xml.internal.rngom.digested.DPattern;
60 import com.sun.xml.internal.rngom.digested.DSchemaBuilderImpl;
61 import com.sun.xml.internal.rngom.parse.IllegalSchemaException;
62 import com.sun.xml.internal.rngom.parse.Parseable;
63 import com.sun.xml.internal.rngom.parse.compact.CompactParseable;
64 import com.sun.xml.internal.rngom.parse.xml.SAXParseable;
65 import com.sun.xml.internal.rngom.xml.sax.XMLReaderCreator;
66 import org.w3c.dom.Document;
67 import org.w3c.dom.Element;
68 import org.w3c.dom.NodeList;
69 import org.xml.sax.Attributes;
70 import org.xml.sax.ContentHandler;
71 import org.xml.sax.EntityResolver;
72 import org.xml.sax.ErrorHandler;
73 import org.xml.sax.InputSource;
74 import org.xml.sax.SAXException;
75 import org.xml.sax.SAXParseException;
76 import org.xml.sax.XMLFilter;
77 import org.xml.sax.XMLReader;
78 import org.xml.sax.helpers.XMLFilterImpl;
79
80 /**
81 * Builds a {@link Model} object.
82 *
83 * This is an utility class that makes it easy to load a grammar object
84 * from various sources.
85 *
86 * @author
87 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
88 */
89 public final class ModelLoader {
90
91 private final Options opt;
92 private final ErrorReceiverFilter errorReceiver;
93 private final JCodeModel codeModel;
94 /**
95 * {@link DOMForest#transform(boolean)} creates this on the side.
96 */
97 private SCDBasedBindingSet scdBasedBindingSet;
98
99
100 /**
101 * A convenience method to load schemas into a {@link Model}.
102 */
103 public static Model load( Options opt, JCodeModel codeModel, ErrorReceiver er ) {
104 return new ModelLoader(opt,codeModel,er).load();
105 }
106
107
108 public ModelLoader(Options _opt, JCodeModel _codeModel, ErrorReceiver er) {
109 this.opt = _opt;
110 this.codeModel = _codeModel;
111 this.errorReceiver = new ErrorReceiverFilter(er);
112 }
113
114 @SuppressWarnings("CallToThreadDumpStack")
115 private Model load() {
116 Model grammar;
117
118 if(!sanityCheck())
119 return null;
120
121
122 try {
123 switch (opt.getSchemaLanguage()) {
124 case DTD :
125 // TODO: make sure that bindFiles,size()<=1
126 InputSource bindFile = null;
127 if (opt.getBindFiles().length > 0)
128 bindFile = opt.getBindFiles()[0];
129 // if there is no binding file, make a dummy one.
130 if (bindFile == null) {
131 // if no binding information is specified, provide a default
132 bindFile =
133 new InputSource(
134 new StringReader(
135 "<?xml version='1.0'?><xml-java-binding-schema><options package='"
136 + (opt.defaultPackage==null?"generated":opt.defaultPackage)
137 + "'/></xml-java-binding-schema>"));
138 }
139
140 checkTooManySchemaErrors();
141 grammar = loadDTD(opt.getGrammars()[0], bindFile );
142 break;
143
144 case RELAXNG :
145 checkTooManySchemaErrors();
146 grammar = loadRELAXNG();
147 break;
148
149 case RELAXNG_COMPACT :
150 checkTooManySchemaErrors();
151 grammar = loadRELAXNGCompact();
152 break;
153
154 case WSDL:
155 grammar = annotateXMLSchema( loadWSDL() );
156 break;
157
158 case XMLSCHEMA:
159 grammar = annotateXMLSchema( loadXMLSchema() );
160 break;
161
162 default :
163 throw new AssertionError(); // assertion failed
164 }
165
166 if (errorReceiver.hadError()) {
167 grammar = null;
168 } else {
169 grammar.setPackageLevelAnnotations(opt.packageLevelAnnotations);
170 }
171
172 return grammar;
173
174 } catch (SAXException e) {
175 // parsing error in the input document.
176 // this error must have been reported to the user vis error handler
177 // so don't print it again.
178 if (opt.verbose) {
179 // however, a bug in XJC might throw unexpected SAXException.
180 // thus when one is debugging, it is useful to print what went
181 // wrong.
182 if (e.getException() != null)
183 e.getException().printStackTrace();
184 else
185 e.printStackTrace();
186 }
187 return null;
188 } catch (AbortException e) {
189 // error should have been reported already, since this is requested by the error receiver
190 return null;
191 }
192 }
193
194
195
196 /**
197 * Do some extra checking and return false if the compilation
198 * should abort.
199 */
200 private boolean sanityCheck() {
201 if( opt.getSchemaLanguage()==Language.XMLSCHEMA ) {
202 Language guess = opt.guessSchemaLanguage();
203
204 String[] msg = null;
205 switch(guess) {
206 case DTD:
207 msg = new String[]{"DTD","-dtd"};
208 break;
209 case RELAXNG:
210 msg = new String[]{"RELAX NG","-relaxng"};
211 break;
212 case RELAXNG_COMPACT:
213 msg = new String[]{"RELAX NG compact syntax","-relaxng-compact"};
214 break;
215 case WSDL:
216 msg = new String[]{"WSDL","-wsdl"};
217 break;
218 }
219 if( msg!=null )
220 errorReceiver.warning( null,
221 Messages.format(
222 Messages.EXPERIMENTAL_LANGUAGE_WARNING,
223 msg[0], msg[1] ));
224 }
225 return true;
226 }
227
228
229 /**
230 * {@link XMLParser} implementation that adds additional processors into the chain.
231 *
232 * <p>
233 * This parser will parse a DOM forest as:
234 * DOMForestParser -->
235 * ExtensionBindingChecker -->
236 * ProhibitedFeatureFilter -->
237 * XSOMParser
238 */
239 private class XMLSchemaParser implements XMLParser {
240 private final XMLParser baseParser;
241
242 private XMLSchemaParser(XMLParser baseParser) {
243 this.baseParser = baseParser;
244 }
245
246 public void parse(InputSource source, ContentHandler handler,
247 ErrorHandler errorHandler, EntityResolver entityResolver ) throws SAXException, IOException {
248 // set up the chain of handlers.
249 handler = wrapBy( new ExtensionBindingChecker(XMLConstants.W3C_XML_SCHEMA_NS_URI,opt,errorReceiver), handler );
250 handler = wrapBy( new IncorrectNamespaceURIChecker(errorReceiver), handler );
251 handler = wrapBy( new CustomizationContextChecker(errorReceiver), handler );
252 // handler = wrapBy( new VersionChecker(controller), handler );
253
254 baseParser.parse( source, handler, errorHandler, entityResolver );
255 }
256 /**
257 * Wraps the specified content handler by a filter.
258 * It is little awkward to use a helper implementation class like XMLFilterImpl
259 * as the method parameter, but this simplifies the code.
260 */
261 private ContentHandler wrapBy( XMLFilterImpl filter, ContentHandler handler ) {
262 filter.setContentHandler(handler);
263 return filter;
264 }
265 }
266
267 private void checkTooManySchemaErrors() {
268 if( opt.getGrammars().length!=1 )
269 errorReceiver.error(null,Messages.format(Messages.ERR_TOO_MANY_SCHEMA));
270 }
271
272 /**
273 * Parses a DTD file into an annotated grammar.
274 *
275 * @param source
276 * DTD file
277 * @param bindFile
278 * External binding file.
279 */
280 private Model loadDTD( InputSource source, InputSource bindFile) {
281
282 // parse the schema as a DTD.
283 return TDTDReader.parse(
284 source,
285 bindFile,
286 errorReceiver,
287 opt);
288 }
289
290 /**
291 * Builds DOMForest and performs the internalization.
292 *
293 * @throws SAXException
294 * when a fatal error happens
295 */
296 public DOMForest buildDOMForest( InternalizationLogic logic )
297 throws SAXException {
298
299 // parse into DOM forest
300 DOMForest forest = new DOMForest(logic, opt);
301
302 forest.setErrorHandler(errorReceiver);
303 if(opt.entityResolver!=null)
304 forest.setEntityResolver(opt.entityResolver);
305
306 // parse source grammars
307 for (InputSource value : opt.getGrammars()) {
308 errorReceiver.pollAbort();
309 forest.parse(value, true);
310 }
311
312 // parse external binding files
313 for (InputSource value : opt.getBindFiles()) {
314 errorReceiver.pollAbort();
315 Document dom = forest.parse(value, true);
316 if(dom==null) continue; // error must have been reported
317 Element root = dom.getDocumentElement();
318 // TODO: it somehow doesn't feel right to do a validation in the Driver class.
319 // think about moving it to somewhere else.
320 if (!fixNull(root.getNamespaceURI()).equals(Const.JAXB_NSURI)
321 || !root.getLocalName().equals("bindings"))
322 errorReceiver.error(new SAXParseException(Messages.format(Messages.ERR_NOT_A_BINDING_FILE,
323 root.getNamespaceURI(),
324 root.getLocalName()),
325 null,
326 value.getSystemId(),
327 -1, -1));
328 }
329
330 scdBasedBindingSet = forest.transform(opt.isExtensionMode());
331
332 return forest;
333 }
334
335 private String fixNull(String s) {
336 if(s==null) return "";
337 else return s;
338 }
339
340 /**
341 * Parses a set of XML Schema files into an annotated grammar.
342 */
343 public XSSchemaSet loadXMLSchema() throws SAXException {
344
345 if( opt.strictCheck && !SchemaConstraintChecker.check(opt.getGrammars(),errorReceiver,opt.entityResolver, opt.disableXmlSecurity)) {
346 // schema error. error should have been reported
347 return null;
348 }
349
350 if(opt.getBindFiles().length==0) {
351 // no external binding. try the speculative no DOMForest execution,
352 // which is faster if the speculation succeeds.
353 try {
354 return createXSOMSpeculative();
355 } catch( SpeculationFailure e) {
356 // failed. go the slow way
357 }
358 }
359
360 // the default slower way is to parse everything into DOM first.
361 // so that we can take external annotations into account.
362 DOMForest forest = buildDOMForest( new XMLSchemaInternalizationLogic() );
363 return createXSOM(forest, scdBasedBindingSet);
364 }
365
366 /**
367 * Parses a set of schemas inside a WSDL file.
368 *
369 * A WSDL file may contain multiple &lt;xsd:schema> elements.
370 */
371 private XSSchemaSet loadWSDL()
372 throws SAXException {
373
374
375 // build DOMForest just like we handle XML Schema
376 DOMForest forest = buildDOMForest( new XMLSchemaInternalizationLogic() );
377
378 DOMForestScanner scanner = new DOMForestScanner(forest);
379
380 XSOMParser xsomParser = createXSOMParser( forest );
381
382 // find <xsd:schema>s and parse them individually
383 for( InputSource grammar : opt.getGrammars() ) {
384 Document wsdlDom = forest.get( grammar.getSystemId() );
385 if (wsdlDom == null) {
386 String systemId = Options.normalizeSystemId(grammar.getSystemId());
387 if (forest.get(systemId) != null) {
388 grammar.setSystemId(systemId);
389 wsdlDom = forest.get( grammar.getSystemId() );
390 }
391 }
392
393 NodeList schemas = wsdlDom.getElementsByTagNameNS(XMLConstants.W3C_XML_SCHEMA_NS_URI,"schema");
394 for( int i=0; i<schemas.getLength(); i++ )
395 scanner.scan( (Element)schemas.item(i), xsomParser.getParserHandler() );
396 }
397 return xsomParser.getResult();
398 }
399
400 /**
401 * Annotates the obtained schema set.
402 *
403 * @return
404 * null if an error happens. In that case, the error messages
405 * will be properly reported to the controller by this method.
406 */
407 public Model annotateXMLSchema(XSSchemaSet xs) {
408 if (xs == null)
409 return null;
410 return BGMBuilder.build(xs, codeModel, errorReceiver, opt);
411 }
412
413 /**
414 * Potentially problematic - make sure the parser instance passed is initialized
415 * with proper security feature.
416 *
417 * @param parser
418 * @return
419 */
420 public XSOMParser createXSOMParser(XMLParser parser) {
421 // set up other parameters to XSOMParser
422 XSOMParser reader = new XSOMParser(new XMLSchemaParser(parser));
423 reader.setAnnotationParser(new AnnotationParserFactoryImpl(opt));
424 reader.setErrorHandler(errorReceiver);
425 reader.setEntityResolver(opt.entityResolver);
426 return reader;
427 }
428
429 public XSOMParser createXSOMParser(final DOMForest forest) {
430 XSOMParser p = createXSOMParser(forest.createParser());
431 p.setEntityResolver(new EntityResolver() {
432 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
433 // DOMForest only parses documents that are reachable through systemIds,
434 // and it won't pick up references like <xs:import namespace="..." /> without
435 // @schemaLocation. So we still need to use an entity resolver here to resolve
436 // these references, yet we don't want to just run them blindly, since if we do that
437 // DOMForestParser always get the translated system ID when catalog is used
438 // (where DOMForest records trees with their original system IDs.)
439 if(systemId!=null && forest.get(systemId)!=null)
440 return new InputSource(systemId);
441 if(opt.entityResolver!=null)
442 return opt.entityResolver.resolveEntity(publicId,systemId);
443
444 return null;
445 }
446 });
447 return p;
448 }
449
450
451 private static final class SpeculationFailure extends Error {}
452
453 private static final class SpeculationChecker extends XMLFilterImpl {
454 @Override
455 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
456 if(localName.equals("bindings") && uri.equals(Const.JAXB_NSURI))
457 throw new SpeculationFailure();
458 super.startElement(uri,localName,qName,attributes);
459 }
460 }
461
462 /**
463 * Parses schemas directly into XSOM by assuming that there's
464 * no external annotations.
465 * <p>
466 * When an external annotation is found, a {@link SpeculationFailure} is thrown,
467 * and we will do it all over again by using the slow way.
468 */
469 private XSSchemaSet createXSOMSpeculative() throws SAXException, SpeculationFailure {
470
471 // check if the schema contains external binding files. If so, speculation is a failure.
472
473 XMLParser parser = new XMLParser() {
474 private final JAXPParser base = new JAXPParser(XmlFactory.createParserFactory(opt.disableXmlSecurity));
475
476 public void parse(InputSource source, ContentHandler handler,
477 ErrorHandler errorHandler, EntityResolver entityResolver ) throws SAXException, IOException {
478 // set up the chain of handlers.
479 handler = wrapBy( new SpeculationChecker(), handler );
480 handler = wrapBy( new VersionChecker(null,errorReceiver,entityResolver), handler );
481
482 base.parse( source, handler, errorHandler, entityResolver );
483 }
484 /**
485 * Wraps the specified content handler by a filter.
486 * It is little awkward to use a helper implementation class like XMLFilterImpl
487 * as the method parameter, but this simplifies the code.
488 */
489 private ContentHandler wrapBy( XMLFilterImpl filter, ContentHandler handler ) {
490 filter.setContentHandler(handler);
491 return filter;
492 }
493 };
494
495 XSOMParser reader = createXSOMParser(parser);
496
497 // parse source grammars
498 for (InputSource value : opt.getGrammars())
499 reader.parse(value);
500
501 return reader.getResult();
502 }
503
504 /**
505 * Parses a {@link DOMForest} into a {@link XSSchemaSet}.
506 *
507 * @return
508 * null if the parsing failed.
509 */
510 public XSSchemaSet createXSOM(DOMForest forest, SCDBasedBindingSet scdBasedBindingSet) throws SAXException {
511 // set up other parameters to XSOMParser
512 XSOMParser reader = createXSOMParser(forest);
513
514 // re-parse the transformed schemas
515 for (String systemId : forest.getRootDocuments()) {
516 errorReceiver.pollAbort();
517 Document dom = forest.get(systemId);
518 if (!dom.getDocumentElement().getNamespaceURI().equals(Const.JAXB_NSURI)) {
519 reader.parse(systemId);
520 }
521 }
522
523 XSSchemaSet result = reader.getResult();
524
525 if(result!=null)
526 scdBasedBindingSet.apply(result,errorReceiver);
527
528 return result;
529 }
530
531 /**
532 * Parses a RELAX NG grammar into an annotated grammar.
533 */
534 private Model loadRELAXNG() throws SAXException {
535
536 // build DOM forest
537 final DOMForest forest = buildDOMForest( new RELAXNGInternalizationLogic() );
538
539 // use JAXP masquerading to validate the input document.
540 // DOMForest -> ExtensionBindingChecker -> RNGOM
541
542 XMLReaderCreator xrc = new XMLReaderCreator() {
543 public XMLReader createXMLReader() {
544
545 // foreset parser cannot change the receivers while it's working,
546 // so we need to have one XMLFilter that works as a buffer
547 XMLFilter buffer = new XMLFilterImpl() {
548 @Override
549 public void parse(InputSource source) throws IOException, SAXException {
550 forest.createParser().parse( source, this, this, this );
551 }
552 };
553
554 XMLFilter f = new ExtensionBindingChecker(Const.RELAXNG_URI,opt,errorReceiver);
555 f.setParent(buffer);
556
557 f.setEntityResolver(opt.entityResolver);
558
559 return f;
560 }
561 };
562
563 Parseable p = new SAXParseable( opt.getGrammars()[0], errorReceiver, xrc );
564
565 return loadRELAXNG(p);
566
567 }
568
569 /**
570 * Loads RELAX NG compact syntax
571 */
572 private Model loadRELAXNGCompact() {
573 if(opt.getBindFiles().length>0)
574 errorReceiver.error(new SAXParseException(
575 Messages.format(Messages.ERR_BINDING_FILE_NOT_SUPPORTED_FOR_RNC),null));
576
577 // TODO: entity resolver?
578 Parseable p = new CompactParseable( opt.getGrammars()[0], errorReceiver );
579
580 return loadRELAXNG(p);
581
582 }
583
584 /**
585 * Common part between the XML syntax and the compact syntax.
586 */
587 private Model loadRELAXNG(Parseable p) {
588 SchemaBuilder sb = new CheckingSchemaBuilder(new DSchemaBuilderImpl(),errorReceiver);
589
590 try {
591 DPattern out = (DPattern)p.parse(sb);
592 return RELAXNGCompiler.build(out,codeModel,opt);
593 } catch (IllegalSchemaException e) {
594 errorReceiver.error(e.getMessage(),e);
595 return null;
596 }
597 }
598 }

mercurial