aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.internal.xjc.reader.internalizer; aoqi@0: aoqi@0: import com.sun.istack.internal.NotNull; aoqi@0: import com.sun.istack.internal.XMLStreamReaderToContentHandler; aoqi@0: import com.sun.tools.internal.xjc.ErrorReceiver; aoqi@0: import com.sun.tools.internal.xjc.Options; aoqi@0: import com.sun.tools.internal.xjc.reader.Const; aoqi@0: import com.sun.tools.internal.xjc.util.ErrorReceiverFilter; aoqi@0: import com.sun.xml.internal.bind.marshaller.DataWriter; aoqi@0: import com.sun.xml.internal.bind.v2.util.XmlFactory; aoqi@0: import com.sun.xml.internal.xsom.parser.JAXPParser; aoqi@0: import com.sun.xml.internal.xsom.parser.XMLParser; aoqi@0: import org.w3c.dom.Document; aoqi@0: import org.w3c.dom.Element; aoqi@0: import org.xml.sax.*; aoqi@0: import org.xml.sax.helpers.XMLFilterImpl; aoqi@0: aoqi@0: import javax.xml.parsers.DocumentBuilder; aoqi@0: import javax.xml.parsers.DocumentBuilderFactory; aoqi@0: import javax.xml.parsers.ParserConfigurationException; aoqi@0: import javax.xml.parsers.SAXParserFactory; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.XMLStreamReader; aoqi@0: import javax.xml.transform.Source; aoqi@0: import javax.xml.transform.Transformer; aoqi@0: import javax.xml.transform.TransformerException; aoqi@0: import javax.xml.transform.TransformerFactory; aoqi@0: import javax.xml.transform.dom.DOMSource; aoqi@0: import javax.xml.transform.sax.SAXResult; aoqi@0: import javax.xml.transform.sax.SAXSource; aoqi@0: import javax.xml.validation.SchemaFactory; aoqi@0: import java.io.IOException; aoqi@0: import java.io.OutputStream; aoqi@0: import java.io.OutputStreamWriter; aoqi@0: import java.util.*; aoqi@0: aoqi@0: import static com.sun.xml.internal.bind.v2.util.XmlFactory.allowExternalAccess; aoqi@0: import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Builds a DOM forest and maintains association from aoqi@0: * system IDs to DOM trees. aoqi@0: * aoqi@0: *
aoqi@0: * A forest is a transitive reflexive closure of referenced documents. aoqi@0: * IOW, if a document is in a forest, all the documents referenced from aoqi@0: * it is in a forest, too. To support this semantics, {@link DOMForest} aoqi@0: * uses {@link InternalizationLogic} to find referenced documents. aoqi@0: * aoqi@0: *
aoqi@0: * Some documents are marked as "root"s, meaning those documents were
aoqi@0: * put into a forest explicitly, not because it is referenced from another
aoqi@0: * document. (However, a root document can be referenced from other
aoqi@0: * documents, too.)
aoqi@0: *
aoqi@0: * @author
aoqi@0: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
aoqi@0: */
aoqi@0: public final class DOMForest {
aoqi@0: /** actual data storage map<SystemId,Document>. */
aoqi@0: private final Map
aoqi@0: * Set of system ids as strings.
aoqi@0: */
aoqi@0: private final Set
aoqi@0: * This method performs a weaker version of the tests where error messages
aoqi@0: * are provided without line number information. So whenever possible
aoqi@0: * use {@link SchemaConstraintChecker}.
aoqi@0: *
aoqi@0: * @see SchemaConstraintChecker
aoqi@0: */
aoqi@0: public boolean checkSchemaCorrectness(ErrorReceiver errorHandler) {
aoqi@0: try {
aoqi@0: boolean disableXmlSecurity = false;
aoqi@0: if (options != null) {
aoqi@0: disableXmlSecurity = options.disableXmlSecurity;
aoqi@0: }
aoqi@0: SchemaFactory sf = XmlFactory.createSchemaFactory(W3C_XML_SCHEMA_NS_URI, disableXmlSecurity);
aoqi@0: ErrorReceiverFilter filter = new ErrorReceiverFilter(errorHandler);
aoqi@0: sf.setErrorHandler(filter);
aoqi@0: Set
aoqi@0: * Poor-man's base URI.
aoqi@0: */
aoqi@0: public String getSystemId( Document dom ) {
aoqi@0: for (Map.Entry
aoqi@0: * The client of this class can feed SAX events into the handler
aoqi@0: * to parse a document into this DOM forest.
aoqi@0: *
aoqi@0: * This version requires that the DOM object to be created and registered
aoqi@0: * to the map beforehand.
aoqi@0: */
aoqi@0: private ContentHandler getParserHandler( Document dom ) {
aoqi@0: ContentHandler handler = new DOMBuilder(dom,locatorTable,outerMostBindings);
aoqi@0: handler = new WhitespaceStripper(handler,errorReceiver,entityResolver);
aoqi@0: handler = new VersionChecker(handler,errorReceiver,entityResolver);
aoqi@0:
aoqi@0: // insert the reference finder so that
aoqi@0: // included/imported schemas will be also parsed
aoqi@0: XMLFilterImpl f = logic.createExternalReferenceFinder(this);
aoqi@0: f.setContentHandler(handler);
aoqi@0:
aoqi@0: if(errorReceiver!=null)
aoqi@0: f.setErrorHandler(errorReceiver);
aoqi@0: if(entityResolver!=null)
aoqi@0: f.setEntityResolver(entityResolver);
aoqi@0:
aoqi@0: return f;
aoqi@0: }
aoqi@0:
aoqi@0: public interface Handler extends ContentHandler {
aoqi@0: /**
aoqi@0: * Gets the DOM that was built.
aoqi@0: */
aoqi@0: public Document getDocument();
aoqi@0: }
aoqi@0:
aoqi@0: private static abstract class HandlerImpl extends XMLFilterImpl implements Handler {
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Returns a {@link ContentHandler} to feed SAX events into.
aoqi@0: *
aoqi@0: *
aoqi@0: * The client of this class can feed SAX events into the handler
aoqi@0: * to parse a document into this DOM forest.
aoqi@0: */
aoqi@0: public Handler getParserHandler( String systemId, boolean root ) {
aoqi@0: final Document dom = documentBuilder.newDocument();
aoqi@0: core.put( systemId, dom );
aoqi@0: if(root)
aoqi@0: rootDocuments.add(systemId);
aoqi@0:
aoqi@0: ContentHandler handler = getParserHandler(dom);
aoqi@0:
aoqi@0: // we will register the DOM to the map once the system ID becomes available.
aoqi@0: // but the SAX allows the event source to not to provide that information,
aoqi@0: // so be prepared for such case.
aoqi@0: HandlerImpl x = new HandlerImpl() {
aoqi@0: public Document getDocument() {
aoqi@0: return dom;
aoqi@0: }
aoqi@0: };
aoqi@0: x.setContentHandler(handler);
aoqi@0:
aoqi@0: return x;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Parses the given document and add it to the DOM forest.
aoqi@0: *
aoqi@0: * @return
aoqi@0: * null if there was a parse error. otherwise non-null.
aoqi@0: */
aoqi@0: public Document parse( String systemId, InputSource inputSource, boolean root ) throws SAXException {
aoqi@0: Document dom = documentBuilder.newDocument();
aoqi@0:
aoqi@0: systemId = Options.normalizeSystemId(systemId);
aoqi@0:
aoqi@0: // put into the map before growing a tree, to
aoqi@0: // prevent recursive reference from causing infinite loop.
aoqi@0: core.put( systemId, dom );
aoqi@0: if(root)
aoqi@0: rootDocuments.add(systemId);
aoqi@0:
aoqi@0: try {
aoqi@0: XMLReader reader = parserFactory.newSAXParser().getXMLReader();
aoqi@0: reader.setContentHandler(getParserHandler(dom));
aoqi@0: if(errorReceiver!=null)
aoqi@0: reader.setErrorHandler(errorReceiver);
aoqi@0: if(entityResolver!=null)
aoqi@0: reader.setEntityResolver(entityResolver);
aoqi@0: reader.parse(inputSource);
aoqi@0: } catch( ParserConfigurationException e ) {
aoqi@0: // in practice, this exception won't happen.
aoqi@0: errorReceiver.error(e.getMessage(),e);
aoqi@0: core.remove(systemId);
aoqi@0: rootDocuments.remove(systemId);
aoqi@0: return null;
aoqi@0: } catch( IOException e ) {
aoqi@0: errorReceiver.error(Messages.format(Messages.DOMFOREST_INPUTSOURCE_IOEXCEPTION, systemId, e.toString()),e);
aoqi@0: core.remove(systemId);
aoqi@0: rootDocuments.remove(systemId);
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: return dom;
aoqi@0: }
aoqi@0:
aoqi@0: public Document parse( String systemId, XMLStreamReader parser, boolean root ) throws XMLStreamException {
aoqi@0: Document dom = documentBuilder.newDocument();
aoqi@0:
aoqi@0: systemId = Options.normalizeSystemId(systemId);
aoqi@0:
aoqi@0: if(root)
aoqi@0: rootDocuments.add(systemId);
aoqi@0:
aoqi@0: if(systemId==null)
aoqi@0: throw new IllegalArgumentException("system id cannot be null");
aoqi@0: core.put( systemId, dom );
aoqi@0:
aoqi@0: new XMLStreamReaderToContentHandler(parser,getParserHandler(dom),false,false).bridge();
aoqi@0:
aoqi@0: return dom;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Performs internalization.
aoqi@0: *
aoqi@0: * This method should be called only once, only after all the
aoqi@0: * schemas are parsed.
aoqi@0: *
aoqi@0: * @return
aoqi@0: * the returned bindings need to be applied after schema
aoqi@0: * components are built.
aoqi@0: */
aoqi@0: public SCDBasedBindingSet transform(boolean enableSCD) {
aoqi@0: return Internalizer.transform(this, enableSCD, options.disableXmlSecurity);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Performs the schema correctness check by using JAXP 1.3.
aoqi@0: *
aoqi@0: *
aoqi@0: * This is "weak", because {@link SchemaFactory#newSchema(Source[])}
aoqi@0: * doesn't handle inclusions very correctly (it ends up parsing it
aoqi@0: * from its original source, not in this tree), and because
aoqi@0: * it doesn't handle two documents for the same namespace very
aoqi@0: * well.
aoqi@0: *
aoqi@0: *
aoqi@0: * We should eventually fix JAXP (and Xerces), but meanwhile
aoqi@0: * this weaker and potentially wrong correctness check is still
aoqi@0: * better than nothing when used inside JAX-WS (JAXB CLI and Ant
aoqi@0: * does a better job of checking this.)
aoqi@0: *
aoqi@0: *
aoqi@0: * To receive errors, use {@link SchemaFactory#setErrorHandler(ErrorHandler)}.
aoqi@0: */
aoqi@0: public void weakSchemaCorrectnessCheck(SchemaFactory sf) {
aoqi@0: List