diff --git a/plist-cil/ChangeLog b/plist-cil/ChangeLog index 4a9ec5a..d09d44d 100644 --- a/plist-cil/ChangeLog +++ b/plist-cil/ChangeLog @@ -1,3 +1,10 @@ +2015-02-19 Natalia Portillo + + * plist-cil.csproj: + * PropertyListParser.cs: + * XmlPropertyListParser.cs: + Implement XmlPropertyListParser + 2015-02-19 Natalia Portillo * UID.cs: diff --git a/plist-cil/PropertyListParser.cs b/plist-cil/PropertyListParser.cs index f174453..f413f62 100644 --- a/plist-cil/PropertyListParser.cs +++ b/plist-cil/PropertyListParser.cs @@ -145,8 +145,7 @@ namespace Claunia.PropertyList case TYPE_BINARY: return BinaryPropertyListParser.Parse(f); case TYPE_XML: - // TODO: Implement XMLPropertyListParser - //return XMLPropertyListParser.parse(f); + return XmlPropertyListParser.Parse(f); case TYPE_ASCII: return ASCIIPropertyListParser.Parse(f); default: @@ -164,8 +163,7 @@ namespace Claunia.PropertyList case TYPE_BINARY: return BinaryPropertyListParser.Parse(bytes); case TYPE_XML: - // TODO: Implement XMLPropertyListParser - //return XMLPropertyListParser.parse(bytes); + return XmlPropertyListParser.Parse(bytes); case TYPE_ASCII: return ASCIIPropertyListParser.Parse(bytes); default: diff --git a/plist-cil/XmlPropertyListParser.cs b/plist-cil/XmlPropertyListParser.cs new file mode 100644 index 0000000..d3dbe01 --- /dev/null +++ b/plist-cil/XmlPropertyListParser.cs @@ -0,0 +1,201 @@ +// plist-cil - An open source library to parse and generate property lists for .NET +// Copyright (C) 2015 Natalia Portillo +// +// This code is based on: +// 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. +using System; +using System.Xml; +using System.Collections.Generic; +using System.IO; + +namespace Claunia.PropertyList +{ + /// + /// Parses XML property lists. + /// + /// @author Daniel Dreibrodt + public static class XmlPropertyListParser + { + /// + /// Parses a XML property list file. + /// + /// The XML property list file. + /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(FileInfo f) { + XmlDocument doc = new XmlDocument(); + doc.Load(f.OpenRead()); + + return ParseDocument(doc); + } + + /// + /// Parses a XML property list from a byte array. + /// + /// The byte array containing the property list's data. + /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(byte[] bytes) { + MemoryStream bis = new MemoryStream(bytes); + return Parse(bis); + } + + /// + /// Parses a XML property list from an input stream. + /// + /// The input stream pointing to the property list's data. + /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(Stream str) { + XmlDocument doc = new XmlDocument(); + doc.Load(str); + + return ParseDocument(doc); + } + + /// + /// Parses the XML document by generating the appropriate NSObjects for each XML node. + /// + /// The root NSObject of the property list contained in the XML document. + /// The XML document. + private static NSObject ParseDocument(XmlDocument doc) { + XmlDocumentType docType = doc.DocumentType; + if (docType == null) { + if (!doc.DocumentElement.Name.Equals("plist")) { + throw new XmlException("The given XML document is not a property list."); + } + } else if (!docType.Name.Equals("plist")) { + throw new XmlException("The given XML document is not a property list."); + } + + XmlNode rootNode; + + if (doc.DocumentElement.Name.Equals("plist")) { + //Root element wrapped in plist tag + List rootNodes = FilterElementNodes(doc.DocumentElement.ChildNodes); + if (rootNodes.Count == 0) { + throw new PropertyListFormatException("The given XML property list has no root element!"); + } else if (rootNodes.Count == 1) { + rootNode = rootNodes[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.DocumentElement; + } + + return ParseObject(rootNode); + } + + /// + /// Parses a node in the XML structure and returns the corresponding NSObject + /// + /// The corresponding NSObject. + /// The XML node. + private static NSObject ParseObject(XmlNode n) { + if (n.Name.Equals("dict")) { + NSDictionary dict = new NSDictionary(); + List children = FilterElementNodes(n.ChildNodes); + for (int i = 0; i < children.Count; i += 2) { + XmlNode key = children[i]; + XmlNode val = children[i + 1]; + + string keyString = GetNodeTextContents(key); + + dict.Add(keyString, ParseObject(val)); + } + return dict; + } else if (n.Name.Equals("array")) { + List children = FilterElementNodes(n.ChildNodes); + NSArray array = new NSArray(children.Count); + for (int i = 0; i < children.Count; i++) { + array.SetValue(i, ParseObject(children[i])); + } + return array; + } else if (n.Name.Equals("true")) { + return new NSNumber(true); + } else if (n.Name.Equals("false")) { + return new NSNumber(false); + } else if (n.Name.Equals("integer")) { + return new NSNumber(GetNodeTextContents(n)); + } else if (n.Name.Equals("real")) { + return new NSNumber(GetNodeTextContents(n)); + } else if (n.Name.Equals("string")) { + return new NSString(GetNodeTextContents(n)); + } else if (n.Name.Equals("data")) { + return new NSData(GetNodeTextContents(n)); + } else if (n.Name.Equals("date")) { + return new NSDate(GetNodeTextContents(n)); + } + return null; + } + + /// + /// Returns all element nodes that are contained in a list of nodes. + /// + /// The sublist containing only nodes representing actual elements. + /// The list of nodes to search. + private static List FilterElementNodes(XmlNodeList list) { + List result = new List(); + foreach (XmlNode child in result) { + if (child.NodeType == XmlNodeType.Element) { + result.Add(child); + } + } + 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. + /// + /// The node's text content. + /// The node. + private static string GetNodeTextContents(XmlNode n) { + if (n.NodeType == XmlNodeType.Text || n.NodeType == XmlNodeType.CDATA) { + string content = n.Value; //This concatenates any adjacent text/cdata/entity nodes + if (content == null) + return ""; + else + return content; + } else { + if (n.HasChildNodes) { + XmlNodeList children = n.ChildNodes; + + foreach (XmlNode child in children) { + //Skip any non-text nodes, like comments or entities + if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA) { + string content = child.Value; //This concatenates any adjacent text/cdata/entity nodes + if (content == null) + return ""; + else + return content; + } + } + + return ""; + } else { + return ""; + } + } + } } +} + diff --git a/plist-cil/plist-cil.csproj b/plist-cil/plist-cil.csproj index 0611829..64c896c 100644 --- a/plist-cil/plist-cil.csproj +++ b/plist-cil/plist-cil.csproj @@ -30,6 +30,7 @@ + @@ -47,6 +48,7 @@ +