src/share/jaxws_classes/com/sun/tools/internal/ws/wsdl/parser/DOMForest.java

Sun, 25 Jun 2017 00:13:53 +0100

author
aefimov
date
Sun, 25 Jun 2017 00:13:53 +0100
changeset 1386
65d3b0e44551
parent 408
b0610cd08440
child 1435
a90b319bae7a
permissions
-rw-r--r--

8182054: Improve wsdl support
Summary: Also reviewed by Roman Grigoriadi <roman.grigoriadi@oracle.com>
Reviewed-by: joehw, lancea

     1 /*
     2  * Copyright (c) 1997, 2017, 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  */
    26 package com.sun.tools.internal.ws.wsdl.parser;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.tools.internal.ws.util.xml.XmlUtil;
    30 import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
    31 import com.sun.tools.internal.ws.wscompile.WsimportOptions;
    32 import com.sun.tools.internal.ws.wsdl.document.schema.SchemaConstants;
    33 import com.sun.tools.internal.xjc.reader.internalizer.LocatorTable;
    34 import com.sun.xml.internal.bind.marshaller.DataWriter;
    35 import org.w3c.dom.Document;
    36 import org.w3c.dom.Element;
    37 import org.w3c.dom.NodeList;
    38 import org.xml.sax.ContentHandler;
    39 import org.xml.sax.*;
    40 import org.xml.sax.helpers.XMLFilterImpl;
    42 import javax.xml.parsers.DocumentBuilder;
    43 import javax.xml.parsers.DocumentBuilderFactory;
    44 import javax.xml.parsers.ParserConfigurationException;
    45 import javax.xml.parsers.SAXParserFactory;
    46 import javax.xml.transform.Transformer;
    47 import javax.xml.transform.TransformerException;
    48 import javax.xml.transform.TransformerFactory;
    49 import javax.xml.transform.dom.DOMSource;
    50 import javax.xml.transform.sax.SAXResult;
    51 import java.io.IOException;
    52 import java.io.InputStream;
    53 import java.io.OutputStream;
    54 import java.io.OutputStreamWriter;
    55 import java.net.*;
    56 import java.util.*;
    58 /**
    59  * @author Vivek Pandey
    60  */
    61 public class DOMForest {
    62     /**
    63      * To correctly feed documents to a schema parser, we need to remember
    64      * which documents (of the forest) were given as the root
    65      * documents, and which of them are read as included/imported
    66      * documents.
    67      * <p/>
    68      * <p/>
    69      * Set of system ids as strings.
    70      */
    71     protected final Set<String> rootDocuments = new HashSet<String>();
    73     /**
    74      * Contains wsdl:import(s)
    75      */
    76     protected final Set<String> externalReferences = new HashSet<String>();
    78     /**
    79      * actual data storage map&lt;SystemId,Document>.
    80      */
    81     protected final Map<String, Document> core = new HashMap<String, Document>();
    82     protected final ErrorReceiver errorReceiver;
    84     private final DocumentBuilder documentBuilder;
    85     private final SAXParserFactory parserFactory;
    87     /**
    88      * inlined schema elements inside wsdl:type section
    89      */
    90     protected final List<Element> inlinedSchemaElements = new ArrayList<Element>();
    93     /**
    94      * Stores location information for all the trees in this forest.
    95      */
    96     public final LocatorTable locatorTable = new LocatorTable();
    98     protected final EntityResolver entityResolver;
    99     /**
   100      * Stores all the outer-most &lt;jaxb:bindings> customizations.
   101      */
   102     public final Set<Element> outerMostBindings = new HashSet<Element>();
   104     /**
   105      * Schema language dependent part of the processing.
   106      */
   107     protected final InternalizationLogic logic;
   108     protected final WsimportOptions options;
   110     public DOMForest(InternalizationLogic logic, @NotNull EntityResolver entityResolver, WsimportOptions options, ErrorReceiver errReceiver) {
   111         this.options = options;
   112         this.entityResolver = entityResolver;
   113         this.errorReceiver = errReceiver;
   114         this.logic = logic;
   115         // secure xml processing can be switched off if input requires it
   116         boolean disableXmlSecurity = options == null ? false : options.disableXmlSecurity;
   118         DocumentBuilderFactory dbf = XmlUtil.newDocumentBuilderFactory(disableXmlSecurity);
   119         this.parserFactory = XmlUtil.newSAXParserFactory(disableXmlSecurity);
   120         try {
   121             this.documentBuilder = dbf.newDocumentBuilder();
   122         } catch (ParserConfigurationException e) {
   123             throw new AssertionError(e);
   124         }
   125     }
   127     public List<Element> getInlinedSchemaElement() {
   128         return inlinedSchemaElements;
   129     }
   131     public @NotNull Document parse(InputSource source, boolean root) throws SAXException, IOException {
   132         if (source.getSystemId() == null)
   133             throw new IllegalArgumentException();
   134         return parse(source.getSystemId(), source, root);
   135     }
   137     /**
   138      * Parses an XML at the given location (
   139      * and XMLs referenced by it) into DOM trees
   140      * and stores them to this forest.
   141      *
   142      * @return the parsed DOM document object.
   143      */
   144     public Document parse(String systemId, boolean root) throws SAXException, IOException{
   146         systemId = normalizeSystemId(systemId);
   148         InputSource is = null;
   150         // allow entity resolver to find the actual byte stream.
   151         is = entityResolver.resolveEntity(null, systemId);
   152         if (is == null)
   153             is = new InputSource(systemId);
   154         else {
   155             resolvedCache.put(systemId, is.getSystemId());
   156             systemId=is.getSystemId();
   157         }
   159         if (core.containsKey(systemId)) {
   160             // this document has already been parsed. Just ignore.
   161             return core.get(systemId);
   162         }
   164         if(!root)
   165             addExternalReferences(systemId);
   167         // but we still use the original system Id as the key.
   168         return parse(systemId, is, root);
   169     }
   170     protected Map<String,String> resolvedCache = new HashMap<String,String>();
   172     public Map<String,String> getReferencedEntityMap() {
   173         return resolvedCache;
   174     }
   175     /**
   176      * Parses the given document and add it to the DOM forest.
   177      *
   178      * @return null if there was a parse error. otherwise non-null.
   179      */
   180     private @NotNull Document parse(String systemId, InputSource inputSource, boolean root) throws SAXException, IOException{
   181         Document dom = documentBuilder.newDocument();
   183         systemId = normalizeSystemId(systemId);
   185         // put into the map before growing a tree, to
   186         // prevent recursive reference from causing infinite loop.
   187         core.put(systemId, dom);
   189         dom.setDocumentURI(systemId);
   190         if (root)
   191             rootDocuments.add(systemId);
   193         try {
   194             XMLReader reader = createReader(dom);
   196             InputStream is = null;
   197             if(inputSource.getByteStream() == null){
   198                 inputSource = entityResolver.resolveEntity(null, systemId);
   199             }
   200             reader.parse(inputSource);
   201             Element doc = dom.getDocumentElement();
   202             if (doc == null) {
   203                 return null;
   204             }
   205             NodeList schemas = doc.getElementsByTagNameNS(SchemaConstants.NS_XSD, "schema");
   206             for (int i = 0; i < schemas.getLength(); i++) {
   207                 inlinedSchemaElements.add((Element) schemas.item(i));
   208             }
   209         } catch (ParserConfigurationException e) {
   210             errorReceiver.error(e);
   211             throw new SAXException(e.getMessage());
   212         }
   213         resolvedCache.put(systemId, dom.getDocumentURI());
   214         return dom;
   215     }
   217     public void addExternalReferences(String ref) {
   218         if (!externalReferences.contains(ref))
   219             externalReferences.add(ref);
   220     }
   223     public Set<String> getExternalReferences() {
   224         return externalReferences;
   225     }
   229     public interface Handler extends ContentHandler {
   230         /**
   231          * Gets the DOM that was built.
   232          */
   233         public Document getDocument();
   234     }
   236     /**
   237          * Returns a {@link org.xml.sax.XMLReader} to parse a document into this DOM forest.
   238          * <p/>
   239          * This version requires that the DOM object to be created and registered
   240          * to the map beforehand.
   241          */
   242     private XMLReader createReader(Document dom) throws SAXException, ParserConfigurationException {
   243         XMLReader reader = parserFactory.newSAXParser().getXMLReader();
   244         DOMBuilder dombuilder = new DOMBuilder(dom, locatorTable, outerMostBindings);
   245         try {
   246             reader.setProperty("http://xml.org/sax/properties/lexical-handler", dombuilder);
   247         } catch(SAXException e) {
   248             errorReceiver.debug(e.getMessage());
   249         }
   251         ContentHandler handler = new WhitespaceStripper(dombuilder, errorReceiver, entityResolver);
   252         handler = new VersionChecker(handler, errorReceiver, entityResolver);
   254         // insert the reference finder so that
   255         // included/imported schemas will be also parsed
   256         XMLFilterImpl f = logic.createExternalReferenceFinder(this);
   257         f.setContentHandler(handler);
   258         if (errorReceiver != null)
   259             f.setErrorHandler(errorReceiver);
   260         f.setEntityResolver(entityResolver);
   262         reader.setContentHandler(f);
   263         if (errorReceiver != null)
   264             reader.setErrorHandler(errorReceiver);
   265         reader.setEntityResolver(entityResolver);
   266         return reader;
   267     }
   269     private String normalizeSystemId(String systemId) {
   270         try {
   271             systemId = new URI(systemId).normalize().toString();
   272         } catch (URISyntaxException e) {
   273             // leave the system ID untouched. In my experience URI is often too strict
   274         }
   275         return systemId;
   276     }
   278     boolean isExtensionMode() {
   279         return options.isExtensionMode();
   280     }
   283     /**
   284      * Gets the DOM tree associated with the specified system ID,
   285      * or null if none is found.
   286      */
   287     public Document get(String systemId) {
   288         Document doc = core.get(systemId);
   290         if (doc == null && systemId.startsWith("file:/") && !systemId.startsWith("file://")) {
   291             // As of JDK1.4, java.net.URL.toExternal method returns URLs like
   292             // "file:/abc/def/ghi" which is an incorrect file protocol URL according to RFC1738.
   293             // Some other correctly functioning parts return the correct URLs ("file:///abc/def/ghi"),
   294             // and this descripancy breaks DOM look up by system ID.
   296             // this extra check solves this problem.
   297             doc = core.get("file://" + systemId.substring(5));
   298         }
   300         if (doc == null && systemId.startsWith("file:")) {
   301             // on Windows, filenames are case insensitive.
   302             // perform case-insensitive search for improved user experience
   303             String systemPath = getPath(systemId);
   304             for (String key : core.keySet()) {
   305                 if (key.startsWith("file:") && getPath(key).equalsIgnoreCase(systemPath)) {
   306                     doc = core.get(key);
   307                     break;
   308                 }
   309             }
   310         }
   312         return doc;
   313     }
   315     /**
   316      * Strips off the leading 'file:///' portion from an URL.
   317      */
   318     private String getPath(String key) {
   319         key = key.substring(5); // skip 'file:'
   320         while (key.length() > 0 && key.charAt(0) == '/')
   321             key = key.substring(1);
   322         return key;
   323     }
   325     /**
   326      * Gets all the system IDs of the documents.
   327      */
   328     public String[] listSystemIDs() {
   329         return core.keySet().toArray(new String[core.keySet().size()]);
   330     }
   332     /**
   333      * Gets the system ID from which the given DOM is parsed.
   334      * <p/>
   335      * Poor-man's base URI.
   336      */
   337     public String getSystemId(Document dom) {
   338         for (Map.Entry<String, Document> e : core.entrySet()) {
   339             if (e.getValue() == dom)
   340                 return e.getKey();
   341         }
   342         return null;
   343     }
   345     /**
   346      * Gets the first one (which is more or less random) in {@link #rootDocuments}.
   347      */
   348     public String getFirstRootDocument() {
   349         if(rootDocuments.isEmpty()) return null;
   350         return rootDocuments.iterator().next();
   351     }
   353     public Set<String> getRootDocuments() {
   354         return rootDocuments;
   355     }
   357     /**
   358      * Dumps the contents of the forest to the specified stream.
   359      * <p/>
   360      * This is a debug method. As such, error handling is sloppy.
   361      */
   362     public void dump(OutputStream out) throws IOException {
   363         try {
   364             // create identity transformer
   365             // secure xml processing can be switched off if input requires it
   366             boolean secureProcessingEnabled = options == null || !options.disableXmlSecurity;
   367             TransformerFactory tf = XmlUtil.newTransformerFactory(secureProcessingEnabled);
   368             Transformer it = tf.newTransformer();
   370             for (Map.Entry<String, Document> e : core.entrySet()) {
   371                 out.write(("---<< " + e.getKey() + '\n').getBytes());
   373                 DataWriter dw = new DataWriter(new OutputStreamWriter(out), null);
   374                 dw.setIndentStep("  ");
   375                 it.transform(new DOMSource(e.getValue()),
   376                         new SAXResult(dw));
   378                 out.write("\n\n\n".getBytes());
   379             }
   380         } catch (TransformerException e) {
   381             e.printStackTrace();
   382         }
   383     }
   385 }

mercurial