Thu, 24 May 2018 17:55:52 +0800
Merge
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<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 <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 }