blob: 1b0fbac125f27c3611896a0c38eda44ede7679f8 [file] [log] [blame]
nitindb0f7b262004-11-23 19:12:23 +00001/*******************************************************************************
2 * Copyright (c) 2004 IBM Corporation and others.
3 * 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
7 *
8 * 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
14
15import java.io.File;
16import java.io.FileInputStream;
17import java.io.FileNotFoundException;
18import java.io.FileOutputStream;
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.io.StringReader;
23import java.net.URL;
24import java.net.URLConnection;
25import java.util.StringTokenizer;
26
27import javax.xml.parsers.DocumentBuilder;
28import javax.xml.parsers.DocumentBuilderFactory;
29import javax.xml.parsers.FactoryConfigurationError;
30import javax.xml.parsers.ParserConfigurationException;
31
david_williams425ffe72004-12-07 21:46:39 +000032import org.eclipse.jst.jsp.core.internal.Logger;
nitindb0f7b262004-11-23 19:12:23 +000033import org.eclipse.wst.sse.core.exceptions.SourceEditingRuntimeException;
34import org.eclipse.wst.sse.core.util.JarUtilities;
35import org.w3c.dom.DOMException;
36import org.w3c.dom.DOMImplementation;
37import org.w3c.dom.Document;
38import org.w3c.dom.Element;
39import org.w3c.dom.Node;
40import org.w3c.dom.NodeList;
41import org.xml.sax.EntityResolver;
42import org.xml.sax.ErrorHandler;
43import org.xml.sax.InputSource;
44import org.xml.sax.SAXException;
45import org.xml.sax.SAXParseException;
46
47/**
nitind3a1c9712004-12-07 06:47:36 +000048 * An XML Creator/Reader/Writer that 1) Ignores any DocumentType Nodes found
49 * within the document (as well as any entities) 2) Ignores any
50 * errors/exceptions from Xerces when loading a document 3) Can load Documents
51 * from within a .JAR file (***read-only***)
nitindb0f7b262004-11-23 19:12:23 +000052 */
53
54public class DocumentProvider {
nitindb0f7b262004-11-23 19:12:23 +000055 protected Document document = null;
56 protected ErrorHandler errorHandler = null;
57 private String fBaseReference;
58 protected String fileName = null;
59 protected boolean fValidating = true;
60 protected InputStream inputStream = null;
61 protected String jarFileName = null;
62 protected EntityResolver resolver = null;
63
64 protected Node rootElement = null;
65 protected String rootElementName = null;
66
67 public DocumentProvider() {
68 }
69
70 protected Document _getParsedDocumentDOM2() {
71 Document result = null;
72
73 InputStream is = null;
74 try {
75 DocumentBuilder builder = getDocumentBuilder();
nitind3a1c9712004-12-07 06:47:36 +000076 // DOMParser parser = new DOMParser();
77 // parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error",
78 // false);//$NON-NLS-1$
79 // parser.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
80 // false);//$NON-NLS-1$
81 // parser.setErrorHandler(getNullErrorHandler());
82 // parser.setEntityResolver(getNullEntityResolver());
83 // is = getInputStream();
nitindb0f7b262004-11-23 19:12:23 +000084 builder.setEntityResolver(getEntityResolver());
85 builder.setErrorHandler(getNullErrorHandler());
86 is = getInputStream();
87 if (is != null)
88 result = builder.parse(is, getBaseReference());
89 }
90 catch (SAXException e) {
91 result = null;
92 // parsing exception, notify the user?
nitind3a1c9712004-12-07 06:47:36 +000093 Logger.log(Logger.WARNING, "SAXException while reading descriptor: " + e); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +000094 }
95 catch (FileNotFoundException e) {
96 // NOT an "exceptional case"; do not Log
97 }
98 catch (IOException e) {
99 Logger.log(Logger.WARNING, "IOException while reading descriptor" + e); //$NON-NLS-1$
100 }
101 finally {
102 if (is != null) {
103 try {
104 is.close();
105 }
106 catch (Exception e) {
107 // what can be done?
108 }
109 }
110 }
111 return result;
112 }
113
114 /**
115 * @return
116 */
117 public String getBaseReference() {
118 return fBaseReference;
119 }
120
121 /**
122 *
123 * @return Document
124 */
125 public Document getDocument() {
126 if (document == null)
127 load();
128 return document;
129 }
130
131 protected DocumentBuilder getDocumentBuilder() {
132 return CommonXML.getDocumentBuilder(isValidating());
133 }
134
135 protected DOMImplementation getDomImplementation() {
136 DocumentBuilder builder = null;
137 try {
138 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
139 }
140 catch (ParserConfigurationException e1) {
141 Logger.logException(e1);
142 }
143 catch (FactoryConfigurationError e1) {
144 Logger.logException(e1);
145 }
146 DOMImplementation impl = builder.getDOMImplementation();
147 return impl;
148 }
149
nitind3a1c9712004-12-07 06:47:36 +0000150 /*************************************************************************
151 * Takes a single string of the form "a/b/c" and ensures that that
152 * structure exists below the head element, down through 'c', and returns
153 * a <em>single</em> element 'c'. For multiple elements (such as
154 * multiple &lt;macro&gt; elements contained within a single
155 * &lt;macros&gt; element, full DOM access is required for searching and
nitindb0f7b262004-11-23 19:12:23 +0000156 * child element manipulation.
nitind3a1c9712004-12-07 06:47:36 +0000157 ************************************************************************/
nitindb0f7b262004-11-23 19:12:23 +0000158 public Element getElement(String name) {
159 if (document == null)
160 load();
161 if (document != null)
162 return (Element) getNode(getRootElement(), name);
163 else
164 return null;
165 }
166
167 /**
nitind3a1c9712004-12-07 06:47:36 +0000168 * Returns an EntityResolver that won't try to load and resolve ANY
169 * entities
nitindb0f7b262004-11-23 19:12:23 +0000170 */
171 private EntityResolver getEntityResolver() {
172 if (resolver == null) {
173 resolver = new EntityResolver() {
174 public InputSource resolveEntity(String publicID, String systemID) throws SAXException, IOException {
175 InputSource result = null;
176 if (getBaseReference() != null) {
177 try {
178 URL spec = new URL("file://" + getBaseReference()); //$NON-NLS-1$
179 URL url = new URL(spec, systemID);
nitind3a1c9712004-12-07 06:47:36 +0000180 if (url.getProtocol().startsWith("file:")) { //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000181 URLConnection connection = url.openConnection();
182 result = new InputSource(systemID != null ? systemID : "/_" + toString()); //$NON-NLS-1$
183 result.setPublicId(publicID);
184 result.setByteStream(connection.getInputStream());
185 }
186 }
187 catch (Exception e) {
188 result = null;
189 }
190 }
191 if (result == null) {
192 result = new InputSource(new StringReader("")); //$NON-NLS-1$
193 result.setPublicId(publicID);
194 result.setSystemId(systemID != null ? systemID : "/_" + getClass().getName()); //$NON-NLS-1$
195 }
196 return result;
197 }
198 };
199 }
200 return resolver;
201 }
202
203 /**
204 *
205 * @return java.lang.String
206 */
nitind21f317d2005-02-17 23:52:35 +0000207 public String getFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000208 return fileName;
209 }
210
211 /**
nitind3a1c9712004-12-07 06:47:36 +0000212 * Returns and input stream to use as the source of the Document 1) from
213 * an InputStream set on this instance 2) from a JAR file with the given
214 * entry name 3) from a normal file
nitindb0f7b262004-11-23 19:12:23 +0000215 *
216 * @return InputStream
217 */
218 public InputStream getInputStream() throws FileNotFoundException {
219 if (inputStream != null)
220 return inputStream;
221 else if (isJAR()) {
222 return JarUtilities.getInputStream(getJarFileName(), getFileName());
223 }
224 else {
225 return new FileInputStream(getFileName());
226 }
227 }
228
229 /**
230 *
231 * @return java.lang.String
232 */
nitind21f317d2005-02-17 23:52:35 +0000233 public String getJarFileName() {
nitindb0f7b262004-11-23 19:12:23 +0000234 return jarFileName;
235 }
236
237 protected Node getNamedChild(Node parent, String childName) {
238 if (parent == null) {
239 return null;
240 }
241 NodeList childList = parent.getChildNodes();
242 for (int i = 0; i < childList.getLength(); i++) {
243 if (childList.item(i).getNodeName().equals(childName))
244 return childList.item(i);
245 }
246 return null;
247 }
248
249 protected Document getNewDocument() {
250 Document result = null;
251 try {
252 result = getDomImplementation().createDocument("", getRootElementName(), null); //$NON-NLS-1$
253 NodeList children = result.getChildNodes();
254 for (int i = 0; i < children.getLength(); i++) {
255 result.removeChild(children.item(i));
256 }
257 // we're going through this effort to avoid a NS element
258 Element settings = result.createElement(getRootElementName());
259 result.appendChild(settings);
260 return result;
261 }
262 catch (DOMException e) {
263 Logger.logException(e);
264 }
265 return null;
266 }
267
nitind3a1c9712004-12-07 06:47:36 +0000268 /*************************************************************************
269 * Takes a single string of the form "a/b/c" and ensures that that
270 * structure exists below the head element, down through 'c', and returns
271 * the element 'c'.
272 ************************************************************************/
nitindb0f7b262004-11-23 19:12:23 +0000273 protected Node getNode(Node node, String name) {
274 StringTokenizer tokenizer = new StringTokenizer(name, "/"); //$NON-NLS-1$
275 String token = null;
276 while (tokenizer.hasMoreTokens()) {
277 token = tokenizer.nextToken();
278 if (getNamedChild(node, token) == null)
279 node.appendChild(document.createElement(token));
280 node = getNamedChild(node, token);
281 }
282 return node;
283 }
284
285 /**
nitind3a1c9712004-12-07 06:47:36 +0000286 * Returns an ErrorHandler that will not stop the parser on reported
287 * errors
nitindb0f7b262004-11-23 19:12:23 +0000288 */
289 private ErrorHandler getNullErrorHandler() {
290 if (errorHandler == null) {
291 errorHandler = new ErrorHandler() {
292 public void error(SAXParseException exception) throws SAXException {
nitind8b436442005-02-16 17:39:13 +0000293 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (error) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000294 }
295
296 public void fatalError(SAXParseException exception) throws SAXException {
nitind8b436442005-02-16 17:39:13 +0000297 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (fatalError) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000298 }
299
300 public void warning(SAXParseException exception) throws SAXException {
nitind8b436442005-02-16 17:39:13 +0000301 Logger.log(Logger.WARNING, "SAXParseException with " + getJarFileName() + "/" + getFileName() + " (warning) while reading descriptor: " + exception.getMessage()); //$NON-NLS-1$
nitindb0f7b262004-11-23 19:12:23 +0000302 }
303 };
304 }
305 return errorHandler;
306 }
307
308 protected Document getParsedDocument() {
309 Document result = null;
310 if (inputStream == null) {
311 File existenceTester = null;
312 if (isJAR())
313 existenceTester = new File(getJarFileName());
314 else
315 existenceTester = new File(getFileName());
316 if (!existenceTester.exists())
317 return null;
318 }
319
320 result = _getParsedDocumentDOM2();
321
322 return result;
323
324 }
325
326 /**
327 * Returns the root Element of the current document
nitind3a1c9712004-12-07 06:47:36 +0000328 *
nitindb0f7b262004-11-23 19:12:23 +0000329 * @return org.w3c.dom.Element
330 */
331 public Node getRootElement() {
332 return getRootElement(getDocument());
333 }
334
335 /**
336 * Returns the/a root Element for the current document
nitind3a1c9712004-12-07 06:47:36 +0000337 *
nitindb0f7b262004-11-23 19:12:23 +0000338 * @return org.w3c.dom.Element
339 */
340 protected Node getRootElement(Document doc) {
341 if (doc == null)
342 return null;
343 if (doc.getDocumentElement() != null)
344 return doc.getDocumentElement();
345 try {
346 Element newRootElement = doc.createElement(getRootElementName());
347 doc.appendChild(newRootElement);
348 return newRootElement;
349 }
350 catch (DOMException e) {
351 Logger.logException(e);
352 }
353 return null;
354 }
355
356 /**
357 *
358 * @return java.lang.String
359 */
360 public java.lang.String getRootElementName() {
361 return rootElementName;
362 }
363
364 protected boolean isJAR() {
365 return getJarFileName() != null;
366 }
367
368 /**
369 * @return
370 */
371 public boolean isValidating() {
372 return fValidating;
373 }
374
375 public void load() {
nitind3a1c9712004-12-07 06:47:36 +0000376 // rootElementName and fileName are expected to be defined at this
377 // point
nitindb0f7b262004-11-23 19:12:23 +0000378 document = getParsedDocument();
379 if (document != null) {
nitind3a1c9712004-12-07 06:47:36 +0000380 if (rootElementName != null)
381 rootElement = getRootElement(document);
382 else
383 rootElement = document.getDocumentElement();
nitindb0f7b262004-11-23 19:12:23 +0000384 }
385
386 if (document == null || rootElement == null) {
387 document = getNewDocument();
388 if (document != null) {
389 NodeList children = document.getChildNodes();
390 for (int i = 0; i < children.getLength(); i++) {
391 if (children.item(i).getNodeType() == Node.ELEMENT_NODE && children.item(i).getNodeName().equals(getRootElementName()))
392 rootElement = (Element) children.item(i);
393 }
394 if (rootElement == null) {
395 for (int i = 0; i < children.getLength(); i++) {
396 if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
397 rootElement = (Element) children.item(i);
398 break;
399 }
400 }
401 }
402 }
403 }
404 }
405
406 protected void saveDocument(Document odocument, OutputStream stream) throws IOException {
407 CommonXML.serialize(odocument, stream);
408 }
409
410 /**
411 * @param string
412 */
413 public void setBaseReference(String string) {
414 fBaseReference = string;
415 }
416
417 /**
418 *
nitind3a1c9712004-12-07 06:47:36 +0000419 * @param newFileName
420 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000421 */
422 public void setFileName(java.lang.String newFileName) {
423 fileName = newFileName;
424 }
425
426 /**
427 * Sets the inputStream for which to provide a Document.
nitind3a1c9712004-12-07 06:47:36 +0000428 *
429 * @param inputStream
430 * The inputStream to set
nitindb0f7b262004-11-23 19:12:23 +0000431 */
432 public void setInputStream(InputStream inputStream) {
433 this.inputStream = inputStream;
434 }
435
436 /**
437 *
nitind3a1c9712004-12-07 06:47:36 +0000438 * @param newJarFileName
439 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000440 */
441 public void setJarFileName(java.lang.String newJarFileName) {
442 jarFileName = newJarFileName;
443 }
444
445 /**
446 *
nitind3a1c9712004-12-07 06:47:36 +0000447 * @param newRootElementName
448 * java.lang.String
nitindb0f7b262004-11-23 19:12:23 +0000449 */
450 public void setRootElementName(java.lang.String newRootElementName) {
451 rootElementName = newRootElementName;
452 }
453
454 /**
455 * @param b
456 */
457 public void setValidating(boolean b) {
458 fValidating = b;
459 }
460
461 public void store() {
462 if (isJAR())
463 return;
464
465 if (rootElement == null) {
466 document = getNewDocument();
467 rootElement = document.getDocumentElement();
468 }
469
470 try {
471 OutputStream ostream = new FileOutputStream(getFileName());
472
473 storeDocument(document, ostream);
474
475 ostream.flush();
476 ostream.close();
477 }
478 catch (IOException e) {
479 Logger.logException("Exception saving document " + getFileName(), e); //$NON-NLS-1$
480 throw new SourceEditingRuntimeException(e);
481 }
482 }
483
484 protected void storeDocument(Document odocument, OutputStream ostream) {
485 try {
486 saveDocument(odocument, ostream);
487 }
488 catch (IOException e) {
489 Logger.logException(e);
490 }
491 }
492}