blob: 08c943f13ce320ec1c5f3ef8b450fa75181f6720 [file] [log] [blame]
nitindb0f7b262004-11-23 19:12:23 +00001/*******************************************************************************
amywu19526ad2007-04-10 17:03:52 +00002 * Copyright (c) 2004, 2006 IBM Corporation and others.
nitindb0f7b262004-11-23 19:12:23 +00003 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
amywu19526ad2007-04-10 17:03:52 +00007 *
nitindb0f7b262004-11-23 19:12:23 +00008 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
nitind49418452005-02-15 06:42:31 +000011package org.eclipse.jst.jsp.core.internal.util;
nitindb0f7b262004-11-23 19:12:23 +000012
13
nitind0a4d3b52006-11-06 20:31:56 +000014import java.io.BufferedInputStream;
nitind6e3e71d2006-02-09 20:39:41 +000015import java.io.ByteArrayInputStream;
nitindb0f7b262004-11-23 19:12:23 +000016import java.io.File;
17import java.io.FileInputStream;
18import java.io.FileNotFoundException;
nitindb0f7b262004-11-23 19:12:23 +000019import java.io.IOException;
20import java.io.InputStream;
nitindb0f7b262004-11-23 19:12:23 +000021import java.net.URL;
22import java.net.URLConnection;
nitindb0f7b262004-11-23 19:12:23 +000023
24import javax.xml.parsers.DocumentBuilder;
25import javax.xml.parsers.DocumentBuilderFactory;
26import javax.xml.parsers.FactoryConfigurationError;
27import javax.xml.parsers.ParserConfigurationException;
28
david_williams425ffe72004-12-07 21:46:39 +000029import org.eclipse.jst.jsp.core.internal.Logger;
david_williams2aecf082005-04-13 05:03:21 +000030import org.eclipse.wst.sse.core.internal.util.JarUtilities;
nitindb0f7b262004-11-23 19:12:23 +000031import org.w3c.dom.DOMException;
32import org.w3c.dom.DOMImplementation;
33import org.w3c.dom.Document;
34import org.w3c.dom.Element;
35import org.w3c.dom.Node;
36import org.w3c.dom.NodeList;
37import org.xml.sax.EntityResolver;
38import org.xml.sax.ErrorHandler;
39import org.xml.sax.InputSource;
40import org.xml.sax.SAXException;
41import org.xml.sax.SAXParseException;
42
nitind0a4d3b52006-11-06 20:31:56 +000043import com.ibm.icu.util.StringTokenizer;
44
nitindb0f7b262004-11-23 19:12:23 +000045/**
nitind3a1c9712004-12-07 06:47:36 +000046 * An XML Creator/Reader/Writer that 1) Ignores any DocumentType Nodes found
47 * within the document (as well as any entities) 2) Ignores any
48 * errors/exceptions from Xerces when loading a document 3) Can load Documents
49 * from within a .JAR file (***read-only***)
nitindb0f7b262004-11-23 19:12:23 +000050 */
51
52public class DocumentProvider {
david_williams757ccfe2005-05-01 08:03:21 +000053 private Document document = null;
54 private ErrorHandler errorHandler = null;
nitindb0f7b262004-11-23 19:12:23 +000055 private String fBaseReference;
david_williams757ccfe2005-05-01 08:03:21 +000056 private String fileName = null;
nitind6e3e71d2006-02-09 20:39:41 +000057 private boolean fValidating = false;
david_williams757ccfe2005-05-01 08:03:21 +000058 private InputStream inputStream = null;
59 private String jarFileName = null;
60 private EntityResolver resolver = null;
nitindb0f7b262004-11-23 19:12:23 +000061
david_williams757ccfe2005-05-01 08:03:21 +000062 private Node rootElement = null;
63 private String rootElementName = null;
nitindb0f7b262004-11-23 19:12:23 +000064
65 public DocumentProvider() {
david_williams757ccfe2005-05-01 08:03:21 +000066 super();
nitindb0f7b262004-11-23 19:12:23 +000067 }
68
nitind63bacdc2005-08-11 03:28:18 +000069 private String _getFileName() {
70 if (inputStream != null)
71 return null;
72 else if (isJAR()) {
73 return getJarFileName();
74 }
75 return getFileName();
76 }
77
david_williams757ccfe2005-05-01 08:03:21 +000078 private Document _getParsedDocumentDOM2() {
nitindb0f7b262004-11-23 19:12:23 +000079 Document result = null;
80
81 InputStream is = null;
82 try {
83 DocumentBuilder builder = getDocumentBuilder();
nitind3a1c9712004-12-07 06:47:36 +000084 // DOMParser parser = new DOMParser();
85 // parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error",
86 // false);//$NON-NLS-1$
87 // parser.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
88 // false);//$NON-NLS-1$
89 // parser.setErrorHandler(getNullErrorHandler());
90 // parser.setEntityResolver(getNullEntityResolver());
91 // is = getInputStream();
nitindb0f7b262004-11-23 19:12:23 +000092 builder.setEntityResolver(getEntityResolver());
93 builder.setErrorHandler(getNullErrorHandler());
94 is = getInputStream();
95 if (is != null)
96 result = builder.parse(is, getBaseReference());
97 }
98 catch (SAXException e) {
99 result = null;
100 // parsing exception, notify the user?
nitind63bacdc2005-08-11 03:28:18 +0000101 Logger.log(Logger.WARNING, "SAXException while reading descriptor " + _getFileName() + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
nitindb0f7b262004-11-23 19:12:23 +0000102 }
103 catch (FileNotFoundException e) {
104 // NOT an "exceptional case"; do not Log
105 }
106 catch (IOException e) {
nitind63bacdc2005-08-11 03:28:18 +0000107 Logger.log(Logger.WARNING, "IOException while reading descriptor " + _getFileName() + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
nitindb0f7b262004-11-23 19:12:23 +0000108 }
109 finally {
110 if (is != null) {
111 try {
112 is.close();
113 }
114 catch (Exception e) {
115 // what can be done?
116 }
117 }
118 }
119 return result;
120 }
121
122 /**
123 * @return
124 */
125 public String getBaseReference() {
126 return fBaseReference;
127 }
128
129 /**
130 *
131 * @return Document
132 */
133 public Document getDocument() {
nitind24835b92006-01-16 23:20:53 +0000134 return getDocument(true);
135 }
136
137 public Document getDocument(boolean createEmptyOnFailure) {
nitindb0f7b262004-11-23 19:12:23 +0000138 if (document == null)
nitind24835b92006-01-16 23:20:53 +0000139 load(createEmptyOnFailure);
nitindb0f7b262004-11-23 19:12:23 +0000140 return document;
141 }
142
david_williams757ccfe2005-05-01 08:03:21 +0000143 private DocumentBuilder getDocumentBuilder() {
nitindb0f7b262004-11-23 19:12:23 +0000144 return CommonXML.getDocumentBuilder(isValidating());
145 }
146
david_williams757ccfe2005-05-01 08:03:21 +0000147 private DOMImplementation getDomImplementation() {
nitindb0f7b262004-11-23 19:12:23 +0000148 DocumentBuilder builder = null;
149 try {
150 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
151 }
152 catch (ParserConfigurationException e1) {
153 Logger.logException(e1);
154 }
155 catch (FactoryConfigurationError e1) {
156 Logger.logException(e1);
157 }
158 DOMImplementation impl = builder.getDOMImplementation();
159 return impl;
160 }
161
nitind3a1c9712004-12-07 06:47:36 +0000162 /*************************************************************************
163 * Takes a single string of the form "a/b/c" and ensures that that
164 * structure exists below the head element, down through 'c', and returns
165 * a <em>single</em> element 'c'. For multiple elements (such as
166 * multiple &lt;macro&gt; elements contained within a single
167 * &lt;macros&gt; element, full DOM access is required for searching and
nitindb0f7b262004-11-23 19:12:23 +0000168 * child element manipulation.
nitind3a1c9712004-12-07 06:47:36 +0000169 ************************************************************************/
nitindb0f7b262004-11-23 19:12:23 +0000170 public Element getElement(String name) {
david_williams757ccfe2005-05-01 08:03:21 +0000171 Element result = null;
nitindb0f7b262004-11-23 19:12:23 +0000172 if (document == null)
nitind24835b92006-01-16 23:20:53 +0000173 load(false);
david_williams757ccfe2005-05-01 08:03:21 +0000174 if (document != null) {
175 result = (Element) getNode(getRootElement(), name);
176 }
177 return result;
nitindb0f7b262004-11-23 19:12:23 +0000178 }
179
180 /**
nitind3a1c9712004-12-07 06:47:36 +0000181 * Returns an EntityResolver that won't try to load and resolve ANY
182 * entities
nitindb0f7b262004-11-23 19:12:23 +0000183 */
184 private EntityResolver getEntityResolver() {
185 if (resolver == null) {
186 resolver = new EntityResolver() {
187 public InputSource resolveEntity(String publicID, String systemID) throws SAXException, IOException {
188 InputSource result = null;
nitind6e3e71d2006-02-09 20:39:41 +0000189 if (isValidating()) {
nitindb0f7b262004-11-23 19:12:23 +0000190 try {
191 URL spec = new URL("file://" + getBaseReference()); //$NON-NLS-1$
192 URL url = new URL(spec, systemID);
nitind3a1c9712004-12-07 06:47:36 +0000193 if (url.getProtocol().startsWith("file:")) { //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000194 URLConnection connection = url.openConnection();
195 result = new InputSource(systemID != null ? systemID : "/_" + toString()); //$NON-NLS-1$
196 result.setPublicId(publicID);
197 result.setByteStream(connection.getInputStream());
198 }
199 }
200 catch (Exception e) {
201 result = null;
202 }
203 }
nitind6e3e71d2006-02-09 20:39:41 +0000204
nitindb0f7b262004-11-23 19:12:23 +0000205 if (result == null) {
nitind6e3e71d2006-02-09 20:39:41 +0000206 result = new InputSource(new ByteArrayInputStream(new byte[0]));
nitindb0f7b262004-11-23 19:12:23 +0000207 result.setPublicId(publicID);
208 result.setSystemId(systemID != null ? systemID : "/_" + getClass().getName()); //$NON-NLS-1$
209 }
210 return result;
211 }
212 };
213 }
214 return resolver;
215 }
216
217 /**
218 *
219 * @return java.lang.String
220 */
nitind21f317d2005-02-17 23:52:35 +0000221 public String getFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000222 return fileName;
223 }
224
225 /**
nitind3a1c9712004-12-07 06:47:36 +0000226 * Returns and input stream to use as the source of the Document 1) from
227 * an InputStream set on this instance 2) from a JAR file with the given
228 * entry name 3) from a normal file
nitindb0f7b262004-11-23 19:12:23 +0000229 *
230 * @return InputStream
231 */
232 public InputStream getInputStream() throws FileNotFoundException {
233 if (inputStream != null)
234 return inputStream;
235 else if (isJAR()) {
236 return JarUtilities.getInputStream(getJarFileName(), getFileName());
237 }
238 else {
nitind0a4d3b52006-11-06 20:31:56 +0000239 return new BufferedInputStream(new FileInputStream(getFileName()));
nitindb0f7b262004-11-23 19:12:23 +0000240 }
241 }
242
243 /**
244 *
245 * @return java.lang.String
246 */
nitind21f317d2005-02-17 23:52:35 +0000247 public String getJarFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000248 return jarFileName;
249 }
250
david_williams757ccfe2005-05-01 08:03:21 +0000251 private Node getNamedChild(Node parent, String childName) {
nitindb0f7b262004-11-23 19:12:23 +0000252 if (parent == null) {
253 return null;
254 }
255 NodeList childList = parent.getChildNodes();
256 for (int i = 0; i < childList.getLength(); i++) {
257 if (childList.item(i).getNodeName().equals(childName))
258 return childList.item(i);
259 }
260 return null;
261 }
262
david_williams757ccfe2005-05-01 08:03:21 +0000263 private Document getNewDocument() {
nitindb0f7b262004-11-23 19:12:23 +0000264 Document result = null;
265 try {
nitind7228c6e2005-08-03 21:36:32 +0000266 result = getDomImplementation().createDocument("http://www.w3.org/XML/1998/namespace", getRootElementName(), null); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000267 NodeList children = result.getChildNodes();
268 for (int i = 0; i < children.getLength(); i++) {
269 result.removeChild(children.item(i));
270 }
271 // we're going through this effort to avoid a NS element
nitind73e76412005-11-10 01:50:19 +0000272 Element settings = result.createElementNS("http://www.w3.org/XML/1998/namespace", getRootElementName()); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000273 result.appendChild(settings);
274 return result;
275 }
276 catch (DOMException e) {
277 Logger.logException(e);
278 }
279 return null;
280 }
281
nitind3a1c9712004-12-07 06:47:36 +0000282 /*************************************************************************
283 * Takes a single string of the form "a/b/c" and ensures that that
284 * structure exists below the head element, down through 'c', and returns
285 * the element 'c'.
286 ************************************************************************/
david_williams757ccfe2005-05-01 08:03:21 +0000287 private Node getNode(Node node, String name) {
nitindb0f7b262004-11-23 19:12:23 +0000288 StringTokenizer tokenizer = new StringTokenizer(name, "/"); //$NON-NLS-1$
289 String token = null;
290 while (tokenizer.hasMoreTokens()) {
291 token = tokenizer.nextToken();
292 if (getNamedChild(node, token) == null)
293 node.appendChild(document.createElement(token));
294 node = getNamedChild(node, token);
295 }
296 return node;
297 }
298
299 /**
nitind3a1c9712004-12-07 06:47:36 +0000300 * Returns an ErrorHandler that will not stop the parser on reported
301 * errors
nitindb0f7b262004-11-23 19:12:23 +0000302 */
303 private ErrorHandler getNullErrorHandler() {
304 if (errorHandler == null) {
305 errorHandler = new ErrorHandler() {
306 public void error(SAXParseException exception) throws SAXException {
david_williamse3f46612005-04-07 01:12:35 +0000307 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (error) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
nitindb0f7b262004-11-23 19:12:23 +0000308 }
309
310 public void fatalError(SAXParseException exception) throws SAXException {
david_williamse3f46612005-04-07 01:12:35 +0000311 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (fatalError) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
nitindb0f7b262004-11-23 19:12:23 +0000312 }
313
314 public void warning(SAXParseException exception) throws SAXException {
david_williamse3f46612005-04-07 01:12:35 +0000315 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (warning) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
nitindb0f7b262004-11-23 19:12:23 +0000316 }
317 };
318 }
319 return errorHandler;
320 }
321
david_williams757ccfe2005-05-01 08:03:21 +0000322 private Document getParsedDocument() {
nitindb0f7b262004-11-23 19:12:23 +0000323 Document result = null;
324 if (inputStream == null) {
325 File existenceTester = null;
326 if (isJAR())
327 existenceTester = new File(getJarFileName());
328 else
329 existenceTester = new File(getFileName());
330 if (!existenceTester.exists())
331 return null;
332 }
333
334 result = _getParsedDocumentDOM2();
335
336 return result;
337
338 }
339
340 /**
341 * Returns the root Element of the current document
nitind3a1c9712004-12-07 06:47:36 +0000342 *
nitindb0f7b262004-11-23 19:12:23 +0000343 * @return org.w3c.dom.Element
344 */
345 public Node getRootElement() {
346 return getRootElement(getDocument());
347 }
348
349 /**
350 * Returns the/a root Element for the current document
nitind3a1c9712004-12-07 06:47:36 +0000351 *
nitindb0f7b262004-11-23 19:12:23 +0000352 * @return org.w3c.dom.Element
353 */
david_williams757ccfe2005-05-01 08:03:21 +0000354 private Node getRootElement(Document doc) {
nitindb0f7b262004-11-23 19:12:23 +0000355 if (doc == null)
356 return null;
357 if (doc.getDocumentElement() != null)
358 return doc.getDocumentElement();
359 try {
360 Element newRootElement = doc.createElement(getRootElementName());
361 doc.appendChild(newRootElement);
362 return newRootElement;
363 }
364 catch (DOMException e) {
365 Logger.logException(e);
366 }
367 return null;
368 }
369
370 /**
371 *
372 * @return java.lang.String
373 */
374 public java.lang.String getRootElementName() {
375 return rootElementName;
376 }
377
david_williams757ccfe2005-05-01 08:03:21 +0000378 private boolean isJAR() {
nitindb0f7b262004-11-23 19:12:23 +0000379 return getJarFileName() != null;
380 }
381
382 /**
383 * @return
384 */
385 public boolean isValidating() {
386 return fValidating;
387 }
388
nitind24835b92006-01-16 23:20:53 +0000389 void load(boolean createEmptyOnFailure) {
nitind3a1c9712004-12-07 06:47:36 +0000390 // rootElementName and fileName are expected to be defined at this
391 // point
nitindb0f7b262004-11-23 19:12:23 +0000392 document = getParsedDocument();
393 if (document != null) {
nitind3a1c9712004-12-07 06:47:36 +0000394 if (rootElementName != null)
395 rootElement = getRootElement(document);
396 else
397 rootElement = document.getDocumentElement();
nitindb0f7b262004-11-23 19:12:23 +0000398 }
399
400 if (document == null || rootElement == null) {
nitind24835b92006-01-16 23:20:53 +0000401 if (createEmptyOnFailure) {
402 document = getNewDocument();
403 if (document != null) {
404 NodeList children = document.getChildNodes();
nitindb0f7b262004-11-23 19:12:23 +0000405 for (int i = 0; i < children.getLength(); i++) {
nitind24835b92006-01-16 23:20:53 +0000406 if (children.item(i).getNodeType() == Node.ELEMENT_NODE && children.item(i).getNodeName().equals(getRootElementName()))
david_williamscc8918e2005-11-19 20:27:41 +0000407 rootElement = children.item(i);
nitind24835b92006-01-16 23:20:53 +0000408 }
409 if (rootElement == null) {
410 for (int i = 0; i < children.getLength(); i++) {
411 if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
412 rootElement = children.item(i);
413 break;
414 }
nitindb0f7b262004-11-23 19:12:23 +0000415 }
416 }
417 }
418 }
419 }
420 }
421
nitindb0f7b262004-11-23 19:12:23 +0000422 /**
423 * @param string
424 */
425 public void setBaseReference(String string) {
426 fBaseReference = string;
427 }
428
429 /**
430 *
nitind3a1c9712004-12-07 06:47:36 +0000431 * @param newFileName
432 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000433 */
434 public void setFileName(java.lang.String newFileName) {
435 fileName = newFileName;
436 }
437
438 /**
439 * Sets the inputStream for which to provide a Document.
nitind3a1c9712004-12-07 06:47:36 +0000440 *
441 * @param inputStream
442 * The inputStream to set
nitindb0f7b262004-11-23 19:12:23 +0000443 */
nitind63bacdc2005-08-11 03:28:18 +0000444 public void setInputStream(InputStream iStream) {
445 this.inputStream = iStream;
nitindb0f7b262004-11-23 19:12:23 +0000446 }
447
448 /**
449 *
nitind3a1c9712004-12-07 06:47:36 +0000450 * @param newJarFileName
451 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000452 */
453 public void setJarFileName(java.lang.String newJarFileName) {
454 jarFileName = newJarFileName;
455 }
456
457 /**
458 *
nitind3a1c9712004-12-07 06:47:36 +0000459 * @param newRootElementName
460 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000461 */
462 public void setRootElementName(java.lang.String newRootElementName) {
463 rootElementName = newRootElementName;
464 }
465
466 /**
467 * @param b
468 */
469 public void setValidating(boolean b) {
470 fValidating = b;
471 }
nitind0a4d3b52006-11-06 20:31:56 +0000472}