blob: 2c9c28c4c7dffd350d8beec0f8aa6b2ac0c544b8 [file] [log] [blame]
nitindb0f7b262004-11-23 19:12:23 +00001/*******************************************************************************
nitinda0fde0d2008-04-02 03:56:43 +00002 * Copyright (c) 2004, 2008 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) {
nitindb0f7b262004-11-23 19:12:23 +000099 // parsing exception, notify the user?
nitinded009d22008-03-06 01:21:10 +0000100 Logger.log(Logger.WARNING_DEBUG, "SAXException while reading descriptor " + _getFileName() + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
nitindb0f7b262004-11-23 19:12:23 +0000101 }
102 catch (FileNotFoundException e) {
103 // NOT an "exceptional case"; do not Log
104 }
105 catch (IOException e) {
nitinded009d22008-03-06 01:21:10 +0000106 Logger.log(Logger.WARNING_DEBUG, "IOException while reading descriptor " + _getFileName() + " " + e); //$NON-NLS-1$ //$NON-NLS-2$
nitindb0f7b262004-11-23 19:12:23 +0000107 }
108 finally {
109 if (is != null) {
110 try {
111 is.close();
112 }
113 catch (Exception e) {
114 // what can be done?
115 }
116 }
117 }
118 return result;
119 }
120
121 /**
122 * @return
123 */
124 public String getBaseReference() {
125 return fBaseReference;
126 }
127
128 /**
129 *
130 * @return Document
131 */
132 public Document getDocument() {
nitind24835b92006-01-16 23:20:53 +0000133 return getDocument(true);
134 }
135
136 public Document getDocument(boolean createEmptyOnFailure) {
nitindb0f7b262004-11-23 19:12:23 +0000137 if (document == null)
nitind24835b92006-01-16 23:20:53 +0000138 load(createEmptyOnFailure);
nitindb0f7b262004-11-23 19:12:23 +0000139 return document;
140 }
141
david_williams127329e2008-08-27 04:06:36 +0000142 DocumentBuilder fDocumentBuilder = null;
nitindec519222008-04-09 22:50:09 +0000143
david_williams757ccfe2005-05-01 08:03:21 +0000144 private DocumentBuilder getDocumentBuilder() {
david_williams127329e2008-08-27 04:06:36 +0000145 if (fDocumentBuilder == null) {
146 fDocumentBuilder = CommonXML.getDocumentBuilder(isValidating());
nitindec519222008-04-09 22:50:09 +0000147 }
david_williams127329e2008-08-27 04:06:36 +0000148 return fDocumentBuilder;
nitindb0f7b262004-11-23 19:12:23 +0000149 }
150
david_williams757ccfe2005-05-01 08:03:21 +0000151 private DOMImplementation getDomImplementation() {
nitindb0f7b262004-11-23 19:12:23 +0000152 DocumentBuilder builder = null;
153 try {
154 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
155 }
156 catch (ParserConfigurationException e1) {
157 Logger.logException(e1);
158 }
159 catch (FactoryConfigurationError e1) {
160 Logger.logException(e1);
161 }
162 DOMImplementation impl = builder.getDOMImplementation();
163 return impl;
164 }
165
nitind3a1c9712004-12-07 06:47:36 +0000166 /*************************************************************************
167 * Takes a single string of the form "a/b/c" and ensures that that
168 * structure exists below the head element, down through 'c', and returns
169 * a <em>single</em> element 'c'. For multiple elements (such as
170 * multiple &lt;macro&gt; elements contained within a single
171 * &lt;macros&gt; element, full DOM access is required for searching and
nitindb0f7b262004-11-23 19:12:23 +0000172 * child element manipulation.
nitind3a1c9712004-12-07 06:47:36 +0000173 ************************************************************************/
nitindb0f7b262004-11-23 19:12:23 +0000174 public Element getElement(String name) {
david_williams757ccfe2005-05-01 08:03:21 +0000175 Element result = null;
nitindb0f7b262004-11-23 19:12:23 +0000176 if (document == null)
nitind24835b92006-01-16 23:20:53 +0000177 load(false);
david_williams757ccfe2005-05-01 08:03:21 +0000178 if (document != null) {
179 result = (Element) getNode(getRootElement(), name);
180 }
181 return result;
nitindb0f7b262004-11-23 19:12:23 +0000182 }
183
184 /**
nitind3a1c9712004-12-07 06:47:36 +0000185 * Returns an EntityResolver that won't try to load and resolve ANY
186 * entities
nitindb0f7b262004-11-23 19:12:23 +0000187 */
188 private EntityResolver getEntityResolver() {
189 if (resolver == null) {
190 resolver = new EntityResolver() {
191 public InputSource resolveEntity(String publicID, String systemID) throws SAXException, IOException {
192 InputSource result = null;
nitind6e3e71d2006-02-09 20:39:41 +0000193 if (isValidating()) {
nitindb0f7b262004-11-23 19:12:23 +0000194 try {
195 URL spec = new URL("file://" + getBaseReference()); //$NON-NLS-1$
196 URL url = new URL(spec, systemID);
nitind3a1c9712004-12-07 06:47:36 +0000197 if (url.getProtocol().startsWith("file:")) { //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000198 URLConnection connection = url.openConnection();
199 result = new InputSource(systemID != null ? systemID : "/_" + toString()); //$NON-NLS-1$
200 result.setPublicId(publicID);
201 result.setByteStream(connection.getInputStream());
202 }
203 }
204 catch (Exception e) {
205 result = null;
206 }
207 }
nitind6e3e71d2006-02-09 20:39:41 +0000208
nitindb0f7b262004-11-23 19:12:23 +0000209 if (result == null) {
nitind6e3e71d2006-02-09 20:39:41 +0000210 result = new InputSource(new ByteArrayInputStream(new byte[0]));
nitindb0f7b262004-11-23 19:12:23 +0000211 result.setPublicId(publicID);
212 result.setSystemId(systemID != null ? systemID : "/_" + getClass().getName()); //$NON-NLS-1$
213 }
214 return result;
215 }
216 };
217 }
218 return resolver;
219 }
220
221 /**
222 *
223 * @return java.lang.String
224 */
nitind21f317d2005-02-17 23:52:35 +0000225 public String getFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000226 return fileName;
227 }
228
229 /**
nitind3a1c9712004-12-07 06:47:36 +0000230 * Returns and input stream to use as the source of the Document 1) from
231 * an InputStream set on this instance 2) from a JAR file with the given
232 * entry name 3) from a normal file
nitindb0f7b262004-11-23 19:12:23 +0000233 *
234 * @return InputStream
235 */
236 public InputStream getInputStream() throws FileNotFoundException {
237 if (inputStream != null)
238 return inputStream;
239 else if (isJAR()) {
240 return JarUtilities.getInputStream(getJarFileName(), getFileName());
241 }
242 else {
nitind0a4d3b52006-11-06 20:31:56 +0000243 return new BufferedInputStream(new FileInputStream(getFileName()));
nitindb0f7b262004-11-23 19:12:23 +0000244 }
245 }
246
247 /**
248 *
249 * @return java.lang.String
250 */
nitind21f317d2005-02-17 23:52:35 +0000251 public String getJarFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000252 return jarFileName;
253 }
254
david_williams757ccfe2005-05-01 08:03:21 +0000255 private Node getNamedChild(Node parent, String childName) {
nitindb0f7b262004-11-23 19:12:23 +0000256 if (parent == null) {
257 return null;
258 }
259 NodeList childList = parent.getChildNodes();
260 for (int i = 0; i < childList.getLength(); i++) {
261 if (childList.item(i).getNodeName().equals(childName))
262 return childList.item(i);
263 }
264 return null;
265 }
266
david_williams757ccfe2005-05-01 08:03:21 +0000267 private Document getNewDocument() {
nitindb0f7b262004-11-23 19:12:23 +0000268 Document result = null;
269 try {
nitind7228c6e2005-08-03 21:36:32 +0000270 result = getDomImplementation().createDocument("http://www.w3.org/XML/1998/namespace", getRootElementName(), null); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000271 NodeList children = result.getChildNodes();
272 for (int i = 0; i < children.getLength(); i++) {
273 result.removeChild(children.item(i));
274 }
275 // we're going through this effort to avoid a NS element
nitind73e76412005-11-10 01:50:19 +0000276 Element settings = result.createElementNS("http://www.w3.org/XML/1998/namespace", getRootElementName()); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000277 result.appendChild(settings);
278 return result;
279 }
280 catch (DOMException e) {
281 Logger.logException(e);
282 }
283 return null;
284 }
285
nitind3a1c9712004-12-07 06:47:36 +0000286 /*************************************************************************
287 * Takes a single string of the form "a/b/c" and ensures that that
288 * structure exists below the head element, down through 'c', and returns
289 * the element 'c'.
290 ************************************************************************/
david_williams757ccfe2005-05-01 08:03:21 +0000291 private Node getNode(Node node, String name) {
nitindb0f7b262004-11-23 19:12:23 +0000292 StringTokenizer tokenizer = new StringTokenizer(name, "/"); //$NON-NLS-1$
293 String token = null;
294 while (tokenizer.hasMoreTokens()) {
295 token = tokenizer.nextToken();
296 if (getNamedChild(node, token) == null)
297 node.appendChild(document.createElement(token));
298 node = getNamedChild(node, token);
299 }
300 return node;
301 }
302
303 /**
nitind3a1c9712004-12-07 06:47:36 +0000304 * Returns an ErrorHandler that will not stop the parser on reported
305 * errors
nitindb0f7b262004-11-23 19:12:23 +0000306 */
307 private ErrorHandler getNullErrorHandler() {
308 if (errorHandler == null) {
309 errorHandler = new ErrorHandler() {
310 public void error(SAXParseException exception) throws SAXException {
nitinda0fde0d2008-04-02 03:56:43 +0000311 Logger.log(Logger.WARNING, "SAXParseException with " + fBaseReference + "/" + getJarFileName() + "/" + getFileName() + " (error) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
nitindb0f7b262004-11-23 19:12:23 +0000312 }
313
314 public void fatalError(SAXParseException exception) throws SAXException {
nitinda0fde0d2008-04-02 03:56:43 +0000315 Logger.log(Logger.WARNING, "SAXParseException with " + fBaseReference + "/" + getJarFileName() + "/" + getFileName() + " (fatalError) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
nitindb0f7b262004-11-23 19:12:23 +0000316 }
317
318 public void warning(SAXParseException exception) throws SAXException {
nitinda0fde0d2008-04-02 03:56:43 +0000319 Logger.log(Logger.WARNING, "SAXParseException with " + fBaseReference + "/" + getJarFileName() + "/" + getFileName() + " (warning) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
nitindb0f7b262004-11-23 19:12:23 +0000320 }
321 };
322 }
323 return errorHandler;
324 }
325
david_williams757ccfe2005-05-01 08:03:21 +0000326 private Document getParsedDocument() {
nitindb0f7b262004-11-23 19:12:23 +0000327 Document result = null;
328 if (inputStream == null) {
329 File existenceTester = null;
330 if (isJAR())
331 existenceTester = new File(getJarFileName());
332 else
333 existenceTester = new File(getFileName());
334 if (!existenceTester.exists())
335 return null;
336 }
337
338 result = _getParsedDocumentDOM2();
339
340 return result;
341
342 }
343
344 /**
345 * Returns the root Element of the current document
nitind3a1c9712004-12-07 06:47:36 +0000346 *
nitindb0f7b262004-11-23 19:12:23 +0000347 * @return org.w3c.dom.Element
348 */
349 public Node getRootElement() {
350 return getRootElement(getDocument());
351 }
352
353 /**
354 * Returns the/a root Element for the current document
nitind3a1c9712004-12-07 06:47:36 +0000355 *
nitindb0f7b262004-11-23 19:12:23 +0000356 * @return org.w3c.dom.Element
357 */
david_williams757ccfe2005-05-01 08:03:21 +0000358 private Node getRootElement(Document doc) {
nitindb0f7b262004-11-23 19:12:23 +0000359 if (doc == null)
360 return null;
361 if (doc.getDocumentElement() != null)
362 return doc.getDocumentElement();
363 try {
364 Element newRootElement = doc.createElement(getRootElementName());
365 doc.appendChild(newRootElement);
366 return newRootElement;
367 }
368 catch (DOMException e) {
369 Logger.logException(e);
370 }
371 return null;
372 }
373
374 /**
375 *
376 * @return java.lang.String
377 */
378 public java.lang.String getRootElementName() {
379 return rootElementName;
380 }
381
david_williams757ccfe2005-05-01 08:03:21 +0000382 private boolean isJAR() {
nitindb0f7b262004-11-23 19:12:23 +0000383 return getJarFileName() != null;
384 }
385
386 /**
387 * @return
388 */
389 public boolean isValidating() {
390 return fValidating;
391 }
392
nitind24835b92006-01-16 23:20:53 +0000393 void load(boolean createEmptyOnFailure) {
nitind3a1c9712004-12-07 06:47:36 +0000394 // rootElementName and fileName are expected to be defined at this
395 // point
nitindb0f7b262004-11-23 19:12:23 +0000396 document = getParsedDocument();
397 if (document != null) {
nitind3a1c9712004-12-07 06:47:36 +0000398 if (rootElementName != null)
399 rootElement = getRootElement(document);
400 else
401 rootElement = document.getDocumentElement();
nitindb0f7b262004-11-23 19:12:23 +0000402 }
403
404 if (document == null || rootElement == null) {
nitind24835b92006-01-16 23:20:53 +0000405 if (createEmptyOnFailure) {
406 document = getNewDocument();
407 if (document != null) {
408 NodeList children = document.getChildNodes();
nitindb0f7b262004-11-23 19:12:23 +0000409 for (int i = 0; i < children.getLength(); i++) {
nitind24835b92006-01-16 23:20:53 +0000410 if (children.item(i).getNodeType() == Node.ELEMENT_NODE && children.item(i).getNodeName().equals(getRootElementName()))
david_williamscc8918e2005-11-19 20:27:41 +0000411 rootElement = children.item(i);
nitind24835b92006-01-16 23:20:53 +0000412 }
413 if (rootElement == null) {
414 for (int i = 0; i < children.getLength(); i++) {
415 if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
416 rootElement = children.item(i);
417 break;
418 }
nitindb0f7b262004-11-23 19:12:23 +0000419 }
420 }
421 }
422 }
423 }
424 }
425
nitindb0f7b262004-11-23 19:12:23 +0000426 /**
427 * @param string
428 */
429 public void setBaseReference(String string) {
430 fBaseReference = string;
431 }
432
433 /**
434 *
nitind3a1c9712004-12-07 06:47:36 +0000435 * @param newFileName
436 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000437 */
438 public void setFileName(java.lang.String newFileName) {
439 fileName = newFileName;
440 }
441
442 /**
443 * Sets the inputStream for which to provide a Document.
nitind3a1c9712004-12-07 06:47:36 +0000444 *
445 * @param inputStream
446 * The inputStream to set
nitindb0f7b262004-11-23 19:12:23 +0000447 */
nitind63bacdc2005-08-11 03:28:18 +0000448 public void setInputStream(InputStream iStream) {
449 this.inputStream = iStream;
nitindb0f7b262004-11-23 19:12:23 +0000450 }
451
452 /**
453 *
nitind3a1c9712004-12-07 06:47:36 +0000454 * @param newJarFileName
455 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000456 */
457 public void setJarFileName(java.lang.String newJarFileName) {
458 jarFileName = newJarFileName;
459 }
460
461 /**
462 *
nitind3a1c9712004-12-07 06:47:36 +0000463 * @param newRootElementName
464 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000465 */
466 public void setRootElementName(java.lang.String newRootElementName) {
467 rootElementName = newRootElementName;
468 }
469
470 /**
471 * @param b
472 */
473 public void setValidating(boolean b) {
474 fValidating = b;
475 }
nitind0a4d3b52006-11-06 20:31:56 +0000476}