001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.lenya.xml;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.File;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.OutputStream;
026: import java.io.Writer;
027: import java.net.URI;
028: import java.net.URL;
029: import java.util.ArrayList;
030: import java.util.List;
031:
032: import javax.xml.parsers.DocumentBuilder;
033: import javax.xml.parsers.DocumentBuilderFactory;
034: import javax.xml.parsers.ParserConfigurationException;
035: import javax.xml.transform.OutputKeys;
036: import javax.xml.transform.Transformer;
037: import javax.xml.transform.TransformerConfigurationException;
038: import javax.xml.transform.TransformerException;
039: import javax.xml.transform.TransformerFactory;
040: import javax.xml.transform.dom.DOMSource;
041: import javax.xml.transform.stream.StreamResult;
042:
043: import org.apache.xml.resolver.tools.CatalogResolver;
044: import org.w3c.dom.DOMException;
045: import org.w3c.dom.Document;
046: import org.w3c.dom.DocumentType;
047: import org.w3c.dom.Element;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.NodeList;
050: import org.w3c.dom.Text;
051: import org.xml.sax.SAXException;
052:
053: /**
054: * Various utility methods to work with JAXP.
055: * @version $Id: DocumentHelper.java 510066 2007-02-21 15:55:43Z andreas $
056: */
057: public class DocumentHelper {
058: /**
059: * Creates a non-validating and namespace-aware DocumentBuilder.
060: * @return A new DocumentBuilder object.
061: * @throws ParserConfigurationException if an error occurs
062: */
063: public static DocumentBuilder createBuilder()
064: throws ParserConfigurationException {
065: DocumentBuilderFactory factory = DocumentBuilderFactory
066: .newInstance();
067: factory.setNamespaceAware(true);
068: DocumentBuilder builder = factory.newDocumentBuilder();
069:
070: CatalogResolver cr = new CatalogResolver();
071: builder.setEntityResolver(cr);
072: return builder;
073: }
074:
075: /**
076: * Creates a document. A xmlns:prefix="namespaceUri" attribute is added to
077: * the document element.
078: * @param namespaceUri The namespace URL of the root element.
079: * @param qualifiedName The qualified name of the root element.
080: * @param documentType The type of document to be created or null. When
081: * doctype is not null, its Node.ownerDocument attribute is set
082: * to the document being created.
083: * @return A new Document object.
084: * @throws DOMException if an error occurs
085: * @throws ParserConfigurationException if an error occurs
086: * @see org.w3c.dom.DOMImplementation#createDocument(String, String,
087: * DocumentType)
088: */
089: public static Document createDocument(String namespaceUri,
090: String qualifiedName, DocumentType documentType)
091: throws DOMException, ParserConfigurationException {
092: DocumentBuilder builder = createBuilder();
093: Document document = builder.getDOMImplementation()
094: .createDocument(namespaceUri, qualifiedName,
095: documentType);
096:
097: // add xmlns:prefix attribute
098: String name = "xmlns";
099: int index = qualifiedName.indexOf(":");
100:
101: if (index > -1) {
102: name += (":" + qualifiedName.substring(0, index));
103: }
104:
105: document.getDocumentElement().setAttributeNS(
106: "http://www.w3.org/2000/xmlns/", name, namespaceUri);
107:
108: return document;
109: }
110:
111: /**
112: * Reads a document from a file.
113: * @return A document.
114: * @param file The file to load the document from.
115: * @throws ParserConfigurationException if an error occurs
116: * @throws SAXException if an error occurs
117: * @throws IOException if an error occurs
118: */
119: public static Document readDocument(File file)
120: throws ParserConfigurationException, SAXException,
121: IOException {
122: DocumentBuilder builder = createBuilder();
123: return builder.parse(file);
124: }
125:
126: /**
127: * Reads a document from a URL.
128: * @return A document.
129: * @param url The URL to load the document from.
130: * @throws ParserConfigurationException if an error occurs
131: * @throws SAXException if an error occurs
132: * @throws IOException if an error occurs
133: */
134: public static Document readDocument(URL url)
135: throws ParserConfigurationException, SAXException,
136: IOException {
137: DocumentBuilder builder = createBuilder();
138: return builder.parse(url.toString());
139: }
140:
141: /**
142: * Reads a document from a URI.
143: * @return A document.
144: * @param uri The URI to load the document from.
145: * @throws ParserConfigurationException if an error occurs
146: * @throws SAXException if an error occurs
147: * @throws IOException if an error occurs
148: */
149: public static Document readDocument(URI uri)
150: throws ParserConfigurationException, SAXException,
151: IOException {
152: DocumentBuilder builder = createBuilder();
153: return builder.parse(uri.toString());
154: }
155:
156: /**
157: * Reads a document from a string.
158: * @return A document.
159: * @param string The string to load the document from.
160: * @param encoding The encoding which is used by the string.
161: * @throws ParserConfigurationException if an error occurs
162: * @throws SAXException if an error occurs
163: * @throws IOException if an error occurs
164: */
165: public static Document readDocument(String string, String encoding)
166: throws ParserConfigurationException, SAXException,
167: IOException {
168: DocumentBuilder builder = createBuilder();
169: byte bytes[] = string.getBytes(encoding);
170: ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
171: return builder.parse(stream);
172: }
173:
174: /**
175: * Reads a document from an input stream.
176: * @return A document.
177: * @param stream The input stream to load the document from.
178: * @throws ParserConfigurationException if an error occurs
179: * @throws SAXException if an error occurs
180: * @throws IOException if an error occurs
181: */
182: public static Document readDocument(InputStream stream)
183: throws ParserConfigurationException, SAXException,
184: IOException {
185: DocumentBuilder builder = createBuilder();
186: return builder.parse(stream);
187: }
188:
189: /**
190: * Writes a document to a file. A new file is created if it does not exist.
191: * @param document The document to save.
192: * @param file The file to save the document to.
193: * @throws IOException if an error occurs
194: * @throws TransformerConfigurationException if an error occurs
195: * @throws TransformerException if an error occurs
196: */
197: public static void writeDocument(Document document, File file)
198: throws TransformerConfigurationException,
199: TransformerException, IOException {
200: // sanity checks
201: if (document == null)
202: throw new IllegalArgumentException(
203: "illegal usage, parameter document may not be null");
204: if (file == null)
205: throw new IllegalArgumentException(
206: "illegal usage, parameter file may not be null");
207:
208: file.getParentFile().mkdirs();
209: file.createNewFile();
210:
211: DOMSource source = new DOMSource(document);
212: StreamResult result = new StreamResult(file);
213: getTransformer(document.getDoctype()).transform(source, result);
214: }
215:
216: /**
217: * Writes a document to a writer.
218: * @param document The document to write.
219: * @param writer The writer to write the document to.
220: * @throws TransformerConfigurationException if an error occurs
221: * @throws TransformerException if an error occurs
222: */
223: public static void writeDocument(Document document, Writer writer)
224: throws TransformerConfigurationException,
225: TransformerException {
226:
227: // sanity checks
228: if (document == null)
229: throw new IllegalArgumentException(
230: "illegal usage of DocumentHelper::writeDocument(), parameter document may not be null");
231:
232: DOMSource source = new DOMSource(document);
233: StreamResult result = new StreamResult(writer);
234: getTransformer(document.getDoctype()).transform(source, result);
235: }
236:
237: /**
238: * Writes a document to an output stream.
239: * @param document The document to write.
240: * @param outputStream The stream to write the document to.
241: * @throws TransformerConfigurationException if an error occurs
242: * @throws TransformerException if an error occurs
243: */
244: public static void writeDocument(Document document,
245: OutputStream outputStream)
246: throws TransformerConfigurationException,
247: TransformerException {
248:
249: // sanity checks
250: if (document == null)
251: throw new IllegalArgumentException(
252: "illegal usage of DocumentHelper::writeDocument(), parameter document may not be null");
253:
254: DOMSource source = new DOMSource(document);
255: StreamResult result = new StreamResult(outputStream);
256: try {
257: getTransformer(document.getDoctype()).transform(source,
258: result);
259: } finally {
260: try {
261: if (outputStream != null) {
262: outputStream.close();
263: }
264: } catch (Exception ignore) {
265:
266: }
267: }
268: }
269:
270: /**
271: * Get the transformer.
272: * @param documentType the document type
273: * @return a transformer
274: * @throws TransformerConfigurationException if an error occurs
275: */
276: protected static Transformer getTransformer(
277: DocumentType documentType)
278: throws TransformerConfigurationException {
279: TransformerFactory factory = TransformerFactory.newInstance();
280: Transformer transformer = factory.newTransformer();
281: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
282: transformer.setOutputProperty(OutputKeys.METHOD, "xml");
283:
284: if (documentType != null) {
285: if (documentType.getPublicId() != null)
286: transformer.setOutputProperty(
287: OutputKeys.DOCTYPE_PUBLIC, documentType
288: .getPublicId());
289: if (documentType.getSystemId() != null)
290: transformer.setOutputProperty(
291: OutputKeys.DOCTYPE_SYSTEM, documentType
292: .getSystemId());
293: }
294:
295: return transformer;
296: }
297:
298: /**
299: * Creates a document type.
300: * @param qualifiedName The qualified name of the document type.
301: * @param publicId The public identifier.
302: * @param systemId The system identifier.
303: * @return the document type
304: * @throws ParserConfigurationException if an error occurs
305: * @see org.w3c.dom.DOMImplementation#createDocumentType(java.lang.String,
306: * java.lang.String, java.lang.String)
307: */
308: public DocumentType createDocumentType(String qualifiedName,
309: String publicId, String systemId)
310: throws ParserConfigurationException {
311: DocumentBuilder builder = createBuilder();
312:
313: return builder.getDOMImplementation().createDocumentType(
314: qualifiedName, publicId, systemId);
315: }
316:
317: /**
318: * Returns the first child element of an element that belong to a certain
319: * namespace or <code>null</code> if none exists.
320: * @param element The parent element.
321: * @param namespaceUri The namespace that the childen must belong to.
322: * @return The first child element or <code>null</code> if none exists.
323: */
324: public static Element getFirstChild(Element element,
325: String namespaceUri) {
326: return getFirstChild(element, namespaceUri, "*");
327: }
328:
329: /**
330: * Returns the first child element of an element that belongs to a certain
331: * namespace and has a certain local name or <code>null</code> if none
332: * exists.
333: * @param element The parent element.
334: * @param namespaceUri The namespace that the childen must belong to.
335: * @param localName The local name of the children.
336: * @return The child element or <code>null</code> if none exists.
337: */
338: public static Element getFirstChild(Element element,
339: String namespaceUri, String localName) {
340: Element[] children = getChildren(element, namespaceUri,
341: localName);
342:
343: if (children.length > 0) {
344: return children[0];
345: }
346: return null;
347: }
348:
349: /**
350: * Returns all child elements of an element, regardless of the namespace.
351: * @param element The parent element.
352: * @return The child elements.
353: */
354: public static Element[] getChildren(Element element) {
355: List childElements = new ArrayList();
356: NodeList children = element.getElementsByTagName("*");
357:
358: for (int i = 0; i < children.getLength(); i++) {
359: if (children.item(i).getParentNode() == element) {
360: childElements.add(children.item(i));
361: }
362: }
363:
364: return (Element[]) childElements
365: .toArray(new Element[childElements.size()]);
366: }
367:
368: /**
369: * Returns all child elements of an element that belong to a certain
370: * namespace.
371: * @param element The parent element.
372: * @param namespaceUri The namespace that the childen must belong to.
373: * @return The child elements.
374: */
375: public static Element[] getChildren(Element element,
376: String namespaceUri) {
377: return getChildren(element, namespaceUri, "*");
378: }
379:
380: /**
381: * Returns all child elements of an element that belong to a certain
382: * namespace and have a certain local name.
383: * @param element The parent element.
384: * @param namespaceUri The namespace that the childen must belong to.
385: * @param localName The local name of the children.
386: * @return The child elements.
387: */
388: public static Element[] getChildren(Element element,
389: String namespaceUri, String localName) {
390: List childElements = new ArrayList();
391: NodeList children = element.getElementsByTagNameNS(
392: namespaceUri, localName);
393:
394: for (int i = 0; i < children.getLength(); i++) {
395: if (children.item(i).getParentNode() == element) {
396: childElements.add(children.item(i));
397: }
398: }
399:
400: return (Element[]) childElements
401: .toArray(new Element[childElements.size()]);
402: }
403:
404: /**
405: * Returns the text inside an element. Only the child text nodes of this
406: * element are collected.
407: * @param element The element.
408: * @return The text inside the element.
409: */
410: public static String getSimpleElementText(Element element) {
411: StringBuffer buffer = new StringBuffer();
412: NodeList children = element.getChildNodes();
413:
414: for (int i = 0; i < children.getLength(); i++) {
415: Node child = children.item(i);
416:
417: if (child instanceof Text) {
418: buffer.append(child.getNodeValue());
419: }
420: }
421:
422: return buffer.toString();
423: }
424:
425: /**
426: * Replaces all child nodes of an element by a single text node.
427: * @param element The element.
428: * @param text The text to insert.
429: */
430: public static void setSimpleElementText(Element element, String text) {
431: NodeList children = element.getChildNodes();
432:
433: for (int i = 0; i < children.getLength(); i++) {
434: Node child = children.item(i);
435: element.removeChild(child);
436: }
437:
438: Node textNode = element.getOwnerDocument().createTextNode(text);
439: element.appendChild(textNode);
440: }
441:
442: /**
443: * Returns all following sibling elements of an element that belong to a
444: * certain namespace.
445: * @param element The parent element.
446: * @param namespaceUri The namespace that the childen must belong to.
447: * @return The following sibling elements.
448: */
449: public static Element[] getNextSiblings(Element element,
450: String namespaceUri) {
451: return getNextSiblings(element, namespaceUri, "*");
452: }
453:
454: /**
455: * Returns all following sibling elements of an element that belong to a
456: * certain namespace. and have a certain local name.
457: * @param element The parent element.
458: * @param namespaceUri The namespace that the childen must belong to.
459: * @param localName The local name of the children.
460: * @return The following sibling elements.
461: */
462: public static Element[] getNextSiblings(Element element,
463: String namespaceUri, String localName) {
464: List childElements = new ArrayList();
465: Element parent = (Element) element.getParentNode();
466: Element[] children = getChildren(parent, namespaceUri,
467: localName);
468:
469: int l = children.length;
470: for (int i = 0; i < children.length; i++) {
471: if (children[i] == element) {
472: l = i;
473: }
474: if (i > l) {
475: childElements.add(children[i]);
476: }
477: }
478:
479: return (Element[]) childElements
480: .toArray(new Element[childElements.size()]);
481: }
482:
483: /**
484: * Returns all preceding sibling elements of an element that belong to a
485: * certain namespace.
486: * @param element The parent element.
487: * @param namespaceUri The namespace that the childen must belong to.
488: * @return The preceding sibling elements.
489: */
490: public static Element[] getPrecedingSiblings(Element element,
491: String namespaceUri) {
492: return getPrecedingSiblings(element, namespaceUri, "*");
493: }
494:
495: /**
496: * Returns all preceding sibling elements of an element that belong to a
497: * certain namespace. and have a certain local name.
498: * @param element The parent element.
499: * @param namespaceUri The namespace that the childen must belong to.
500: * @param localName The local name of the children.
501: * @return The preceding sibling elements.
502: */
503: public static Element[] getPrecedingSiblings(Element element,
504: String namespaceUri, String localName) {
505: List childElements = new ArrayList();
506: Element parent = (Element) element.getParentNode();
507: Element[] children = getChildren(parent, namespaceUri,
508: localName);
509:
510: int i = 0;
511: while (children[i] != element && i < children.length) {
512: childElements.add(children[i]);
513: i++;
514: }
515:
516: return (Element[]) childElements
517: .toArray(new Element[childElements.size()]);
518: }
519: }
|