| /******************************************************************************* |
| * Copyright (c) 2005, 2012 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0, which accompanies this distribution |
| * and is available at https://www.eclipse.org/legal/epl-2.0/. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.common.utility.internal; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Result; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * The XML APIs are a bit awkward, and they like to declare |
| * "checked" exceptions (boo). This utility class simplifies using those APIs. |
| * In particular, it facilitates the getting and setting of the values |
| * of the children of a particular node (e.g. when reading and writing |
| * the attributes of an object from and to an XML document). |
| */ |
| public final class XMLTools { |
| /** |
| * The DOM parser factory. |
| * Lazily initialized. |
| */ |
| private static DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY; |
| |
| /** |
| * The DOM parser. |
| * Just keep one around and synchronize access to it. |
| * Lazily initialized. |
| */ |
| private static DocumentBuilder DOCUMENT_BUILDER; |
| |
| |
| /** |
| * The transformer factory. |
| * Lazily initialized. |
| */ |
| private static TransformerFactory TRANSFORMER_FACTORY; |
| |
| /** |
| * The transformer. |
| * Just keep one around and synchronize access to it. |
| * Lazily initialized. |
| */ |
| private static Transformer TRANSFORMER; |
| |
| |
| // ********** parsing ********** |
| |
| /** |
| * Build and return an XML document based on the contents |
| * of the specified input source. |
| * {@link DocumentBuilder#parse(InputSource inputSource)} |
| * throws {@link RuntimeException}s. |
| */ |
| public static synchronized Document parse(InputSource inputSource) { |
| try { |
| return getDocumentBuilder().parse(inputSource); |
| } catch (SAXException ex) { |
| throw new RuntimeException(ex); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Build and return an XML document based on the contents |
| * of the specified reader. |
| */ |
| public static Document parse(Reader reader) { |
| Document document = null; |
| try { |
| document = parse(new InputSource(reader)); |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| return document; |
| } |
| |
| /** |
| * Build and return an XML document based on the contents |
| * of the specified input stream. |
| */ |
| public static Document parse(InputStream inputStream) { |
| try { |
| return parse(new InputStreamReader(inputStream, "UTF-8")); //$NON-NLS-1$ |
| } catch (UnsupportedEncodingException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Build and return an XML document based on the contents |
| * of the specified file. |
| */ |
| public static Document parse(File file) { |
| InputStream inputStream; |
| try { |
| inputStream = new BufferedInputStream(new FileInputStream(file), 8192); // 8KB |
| } catch (FileNotFoundException ex) { |
| throw new RuntimeException(ex); |
| } |
| Document document = parse(inputStream); |
| try { |
| inputStream.close(); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| return document; |
| } |
| |
| private static synchronized DocumentBuilder getDocumentBuilder() { |
| if (DOCUMENT_BUILDER == null) { |
| try { |
| DOCUMENT_BUILDER = getDocumentBuilderFactory().newDocumentBuilder(); |
| } catch (ParserConfigurationException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| return DOCUMENT_BUILDER; |
| } |
| |
| /** |
| * See {@link javax.xml.parsers.DocumentBuilderFactory#newInstance()} for |
| * documentation on how the implementation class is determined. |
| */ |
| private static synchronized DocumentBuilderFactory getDocumentBuilderFactory() { |
| if (DOCUMENT_BUILDER_FACTORY == null) { |
| DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); |
| } |
| return DOCUMENT_BUILDER_FACTORY; |
| } |
| |
| |
| // ********** reading DOM ********** |
| |
| /** |
| * Return the child element node of the specified parent node with |
| * the specified name. Return <code>null</code> if the child is not found. |
| */ |
| public static Node getChild(Node parent, String childName) { |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if ((child.getNodeType() == Node.ELEMENT_NODE) && child.getNodeName().equals(childName)) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return all the child element nodes of the specified node. |
| */ |
| public static Iterable<Node> getChildren(Node node) { |
| NodeList children = node.getChildNodes(); |
| int len = children.getLength(); |
| ArrayList<Node> result = new ArrayList<Node>(len); |
| for (int i = 0; i < len; i++) { |
| Node child = children.item(i); |
| if (child.getNodeType() == Node.ELEMENT_NODE) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Return all the child element nodes of the specified node |
| * with the specified name. |
| */ |
| public static Iterable<Node> getChildren(Node node, String childName) { |
| NodeList children = node.getChildNodes(); |
| int len = children.getLength(); |
| ArrayList<Node> result = new ArrayList<Node>(len); |
| for (int i = 0; i < len; i++) { |
| Node child = children.item(i); |
| if ((child.getNodeType() == Node.ELEMENT_NODE) |
| && child.getNodeName().equals(childName)) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Return the text content of the specified node. |
| * Throw an exception if the node is not a "simple" node. |
| */ |
| public static String getTextContent(Node node) { |
| NodeList children = node.getChildNodes(); |
| // <foo></foo> or <foo/> |
| if (children.getLength() == 0) { |
| return StringTools.EMPTY_STRING; |
| } |
| |
| // <foo>bar</foo> |
| if (children.getLength() == 1) { |
| Node child = children.item(0); |
| if (child.getNodeType() == Node.TEXT_NODE) { |
| return node.getFirstChild().getNodeValue(); |
| } |
| } |
| |
| // if this is not a "simple" node, throw an exception |
| throw new IllegalArgumentException(node.getNodeName()); |
| } |
| |
| /** |
| * Return the text content of the specified child node. |
| * The child node must exist (or you will get a {@link NullPointerException}). |
| * <p> |
| * For example, given the following XML: |
| * <pre> |
| * <parent> |
| * <child>Charlie</child> |
| * </parent> |
| * </pre> |
| * <code>XML.getChildTextContent(parentNode, "child")</code> |
| * will return <code>"Charlie"</code>. |
| */ |
| public static String getChildTextContent(Node parent, String childName) { |
| return getTextContent(getChild(parent, childName)); |
| } |
| |
| /** |
| * Return the text content of the specified child node. |
| * If the child node does not exist, return the specified default value. |
| */ |
| public static String getChildTextContent(Node parent, String childName, String defaultValue) { |
| Node child = getChild(parent, childName); |
| return (child == null) ? defaultValue : getTextContent(child); |
| } |
| |
| /** |
| * Return the <code>int</code> content of the specified child node. |
| * The child node must exist (or you will get a {@link NullPointerException}). |
| */ |
| public static int getChildIntContent(Node parent, String childName) { |
| return convertToInt(getTextContent(getChild(parent, childName))); |
| } |
| |
| /** |
| * Return the <code>int</code> content of the specified child node. |
| * If the child node does not exist, return the specified default value. |
| */ |
| public static int childIntContent(Node parent, String childName, int defaultValue) { |
| Node child = getChild(parent, childName); |
| return (child == null) ? defaultValue : convertToInt(getTextContent(child)); |
| } |
| |
| /** |
| * Convert the specified string to an int. |
| */ |
| private static int convertToInt(String string) { |
| return Integer.parseInt(string); |
| } |
| |
| /** |
| * Return the <code>boolean</code> content of the specified child node. |
| * The child node must exist (or you will get a {@link NullPointerException}). |
| */ |
| public static boolean getChildBooleanContent(Node parent, String childName) { |
| return convertToBoolean(getTextContent(getChild(parent, childName))); |
| } |
| |
| /** |
| * Return the <code>boolean</code> content of the specified child node. |
| * If the child node does not exist, return the specified default value. |
| */ |
| public static boolean getChildBooleanContent(Node parent, String childName, boolean defaultValue) { |
| Node child = getChild(parent, childName); |
| return (child == null) ? defaultValue : convertToBoolean(getTextContent(child)); |
| } |
| |
| /** |
| * Convert the specified string to a <code>boolean</code>. |
| */ |
| private static boolean convertToBoolean(String string) { |
| String s = string.toLowerCase(); |
| if (s.equals("t") || s.equals("true") || s.equals("1")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return true; |
| } |
| if (s.equals("f") || s.equals("false") || s.equals("0")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return false; |
| } |
| throw new IllegalArgumentException(string); |
| } |
| |
| |
| // ********** writing DOM ********** |
| |
| /** |
| * Build and return a new document. Once the document has been |
| * built, it can be printed later by calling {@link #print(Document, File)} |
| * or {@link #print(Document, OutputStream)}. |
| */ |
| public static Document newDocument() { |
| return getDocumentBuilder().newDocument(); |
| } |
| |
| /** |
| * Add a simple text node with the specified name and text |
| * to the specified parent node. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, String text) { |
| Document document = parent.getOwnerDocument(); |
| Node child = document.createElement(childName); |
| Node childTextNode = document.createTextNode(text); |
| child.appendChild(childTextNode); |
| parent.appendChild(child); |
| } |
| |
| /** |
| * Add a simple text node with the specified name and text |
| * to the specified parent node. If the text equals the default |
| * value, do not add the simple text node at all. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, String text, String defaultValue) { |
| if ( ! text.equals(defaultValue)) { |
| addSimpleTextNode(parent, childName, text); |
| } |
| } |
| |
| /** |
| * Add a simple text node with the specified name and numeric text |
| * to the specified parent node. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, int text) { |
| addSimpleTextNode(parent, childName, String.valueOf(text)); |
| } |
| |
| /** |
| * Add a simple text node with the specified name and numeric text |
| * to the specified parent node. If numeric text equals the default |
| * value, do not add the simple text node at all. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, int text, int defaultValue) { |
| if (text != defaultValue) { |
| addSimpleTextNode(parent, childName, text); |
| } |
| } |
| |
| /** |
| * Add a simple text node with the specified name and boolean text |
| * to the specified parent node. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, boolean text) { |
| addSimpleTextNode(parent, childName, String.valueOf(text)); |
| } |
| |
| /** |
| * Add a simple text node with the specified name and boolean text |
| * to the specified parent node. If the boolean text equals the default |
| * value, do not add the simple text node at all. |
| */ |
| public static void addSimpleTextNode(Node parent, String childName, boolean text, boolean defaultValue) { |
| if (text != defaultValue) { |
| addSimpleTextNode(parent, childName, text); |
| } |
| } |
| |
| /** |
| * Add a list of simple text nodes with the specified name and text |
| * to the specified parent node's children node. |
| * <p> |
| * For example, the following call: |
| * <pre> |
| * XML.addSimpleTextNodes(parentNode, "children", "child", new String[] {"foo", "bar", "baz"}) |
| * </pre> |
| * will generate the following XML: |
| * <pre> |
| * <parent> |
| * ... |
| * <children> |
| * <child>foo</child> |
| * <child>bar</child> |
| * <child>baz</child> |
| * </children> |
| * ... |
| * </parent> |
| * </pre> |
| */ |
| public static void addSimpleTextNodes(Node parent, String childrenName, String childName, String[] childrenTexts) { |
| Node childrenNode = parent.getOwnerDocument().createElement(childrenName); |
| parent.appendChild(childrenNode); |
| int len = childrenTexts.length; |
| for (int i = 0; i < len; i++) { |
| addSimpleTextNode(childrenNode, childName, childrenTexts[i]); |
| } |
| } |
| |
| /** |
| * Print the specified source to the specified result. |
| */ |
| public static synchronized void print(Source source, Result result) { |
| try { |
| getTransformer().transform(source, result); |
| } catch (TransformerException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Print the specified document to the specified output stream. |
| * Document#print(OutputStream outputStream) |
| */ |
| public static void print(Document document, OutputStream outputStream) { |
| print(new DOMSource(document), new StreamResult(outputStream)); |
| } |
| |
| /** |
| * Print the previously built document to the specified file. |
| * Document#print(File file) |
| */ |
| public static void print(Document document, File file) { |
| OutputStream outputStream; |
| try { |
| outputStream = new BufferedOutputStream(new FileOutputStream(file), 8192); // 8KB |
| } catch (FileNotFoundException ex) { |
| throw new RuntimeException(ex); |
| } |
| print(document, outputStream); |
| try { |
| outputStream.close(); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private static synchronized Transformer getTransformer() { |
| if (TRANSFORMER == null) { |
| try { |
| TRANSFORMER = getTransformerFactory().newTransformer(); |
| } catch (TransformerConfigurationException ex) { |
| throw new RuntimeException(ex); |
| } |
| try { |
| TRANSFORMER.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ |
| } catch (IllegalArgumentException ex) { |
| // ignore exception - the output will still be valid XML, it just won't be very user-friendly |
| } |
| } |
| return TRANSFORMER; |
| } |
| |
| /** |
| * See {@link javax.xml.transform.TransformerFactory#newInstance()} |
| * for documentation on how the implementation class is determined |
| */ |
| private static synchronized TransformerFactory getTransformerFactory() { |
| if (TRANSFORMER_FACTORY == null) { |
| TRANSFORMER_FACTORY = TransformerFactory.newInstance(); |
| } |
| return TRANSFORMER_FACTORY; |
| } |
| |
| |
| // ********** constructor ********** |
| |
| /** |
| * Suppress default constructor, ensuring non-instantiability. |
| */ |
| private XMLTools() { |
| super(); |
| throw new UnsupportedOperationException(); |
| } |
| } |