PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/java/dd_plist/java/com/dd/plist/XMLPropertyListParser.java

https://gitlab.com/philipithomas/bazel
Java | 293 lines | 151 code | 25 blank | 117 comment | 54 complexity | 768f1309ad27573e3a5280ad93395a0f MD5 | raw file
  1. /*
  2. * plist - An open source library to parse and generate property lists
  3. * Copyright (C) 2014 Daniel Dreibrodt
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. package com.dd.plist;
  24. import org.w3c.dom.*;
  25. import org.xml.sax.EntityResolver;
  26. import org.xml.sax.InputSource;
  27. import org.xml.sax.SAXException;
  28. import javax.xml.parsers.DocumentBuilder;
  29. import javax.xml.parsers.DocumentBuilderFactory;
  30. import javax.xml.parsers.ParserConfigurationException;
  31. import java.io.ByteArrayInputStream;
  32. import java.io.File;
  33. import java.io.IOException;
  34. import java.io.InputStream;
  35. import java.text.ParseException;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. /**
  39. * Parses XML property lists.
  40. *
  41. * @author Daniel Dreibrodt
  42. */
  43. public class XMLPropertyListParser {
  44. /**
  45. * Instantiation is prohibited by outside classes.
  46. */
  47. protected XMLPropertyListParser() {
  48. /** empty **/
  49. }
  50. private static DocumentBuilderFactory docBuilderFactory = null;
  51. /**
  52. * Initialize the document builder factory so that it can be reused and does not need to
  53. * be reinitialized for each parse action.
  54. */
  55. private static synchronized void initDocBuilderFactory() {
  56. docBuilderFactory = DocumentBuilderFactory.newInstance();
  57. docBuilderFactory.setIgnoringComments(true);
  58. docBuilderFactory.setCoalescing(true);
  59. }
  60. /**
  61. * Gets a DocumentBuilder to parse a XML property list.
  62. * As DocumentBuilders are not thread-safe a new DocBuilder is generated for each request.
  63. *
  64. * @return A new DocBuilder that can parse property lists w/o an internet connection.
  65. * @throws javax.xml.parsers.ParserConfigurationException If a document builder for parsing a XML property list
  66. * could not be created. This should not occur.
  67. */
  68. private static synchronized DocumentBuilder getDocBuilder() throws ParserConfigurationException {
  69. if (docBuilderFactory == null)
  70. initDocBuilderFactory();
  71. DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
  72. docBuilder.setEntityResolver(new EntityResolver() {
  73. public InputSource resolveEntity(String publicId, String systemId) {
  74. if ("-//Apple Computer//DTD PLIST 1.0//EN".equals(publicId) || // older publicId
  75. "-//Apple//DTD PLIST 1.0//EN".equals(publicId)) { // newer publicId
  76. // return a dummy, zero length DTD so we don't have to fetch
  77. // it from the network.
  78. return new InputSource(new ByteArrayInputStream(new byte[0]));
  79. }
  80. return null;
  81. }
  82. });
  83. return docBuilder;
  84. }
  85. /**
  86. * Parses a XML property list file.
  87. *
  88. * @param f The XML property list file.
  89. * @return The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
  90. * @see javax.xml.parsers.DocumentBuilder#parse(java.io.File)
  91. * @throws javax.xml.parsers.ParserConfigurationException If a document builder for parsing a XML property list
  92. * could not be created. This should not occur.
  93. * @throws java.io.IOException If any IO error occurs while reading the file.
  94. * @throws org.xml.sax.SAXException If any parse error occurs.
  95. * @throws com.dd.plist.PropertyListFormatException If the given property list has an invalid format.
  96. * @throws java.text.ParseException If a date string could not be parsed.
  97. */
  98. public static NSObject parse(File f) throws ParserConfigurationException, IOException, SAXException, PropertyListFormatException, ParseException {
  99. DocumentBuilder docBuilder = getDocBuilder();
  100. Document doc = docBuilder.parse(f);
  101. return parse(doc);
  102. }
  103. /**
  104. * Parses a XML property list from a byte array.
  105. *
  106. * @param bytes The byte array containing the property list's data.
  107. * @return The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
  108. * @throws javax.xml.parsers.ParserConfigurationException If a document builder for parsing a XML property list
  109. * could not be created. This should not occur.
  110. * @throws java.io.IOException If any IO error occurs while reading the file.
  111. * @throws org.xml.sax.SAXException If any parse error occurs.
  112. * @throws com.dd.plist.PropertyListFormatException If the given property list has an invalid format.
  113. * @throws java.text.ParseException If a date string could not be parsed.
  114. */
  115. public static NSObject parse(final byte[] bytes) throws ParserConfigurationException, ParseException, SAXException, PropertyListFormatException, IOException {
  116. ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
  117. return parse(bis);
  118. }
  119. /**
  120. * Parses a XML property list from an input stream.
  121. *
  122. * @param is The input stream pointing to the property list's data.
  123. * @return The root object of the property list. This is usually a NSDictionary but can also be a NSArray.
  124. * @see javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream)
  125. * @throws javax.xml.parsers.ParserConfigurationException If a document builder for parsing a XML property list
  126. * could not be created. This should not occur.
  127. * @throws java.io.IOException If any IO error occurs while reading the file.
  128. * @throws org.xml.sax.SAXException If any parse error occurs.
  129. * @throws com.dd.plist.PropertyListFormatException If the given property list has an invalid format.
  130. * @throws java.text.ParseException If a date string could not be parsed.
  131. */
  132. public static NSObject parse(InputStream is) throws ParserConfigurationException, IOException, SAXException, PropertyListFormatException, ParseException {
  133. DocumentBuilder docBuilder = getDocBuilder();
  134. Document doc = docBuilder.parse(is);
  135. return parse(doc);
  136. }
  137. /**
  138. * Parses a property list from an XML document.
  139. *
  140. * @param doc The XML document.
  141. * @return The root NSObject of the property list contained in the XML document.
  142. * @throws java.io.IOException If any IO error occurs while reading the file.
  143. * @throws com.dd.plist.PropertyListFormatException If the given property list has an invalid format.
  144. * @throws java.text.ParseException If a date string could not be parsed.
  145. */
  146. public static NSObject parse(Document doc) throws PropertyListFormatException, IOException, ParseException {
  147. DocumentType docType = doc.getDoctype();
  148. if (docType == null) {
  149. if (!doc.getDocumentElement().getNodeName().equals("plist")) {
  150. throw new UnsupportedOperationException("The given XML document is not a property list.");
  151. }
  152. } else if (!docType.getName().equals("plist")) {
  153. throw new UnsupportedOperationException("The given XML document is not a property list.");
  154. }
  155. Node rootNode;
  156. if (doc.getDocumentElement().getNodeName().equals("plist")) {
  157. //Root element wrapped in plist tag
  158. List<Node> rootNodes = filterElementNodes(doc.getDocumentElement().getChildNodes());
  159. if (rootNodes.isEmpty()) {
  160. throw new PropertyListFormatException("The given XML property list has no root element!");
  161. } else if (rootNodes.size() == 1) {
  162. rootNode = rootNodes.get(0);
  163. } else {
  164. throw new PropertyListFormatException("The given XML property list has more than one root element!");
  165. }
  166. } else {
  167. //Root NSObject not wrapped in plist-tag
  168. rootNode = doc.getDocumentElement();
  169. }
  170. return parseObject(rootNode);
  171. }
  172. /**
  173. * Parses a node in the XML structure and returns the corresponding NSObject
  174. *
  175. * @param n The XML node.
  176. * @return The corresponding NSObject.
  177. * @throws java.io.IOException If any IO error occurs while parsing a Base64 encoded NSData object.
  178. * @throws java.text.ParseException If a date string could not be parsed.
  179. */
  180. private static NSObject parseObject(Node n) throws ParseException, IOException {
  181. String type = n.getNodeName();
  182. if (type.equals("dict")) {
  183. NSDictionary dict = new NSDictionary();
  184. List<Node> children = filterElementNodes(n.getChildNodes());
  185. for (int i = 0; i < children.size(); i += 2) {
  186. Node key = children.get(i);
  187. Node val = children.get(i + 1);
  188. String keyString = getNodeTextContents(key);
  189. dict.put(keyString, parseObject(val));
  190. }
  191. return dict;
  192. } else if (type.equals("array")) {
  193. List<Node> children = filterElementNodes(n.getChildNodes());
  194. NSArray array = new NSArray(children.size());
  195. for (int i = 0; i < children.size(); i++) {
  196. array.setValue(i, parseObject(children.get(i)));
  197. }
  198. return array;
  199. } else if (type.equals("true")) {
  200. return new NSNumber(true);
  201. } else if (type.equals("false")) {
  202. return new NSNumber(false);
  203. } else if (type.equals("integer")) {
  204. return new NSNumber(getNodeTextContents(n));
  205. } else if (type.equals("real")) {
  206. return new NSNumber(getNodeTextContents(n));
  207. } else if (type.equals("string")) {
  208. return new NSString(getNodeTextContents(n));
  209. } else if (type.equals("data")) {
  210. return new NSData(getNodeTextContents(n));
  211. } else if (type.equals("date")) {
  212. return new NSDate(getNodeTextContents(n));
  213. }
  214. return null;
  215. }
  216. /**
  217. * Returns all element nodes that are contained in a list of nodes.
  218. *
  219. * @param list The list of nodes to search.
  220. * @return The sub-list containing only nodes representing actual elements.
  221. */
  222. private static List<Node> filterElementNodes(NodeList list) {
  223. List<Node> result = new ArrayList<Node>(list.getLength());
  224. for (int i = 0; i < list.getLength(); i++) {
  225. if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {
  226. result.add(list.item(i));
  227. }
  228. }
  229. return result;
  230. }
  231. /**
  232. * Returns a node's text content.
  233. * This method will return the text value represented by the node's direct children.
  234. * If the given node is a TEXT or CDATA node, then its value is returned.
  235. *
  236. * @param n The node.
  237. * @return The node's text content.
  238. */
  239. private static String getNodeTextContents(Node n) {
  240. if (n.getNodeType() == Node.TEXT_NODE || n.getNodeType() == Node.CDATA_SECTION_NODE) {
  241. Text txtNode = (Text) n;
  242. String content = txtNode.getWholeText(); //This concatenates any adjacent text/cdata/entity nodes
  243. if (content == null)
  244. return "";
  245. else
  246. return content;
  247. } else {
  248. if (n.hasChildNodes()) {
  249. NodeList children = n.getChildNodes();
  250. for (int i = 0; i < children.getLength(); i++) {
  251. //Skip any non-text nodes, like comments or entities
  252. Node child = children.item(i);
  253. if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
  254. Text txtNode = (Text) child;
  255. String content = txtNode.getWholeText(); //This concatenates any adjacent text/cdata/entity nodes
  256. if (content == null)
  257. return "";
  258. else
  259. return content;
  260. }
  261. }
  262. return "";
  263. } else {
  264. return "";
  265. }
  266. }
  267. }
  268. }