aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java')
-rw-r--r--third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java273
1 files changed, 273 insertions, 0 deletions
diff --git a/third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java b/third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java
new file mode 100644
index 0000000000..14776ea0b8
--- /dev/null
+++ b/third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java
@@ -0,0 +1,273 @@
+/*
+ * plist - An open source library to parse and generate property lists
+ * Copyright (C) 2014 Daniel Dreibrodt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.dd.plist;
+
+import org.w3c.dom.*;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parses XML property lists.
+ *
+ * @author Daniel Dreibrodt
+ */
+public class XMLPropertyListParser {
+
+ /**
+ * Instantiation is prohibited by outside classes.
+ */
+ protected XMLPropertyListParser() {
+ /** empty **/
+ }
+
+ private static DocumentBuilderFactory docBuilderFactory = null;
+
+ /**
+ * Initialize the document builder factory so that it can be reuused and does not need to
+ * be reinitialized for each new parsing.
+ */
+ private static synchronized void initDocBuilderFactory() {
+ docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setIgnoringComments(true);
+ docBuilderFactory.setCoalescing(true);
+ }
+
+ /**
+ * Gets a DocumentBuilder to parse a XML property list.
+ * As DocumentBuilders are not thread-safe a new DocBuilder is generated for each request.
+ *
+ * @return A new DocBuilder that can parse property lists w/o an internet connection.
+ */
+ private static synchronized DocumentBuilder getDocBuilder() throws ParserConfigurationException {
+ if (docBuilderFactory == null)
+ initDocBuilderFactory();
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ docBuilder.setEntityResolver(new EntityResolver() {
+ public InputSource resolveEntity(String publicId, String systemId) {
+ if ("-//Apple Computer//DTD PLIST 1.0//EN".equals(publicId) || // older publicId
+ "-//Apple//DTD PLIST 1.0//EN".equals(publicId)) { // newer publicId
+ // return a dummy, zero length DTD so we don't have to fetch
+ // it from the network.
+ return new InputSource(new ByteArrayInputStream(new byte[0]));
+ }
+ return null;
+ }
+ });
+ return docBuilder;
+ }
+
+ /**
+ * Parses a XML property list file.
+ *
+ * @param f The XML property list file.
+ * @return The root object of the property list. This is usally a NSDictionary but can also be a NSArray.
+ * @throws Exception When an error occurs during parsing.
+ * @see javax.xml.parsers.DocumentBuilder#parse(java.io.File)
+ */
+ public static NSObject parse(File f) throws ParseException, IOException, PropertyListFormatException, SAXException, ParserConfigurationException {
+ DocumentBuilder docBuilder = getDocBuilder();
+
+ Document doc = docBuilder.parse(f);
+
+ return parseDocument(doc);
+ }
+
+ /**
+ * Parses a XML property list from a byte array.
+ *
+ * @param bytes The byte array containing the property list's data.
+ * @return The root object of the property list. This is usally a NSDictionary but can also be a NSArray.
+ * @throws Exception When an error occurs during parsing.
+ */
+ public static NSObject parse(final byte[] bytes) throws ParserConfigurationException, ParseException, SAXException, PropertyListFormatException, IOException {
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ return parse(bis);
+ }
+
+ /**
+ * Parses a XML property list from an input stream.
+ *
+ * @param is The input stream pointing to the property list's data.
+ * @return The root object of the property list. This is usally a NSDictionary but can also be a NSArray.
+ * @throws Exception When an error occurs during parsing.
+ * @see javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream)
+ */
+ public static NSObject parse(InputStream is) throws ParserConfigurationException, IOException, SAXException, PropertyListFormatException, ParseException {
+ DocumentBuilder docBuilder = getDocBuilder();
+
+ Document doc = docBuilder.parse(is);
+
+ return parseDocument(doc);
+ }
+
+ /**
+ * Parses the XML document by generating the appropriate NSObjects for each XML node.
+ *
+ * @param doc The XML document.
+ * @return The root NSObject of the property list contained in the XML document.
+ * @throws Exception If an error occured during parsing.
+ */
+ private static NSObject parseDocument(Document doc) throws PropertyListFormatException, IOException, ParseException {
+ DocumentType docType = doc.getDoctype();
+ if (docType == null) {
+ if (!doc.getDocumentElement().getNodeName().equals("plist")) {
+ throw new UnsupportedOperationException("The given XML document is not a property list.");
+ }
+ } else if (!docType.getName().equals("plist")) {
+ throw new UnsupportedOperationException("The given XML document is not a property list.");
+ }
+
+ Node rootNode;
+
+ if (doc.getDocumentElement().getNodeName().equals("plist")) {
+ //Root element wrapped in plist tag
+ List<Node> rootNodes = filterElementNodes(doc.getDocumentElement().getChildNodes());
+ if (rootNodes.isEmpty()) {
+ throw new PropertyListFormatException("The given XML property list has no root element!");
+ } else if (rootNodes.size() == 1) {
+ rootNode = rootNodes.get(0);
+ } else {
+ throw new PropertyListFormatException("The given XML property list has more than one root element!");
+ }
+ } else {
+ //Root NSObject not wrapped in plist-tag
+ rootNode = doc.getDocumentElement();
+ }
+
+ return parseObject(rootNode);
+ }
+
+ /**
+ * Parses a node in the XML structure and returns the corresponding NSObject
+ *
+ * @param n The XML node.
+ * @return The corresponding NSObject.
+ * @throws Exception If an error occured during parsing the node.
+ */
+ private static NSObject parseObject(Node n) throws ParseException, IOException {
+ String type = n.getNodeName();
+ if (type.equals("dict")) {
+ NSDictionary dict = new NSDictionary();
+ List<Node> children = filterElementNodes(n.getChildNodes());
+ for (int i = 0; i < children.size(); i += 2) {
+ Node key = children.get(i);
+ Node val = children.get(i + 1);
+
+ String keyString = getNodeTextContents(key);
+
+ dict.put(keyString, parseObject(val));
+ }
+ return dict;
+ } else if (type.equals("array")) {
+ List<Node> children = filterElementNodes(n.getChildNodes());
+ NSArray array = new NSArray(children.size());
+ for (int i = 0; i < children.size(); i++) {
+ array.setValue(i, parseObject(children.get(i)));
+ }
+ return array;
+ } else if (type.equals("true")) {
+ return new NSNumber(true);
+ } else if (type.equals("false")) {
+ return new NSNumber(false);
+ } else if (type.equals("integer")) {
+ return new NSNumber(getNodeTextContents(n));
+ } else if (type.equals("real")) {
+ return new NSNumber(getNodeTextContents(n));
+ } else if (type.equals("string")) {
+ return new NSString(getNodeTextContents(n));
+ } else if (type.equals("data")) {
+ return new NSData(getNodeTextContents(n));
+ } else if (type.equals("date")) {
+ return new NSDate(getNodeTextContents(n));
+ }
+ return null;
+ }
+
+ /**
+ * Returns all element nodes that are contained in a list of nodes.
+ *
+ * @param list The list of nodes to search.
+ * @return The sublist containing only nodes representing actual elements.
+ */
+ private static List<Node> filterElementNodes(NodeList list) {
+ List<Node> result = new ArrayList<Node>(list.getLength());
+ for (int i = 0; i < list.getLength(); i++) {
+ if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
+ result.add(list.item(i));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a node's text content.
+ * This method will return the text value represented by the node's direct children.
+ * If the given node is a TEXT or CDATA node, then its value is returned.
+ *
+ * @param n The node.
+ * @return The node's text content.
+ */
+ private static String getNodeTextContents(Node n) {
+ if (n.getNodeType() == Node.TEXT_NODE || n.getNodeType() == Node.CDATA_SECTION_NODE) {
+ Text txtNode = (Text) n;
+ String content = txtNode.getWholeText(); //This concatenates any adjacent text/cdata/entity nodes
+ if (content == null)
+ return "";
+ else
+ return content;
+ } else {
+ if (n.hasChildNodes()) {
+ NodeList children = n.getChildNodes();
+
+ for (int i = 0; i < children.getLength(); i++) {
+ //Skip any non-text nodes, like comments or entities
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
+ Text txtNode = (Text) child;
+ String content = txtNode.getWholeText(); //This concatenates any adjacent text/cdata/entity nodes
+ if (content == null)
+ return "";
+ else
+ return content;
+ }
+ }
+
+ return "";
+ } else {
+ return "";
+ }
+ }
+ }
+}