From 7c2546a708d25ff27fb24a2c424131c618b69c5f Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 18 Feb 2015 19:34:11 +0000 Subject: [PATCH] Implement PropertyListParser --- plist-cil/ChangeLog | 6 + plist-cil/PropertyListParser.cs | 370 ++++++++++++++++++++++++++++++++ plist-cil/plist-cil.csproj | 1 + 3 files changed, 377 insertions(+) create mode 100644 plist-cil/PropertyListParser.cs diff --git a/plist-cil/ChangeLog b/plist-cil/ChangeLog index 7feef28..e671cb7 100644 --- a/plist-cil/ChangeLog +++ b/plist-cil/ChangeLog @@ -1,3 +1,9 @@ +2015-02-18 Natalia Portillo + + * plist-cil.csproj: + * PropertyListParser.cs: + Implement PropertyListParser + 2015-02-18 Natalia Portillo * NSSet.cs: diff --git a/plist-cil/PropertyListParser.cs b/plist-cil/PropertyListParser.cs new file mode 100644 index 0000000..c47e62d --- /dev/null +++ b/plist-cil/PropertyListParser.cs @@ -0,0 +1,370 @@ +// 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.Text; +using System.IO; + +namespace Claunia.PropertyList +{ + /// + /// This class provides methods to parse property lists. It can handle files, + /// input streams and byte arrays. All known property list formats are supported. + /// + /// This class also provides methods to save and convert property lists. + /// + /// + /// @author Daniel Dreibrodt + public static class PropertyListParser + { + const int TYPE_XML = 0; + const int TYPE_BINARY = 1; + const int TYPE_ASCII = 2; + const int TYPE_ERROR_BLANK = 10; + const int TYPE_ERROR_UNKNOWN = 11; + + /// + /// Determines the type of a property list by means of the first bytes of its data + /// + /// The type of the property list + /// The very first bytes of data of the property list (minus any whitespace) as a string + private static int DetermineType(string dataBeginning) { + dataBeginning = dataBeginning.Trim(); + if(dataBeginning.Length == 0) { + return TYPE_ERROR_BLANK; + } + if(dataBeginning.StartsWith("bplist")) { + return TYPE_BINARY; + } + if(dataBeginning.StartsWith("(") || dataBeginning.StartsWith("{") || dataBeginning.StartsWith("/")) { + return TYPE_ASCII; + } + if(dataBeginning.StartsWith("<")) { + return TYPE_XML; + } + return TYPE_ERROR_UNKNOWN; + } + + /// + /// Determines the type of a property list by means of the first bytes of its data + /// + /// The very first bytes of data of the property list (minus any whitespace) + /// The type of the property list + private static int DetermineType(byte[] bytes) { + //Skip any possible whitespace at the beginning of the file + int offset = 0; + while(offset < bytes.Length && bytes[offset] == ' ' || bytes[offset] == '\t' || bytes[offset] == '\r' || bytes[offset] == '\n' || bytes[offset] == '\f') { + offset++; + } + return DetermineType(Encoding.ASCII.GetString(bytes, offset, Math.Min(8, bytes.Length - offset))); + } + + /// + /// Determines the type of a property list by means of the first bytes of its data + /// + /// The type of the property list + /// An input stream pointing to the beginning of the property list data. + /// The stream will be reset to the beginning of the property + /// list data after the type has been determined. + private static int DetermineType(Stream fs) { + //Skip any possible whitespace at the beginning of the file + byte[] magicBytes = new byte[8]; + int b; + long mark; + do { + mark = fs.Position; + b = fs.ReadByte(); + } + while(b != -1 && b == ' ' || b == '\t' || b == '\r' || b == '\n' || b == '\f'); + magicBytes[0] = (byte)b; + int read = fs.Read(magicBytes, 1, 7); + int type = DetermineType(Encoding.ASCII.GetString(magicBytes, 0, read)); + fs.Seek(mark, SeekOrigin.Begin); + //if(fs.markSupported()) + // fs.reset(); + return type; + } + + /// + /// Reads all bytes from an Stream and stores them in an array, up to + /// a maximum count. + /// + /// The Stream pointing to the data that should be stored in the array. + static byte[] ReadAll(Stream fs) { + MemoryStream outputStream = new MemoryStream(); + byte[] buf = new byte[512]; + int read = 512; + while (read == 512) { + read = fs.Read(buf, 0, 512); + if(read != -1) + outputStream.Write(buf, 0, read); + } + return outputStream.ToArray(); + } + + /// + /// Parses a property list from a file. + /// + /// Path to the property list file. + /// The root object in the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(string filePath) { + return Parse(new FileInfo(filePath)); + } + + /// + /// Parses a property list from a file. + /// + /// The property list file. + /// The root object in the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(FileInfo f) { + FileStream fis = f.OpenRead(); + int type = DetermineType(fis); + fis.Close(); + switch(type) { + case TYPE_BINARY: + // TODO: Implement BinaryPropertyListParser + //return BinaryPropertyListParser.parse(f); + case TYPE_XML: + // TODO: Implement XMLPropertyListParser + //return XMLPropertyListParser.parse(f); + case TYPE_ASCII: + // TODO: Implement ASCIIPropertyListParser + //return ASCIIPropertyListParser.parse(f); + default: + throw new PropertyListFormatException("The given file is not a property list of a supported format."); + } + } + + /// + /// Parses a property list from a byte array. + /// + /// The property list data as a byte array. + /// The root object in the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(byte[] bytes) { + switch(DetermineType(bytes)) { + case TYPE_BINARY: + // TODO: Implement BinaryPropertyListParser + //return BinaryPropertyListParser.parse(bytes); + case TYPE_XML: + // TODO: Implement XMLPropertyListParser + //return XMLPropertyListParser.parse(bytes); + case TYPE_ASCII: + // TODO: Implement ASCIIPropertyListParser + //return ASCIIPropertyListParser.parse(bytes); + default: + throw new PropertyListFormatException("The given data is not a property list of a supported format."); + } + } + + /// + /// Parses a property list from an Stream. + /// + /// The Stream delivering the property list data. + /// The root object of the property list. This is usually a NSDictionary but can also be a NSArray. + public static NSObject Parse(Stream fs) { + return Parse(ReadAll(fs)); + } + + /// + /// Saves a property list with the given object as root into a XML file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsXml(NSObject root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); + SaveAsXml(root, fous); + fous.Close(); + } + + /// + /// Saves a property list with the given object as root in XML format into an output stream. + /// + /// The root object. + /// The output stream. + /// When an error occurs during the writing process. + public static void SaveAsXml(NSObject root, Stream outStream) { + StreamWriter w = new StreamWriter(outStream, Encoding.UTF8); + w.Write(root.ToXmlPropertyList()); + w.Close(); + } + + /// + /// Converts a given property list file into the OS X and iOS XML format. + /// + /// The source file. + /// The target file. + public static void ConvertToXml(FileInfo inFile, FileInfo outFile) { + NSObject root = Parse(inFile); + SaveAsXml(root, outFile); + } + + /// + /// Saves a property list with the given object as root into a binary file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsBinary(NSObject root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + // TODO: Implement BinaryPropertyListWriter + //BinaryPropertyListWriter.write(out, root); + } + + /// + /// Saves a property list with the given object as root in binary format into an output stream. + /// + /// The root object. + /// The output stream. + /// When an error occurs during the writing process. + public static void SaveAsBinary(NSObject root, Stream outStream) { + // TODO: Implement BinaryPropertyListWriter + //BinaryPropertyListWriter.write(out, root); + } + + /// + /// Converts a given property list file into the OS X and iOS binary format. + /// + /// The source file. + /// The target file. + public static void ConvertToBinary(FileInfo inFile, FileInfo outFile) { + NSObject root = Parse(inFile); + SaveAsBinary(root, outFile); + } + + /// + /// Saves a property list with the given object as root into a ASCII file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsASCII(NSDictionary root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); + StreamWriter w = new StreamWriter(fous, Encoding.ASCII); + w.Write(root.ToASCIIPropertyList()); + w.Close(); + fous.Close(); + } + + /// + /// Saves a property list with the given object as root into a ASCII file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsASCII(NSArray root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); + StreamWriter w = new StreamWriter(fous, Encoding.ASCII); + // TODO: Implement ASCIIPropertyListParser + //w.Write(root.ToASCIIPropertyList()); + w.Close(); + fous.Close(); + } + + /// + /// Converts a given property list file into ASCII format. + /// + /// The source file. + /// The target file. + public static void ConvertToASCII(FileInfo inFile, FileInfo outFile) { + NSObject root = Parse(inFile); + if(root is NSDictionary) { + SaveAsASCII((NSDictionary) root, outFile); + } + else if(root is NSArray) { + SaveAsASCII((NSArray) root, outFile); + } + else { + throw new PropertyListFormatException("The root of the given input property list " + + "is neither a Dictionary nor an Array!"); + } + } + + /// + /// Saves a property list with the given object as root into a ASCII file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsGnuStepASCII(NSDictionary root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); + StreamWriter w = new StreamWriter(fous, Encoding.ASCII); + w.Write(root.ToGnuStepASCIIPropertyList()); + w.Close(); + fous.Close(); + } + + /// + /// Saves a property list with the given object as root into a ASCII file. + /// + /// The root object. + /// The output file. + /// When an error occurs during the writing process. + public static void SaveAsGnuStepASCII(NSArray root, FileInfo outFile) { + string parent = outFile.DirectoryName; + if (!Directory.Exists(parent)) + Directory.CreateDirectory(parent); + Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite); + StreamWriter w = new StreamWriter(fous, Encoding.ASCII); + // TODO: Implement ASCIIPropertyListParser + //w.Write(root.toGnuStepASCIIPropertyList()); + w.Close(); + fous.Close(); + } + + /// + /// Converts a given property list file into ASCII format. + /// + /// The source file. + /// The target file. + public static void ConvertToGnuStepASCII(FileInfo inFile, FileInfo outFile) { + NSObject root = Parse(inFile); + if(root is NSDictionary) { + SaveAsGnuStepASCII((NSDictionary) root, outFile); + } + else if(root is NSArray) { + SaveAsGnuStepASCII((NSArray) root, outFile); + } + else { + throw new PropertyListFormatException("The root of the given input property list " + + "is neither a Dictionary nor an Array!"); + } + } + } +} + diff --git a/plist-cil/plist-cil.csproj b/plist-cil/plist-cil.csproj index 8a8f79a..a0f3303 100644 --- a/plist-cil/plist-cil.csproj +++ b/plist-cil/plist-cil.csproj @@ -43,6 +43,7 @@ +