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