mirror of
https://github.com/claunia/plist-cil.git
synced 2025-12-16 19:14:26 +00:00
Implement ASCIIPropertyListParser
This commit is contained in:
650
plist-cil/ASCIIPropertyListParser.cs
Normal file
650
plist-cil/ASCIIPropertyListParser.cs
Normal file
@@ -0,0 +1,650 @@
|
||||
// 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.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Claunia.PropertyList
|
||||
{
|
||||
/// <summary>
|
||||
/// <p>
|
||||
/// Parser for ASCII property lists. Supports Apple OS X/iOS and GnuStep/NeXTSTEP format.
|
||||
/// This parser is based on the recursive descent paradigm, but the underlying grammar
|
||||
/// is not explicitely defined.
|
||||
/// </p>
|
||||
/// <p>
|
||||
/// Resources on ASCII property list format:
|
||||
/// </p>
|
||||
/// <ul>
|
||||
/// <li><a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html">
|
||||
/// Property List Programming Guide - Old-Style ASCII Property Lists
|
||||
/// </a></li>
|
||||
/// <li><a href="http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html">
|
||||
/// GnuStep - NSPropertyListSerialization class documentation
|
||||
/// </a></li>
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
/// @author Daniel Dreibrodt
|
||||
public class ASCIIPropertyListParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses an ASCII property list file.
|
||||
/// </summary>
|
||||
/// <param name="f">The ASCII property list file..</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
/// <exception cref="IOException">When an error occured while reading from the input stream.</exception>
|
||||
public static NSObject Parse(FileInfo f) {
|
||||
return Parse(f.OpenRead());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an ASCII property list from an input stream.
|
||||
/// </summary>
|
||||
/// <param name="fs">The input stream that points to the property list's data.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
/// <exception cref="IOException"></exception>
|
||||
public static NSObject Parse(Stream fs) {
|
||||
byte[] buf = PropertyListParser.ReadAll(fs);
|
||||
fs.Close();
|
||||
return Parse(buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an ASCII property list from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="bytes">The ASCII property list data.</param>
|
||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||
public static NSObject Parse(byte[] bytes) {
|
||||
ASCIIPropertyListParser parser = new ASCIIPropertyListParser(bytes);
|
||||
return parser.Parse();
|
||||
}
|
||||
|
||||
public const char WHITESPACE_SPACE = ' ';
|
||||
public const char WHITESPACE_TAB = '\t';
|
||||
public const char WHITESPACE_NEWLINE = '\n';
|
||||
public const char WHITESPACE_CARRIAGE_RETURN = '\r';
|
||||
|
||||
public const char ARRAY_BEGIN_TOKEN = '(';
|
||||
public const char ARRAY_END_TOKEN = ')';
|
||||
public const char ARRAY_ITEM_DELIMITER_TOKEN = ',';
|
||||
|
||||
public const char DICTIONARY_BEGIN_TOKEN = '{';
|
||||
public const char DICTIONARY_END_TOKEN = '}';
|
||||
public const char DICTIONARY_ASSIGN_TOKEN = '=';
|
||||
public const char DICTIONARY_ITEM_DELIMITER_TOKEN = ';';
|
||||
|
||||
public const char QUOTEDSTRING_BEGIN_TOKEN = '"';
|
||||
public const char QUOTEDSTRING_END_TOKEN = '"';
|
||||
public const char QUOTEDSTRING_ESCAPE_TOKEN = '\\';
|
||||
|
||||
public const char DATA_BEGIN_TOKEN = '<';
|
||||
public const char DATA_END_TOKEN = '>';
|
||||
|
||||
public const char DATA_GSOBJECT_BEGIN_TOKEN = '*';
|
||||
public const char DATA_GSDATE_BEGIN_TOKEN = 'D';
|
||||
public const char DATA_GSBOOL_BEGIN_TOKEN = 'B';
|
||||
public const char DATA_GSBOOL_TRUE_TOKEN = 'Y';
|
||||
public const char DATA_GSBOOL_FALSE_TOKEN = 'N';
|
||||
public const char DATA_GSINT_BEGIN_TOKEN = 'I';
|
||||
public const char DATA_GSREAL_BEGIN_TOKEN = 'R';
|
||||
|
||||
public const char DATE_DATE_FIELD_DELIMITER = '-';
|
||||
public const char DATE_TIME_FIELD_DELIMITER = ':';
|
||||
public const char DATE_GS_DATE_TIME_DELIMITER = ' ';
|
||||
public const char DATE_APPLE_DATE_TIME_DELIMITER = 'T';
|
||||
public const char DATE_APPLE_END_TOKEN = 'Z';
|
||||
|
||||
public const char COMMENT_BEGIN_TOKEN = '/';
|
||||
public const char MULTILINE_COMMENT_SECOND_TOKEN = '*';
|
||||
public const char SINGLELINE_COMMENT_SECOND_TOKEN = '/';
|
||||
public const char MULTILINE_COMMENT_END_TOKEN = '/';
|
||||
|
||||
/**
|
||||
* Property list source data
|
||||
*/
|
||||
private byte[] data;
|
||||
/**
|
||||
* Current parsing index
|
||||
*/
|
||||
private int index;
|
||||
|
||||
/**
|
||||
* Only allow subclasses to change instantiation.
|
||||
*/
|
||||
protected ASCIIPropertyListParser() {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new parser for the given property list content.
|
||||
/// </summary>
|
||||
/// <param name="propertyListContent">The content of the property list that is to be parsed.</param>
|
||||
private ASCIIPropertyListParser(byte[] propertyListContent) {
|
||||
data = propertyListContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given sequence of symbols can be accepted.
|
||||
/// </summary>
|
||||
/// <returns>Whether the given tokens occur at the current parsing position.</returns>
|
||||
/// <param name="sequence">The sequence of tokens to look for.</param>
|
||||
private bool AcceptSequence(params char[] sequence) {
|
||||
for (int i = 0; i < sequence.Length; i++) {
|
||||
if (data[index + i] != sequence[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given symbols can be accepted, that is, if one
|
||||
/// of the given symbols is found at the current parsing position.
|
||||
/// </summary>
|
||||
/// <param name="acceptableSymbols">The symbols to check.</param>
|
||||
/// <returns>Whether one of the symbols can be accepted or not.</returns>
|
||||
private bool Accept(params char[] acceptableSymbols) {
|
||||
bool symbolPresent = false;
|
||||
foreach (char c in acceptableSymbols) {
|
||||
if (data[index] == c)
|
||||
symbolPresent = true;
|
||||
}
|
||||
return symbolPresent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given symbol can be accepted, that is, if
|
||||
/// the given symbols is found at the current parsing position.
|
||||
/// </summary>
|
||||
/// <param name="acceptableSymbol">The symbol to check.</param>
|
||||
/// <returns>Whether the symbol can be accepted or not.</returns>
|
||||
private bool Accept(char acceptableSymbol) {
|
||||
return data[index] == acceptableSymbol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expects the input to have one of the given symbols at the current parsing position.
|
||||
/// </summary>
|
||||
/// <param name="expectedSymbols">The expected symbols.</param>
|
||||
/// <exception cref="FormatException">If none of the expected symbols could be found.</exception>
|
||||
private void Expect(params char[] expectedSymbols) {
|
||||
if (!Accept(expectedSymbols)) {
|
||||
String excString = "Expected '" + expectedSymbols[0] + "'";
|
||||
for (int i = 1; i < expectedSymbols.Length; i++) {
|
||||
excString += " or '" + expectedSymbols[i] + "'";
|
||||
}
|
||||
excString += " but found '" + (char) data[index] + "'";
|
||||
throw new FormatException(String.Format("{0} at {1}", excString, index));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expects the input to have the given symbol at the current parsing position.
|
||||
/// </summary>
|
||||
/// <param name="expectedSymbol">The expected symbol.</param>
|
||||
/// <exception cref="FormatException">If the expected symbol could be found.</exception>
|
||||
private void Expect(char expectedSymbol) {
|
||||
if (!Accept(expectedSymbol))
|
||||
throw new FormatException(String.Format("Expected '{0}' but found '{1}' at {2}", expectedSymbol, data[index], index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an expected symbol.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol to read.</param>
|
||||
/// <exception cref="FormatException">If the expected symbol could not be read.</exception>
|
||||
private void Read(char symbol) {
|
||||
Expect(symbol);
|
||||
index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the current symbol.
|
||||
*/
|
||||
private void Skip() {
|
||||
index++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips several symbols
|
||||
/// </summary>
|
||||
/// <param name="numSymbols">The amount of symbols to skip.</param>
|
||||
private void Skip(int numSymbols) {
|
||||
index += numSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips all whitespaces and comments from the current parsing position onward.
|
||||
*/
|
||||
private void SkipWhitespacesAndComments() {
|
||||
bool commentSkipped;
|
||||
do {
|
||||
commentSkipped = false;
|
||||
|
||||
//Skip whitespaces
|
||||
while (Accept(WHITESPACE_CARRIAGE_RETURN, WHITESPACE_NEWLINE, WHITESPACE_SPACE, WHITESPACE_TAB)) {
|
||||
Skip();
|
||||
}
|
||||
|
||||
//Skip single line comments "//..."
|
||||
if (AcceptSequence(COMMENT_BEGIN_TOKEN, SINGLELINE_COMMENT_SECOND_TOKEN)) {
|
||||
Skip(2);
|
||||
ReadInputUntil(WHITESPACE_CARRIAGE_RETURN, WHITESPACE_NEWLINE);
|
||||
commentSkipped = true;
|
||||
}
|
||||
//Skip multi line comments "/* ... */"
|
||||
else if (AcceptSequence(COMMENT_BEGIN_TOKEN, MULTILINE_COMMENT_SECOND_TOKEN)) {
|
||||
Skip(2);
|
||||
while (true) {
|
||||
if (AcceptSequence(MULTILINE_COMMENT_SECOND_TOKEN, MULTILINE_COMMENT_END_TOKEN)) {
|
||||
Skip(2);
|
||||
break;
|
||||
}
|
||||
Skip();
|
||||
}
|
||||
commentSkipped = true;
|
||||
}
|
||||
}
|
||||
while (commentSkipped); //if a comment was skipped more whitespace or another comment can follow, so skip again
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads input until one of the given symbols is found.
|
||||
/// </summary>
|
||||
/// <returns>The input until one the given symbols.</returns>
|
||||
/// <param name="symbols">The symbols that can occur after the string to read.</param>
|
||||
private string ReadInputUntil(params char[] symbols) {
|
||||
string s = "";
|
||||
while (!Accept(symbols)) {
|
||||
s += (char) data[index];
|
||||
Skip();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads input until the given symbol is found.
|
||||
/// </summary>
|
||||
/// <returns>The input until the given symbol.</returns>
|
||||
/// <param name="symbol">The symbol that can occur after the string to read.</param>
|
||||
private string ReadInputUntil(char symbol) {
|
||||
String s = "";
|
||||
while (!Accept(symbol)) {
|
||||
s += (char) data[index];
|
||||
Skip();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the property list from the beginning and returns the root object
|
||||
/// of the property list.
|
||||
/// </summary>
|
||||
/// <returns>The root object of the property list. This can either be a NSDictionary or a NSArray.</returns>
|
||||
/// <exception cref="FormatException">When an error occured during parsing</exception>
|
||||
public NSObject Parse() {
|
||||
index = 0;
|
||||
SkipWhitespacesAndComments();
|
||||
Expect(DICTIONARY_BEGIN_TOKEN, ARRAY_BEGIN_TOKEN, COMMENT_BEGIN_TOKEN);
|
||||
try {
|
||||
return ParseObject();
|
||||
} catch (IndexOutOfRangeException ex) {
|
||||
throw new FormatException(String.Format("Reached end of input unexpectedly at {0}.", index));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the NSObject found at the current position in the property list
|
||||
/// data stream.
|
||||
/// </summary>
|
||||
/// <returns>The parsed NSObject.</returns>
|
||||
/// <seealso cref="ASCIIPropertyListParser.index"/>
|
||||
private NSObject ParseObject() {
|
||||
switch (data[index]) {
|
||||
case (byte)ARRAY_BEGIN_TOKEN: {
|
||||
return ParseArray();
|
||||
}
|
||||
case (byte)DICTIONARY_BEGIN_TOKEN: {
|
||||
return ParseDictionary();
|
||||
}
|
||||
case (byte)DATA_BEGIN_TOKEN: {
|
||||
return ParseData();
|
||||
}
|
||||
case (byte)QUOTEDSTRING_BEGIN_TOKEN: {
|
||||
string quotedString = ParseQuotedString();
|
||||
//apple dates are quoted strings of length 20 and after the 4 year digits a dash is found
|
||||
if (quotedString.Length == 20 && quotedString[4] == DATE_DATE_FIELD_DELIMITER) {
|
||||
try {
|
||||
return new NSDate(quotedString);
|
||||
} catch (Exception ex) {
|
||||
//not a date? --> return string
|
||||
return new NSString(quotedString);
|
||||
}
|
||||
} else {
|
||||
return new NSString(quotedString);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
//0-9
|
||||
if (data[index] > 0x2F && data[index] < 0x3A) {
|
||||
//could be a date or just a string
|
||||
return ParseDateString();
|
||||
} else {
|
||||
//non-numerical -> string or boolean
|
||||
string parsedString = ParseString();
|
||||
return new NSString(parsedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an array from the current parsing position.
|
||||
/// The prerequisite for calling this method is, that an array begin token has been read.
|
||||
/// </summary>
|
||||
/// <returns>The array found at the parsing position.</returns>
|
||||
private NSArray ParseArray() {
|
||||
//Skip begin token
|
||||
Skip();
|
||||
SkipWhitespacesAndComments();
|
||||
List<NSObject> objects = new List<NSObject>();
|
||||
while (!Accept(ARRAY_END_TOKEN)) {
|
||||
objects.Add(ParseObject());
|
||||
SkipWhitespacesAndComments();
|
||||
if (Accept(ARRAY_ITEM_DELIMITER_TOKEN)) {
|
||||
Skip();
|
||||
} else {
|
||||
break; //must have reached end of array
|
||||
}
|
||||
SkipWhitespacesAndComments();
|
||||
}
|
||||
//parse end token
|
||||
Read(ARRAY_END_TOKEN);
|
||||
return new NSArray(objects.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a dictionary from the current parsing position.
|
||||
/// The prerequisite for calling this method is, that a dictionary begin token has been read.
|
||||
/// </summary>
|
||||
/// <returns>The dictionary found at the parsing position.</returns>
|
||||
private NSDictionary ParseDictionary() {
|
||||
//Skip begin token
|
||||
Skip();
|
||||
SkipWhitespacesAndComments();
|
||||
NSDictionary dict = new NSDictionary();
|
||||
while (!Accept(DICTIONARY_END_TOKEN)) {
|
||||
//Parse key
|
||||
string keyString;
|
||||
if (Accept(QUOTEDSTRING_BEGIN_TOKEN)) {
|
||||
keyString = ParseQuotedString();
|
||||
} else {
|
||||
keyString = ParseString();
|
||||
}
|
||||
SkipWhitespacesAndComments();
|
||||
|
||||
//Parse assign token
|
||||
Read(DICTIONARY_ASSIGN_TOKEN);
|
||||
SkipWhitespacesAndComments();
|
||||
|
||||
NSObject nso = ParseObject();
|
||||
dict.Add(keyString, nso);
|
||||
SkipWhitespacesAndComments();
|
||||
Read(DICTIONARY_ITEM_DELIMITER_TOKEN);
|
||||
SkipWhitespacesAndComments();
|
||||
}
|
||||
//skip end token
|
||||
Skip();
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a data object from the current parsing position.
|
||||
/// This can either be a NSData object or a GnuStep NSNumber or NSDate.
|
||||
/// The prerequisite for calling this method is, that a data begin token has been read.
|
||||
/// </summary>
|
||||
/// <returns>The data object found at the parsing position.</returns>
|
||||
private NSObject ParseData() {
|
||||
NSObject obj = null;
|
||||
//Skip begin token
|
||||
Skip();
|
||||
if (Accept(DATA_GSOBJECT_BEGIN_TOKEN)) {
|
||||
Skip();
|
||||
Expect(DATA_GSBOOL_BEGIN_TOKEN, DATA_GSDATE_BEGIN_TOKEN, DATA_GSINT_BEGIN_TOKEN, DATA_GSREAL_BEGIN_TOKEN);
|
||||
if (Accept(DATA_GSBOOL_BEGIN_TOKEN)) {
|
||||
//Boolean
|
||||
Skip();
|
||||
Expect(DATA_GSBOOL_TRUE_TOKEN, DATA_GSBOOL_FALSE_TOKEN);
|
||||
if (Accept(DATA_GSBOOL_TRUE_TOKEN)) {
|
||||
obj = new NSNumber(true);
|
||||
} else {
|
||||
obj = new NSNumber(false);
|
||||
}
|
||||
//Skip the parsed boolean token
|
||||
Skip();
|
||||
} else if (Accept(DATA_GSDATE_BEGIN_TOKEN)) {
|
||||
//Date
|
||||
Skip();
|
||||
string dateString = ReadInputUntil(DATA_END_TOKEN);
|
||||
obj = new NSDate(dateString);
|
||||
} else if (Accept(DATA_GSINT_BEGIN_TOKEN, DATA_GSREAL_BEGIN_TOKEN)) {
|
||||
//Number
|
||||
Skip();
|
||||
string numberString = ReadInputUntil(DATA_END_TOKEN);
|
||||
obj = new NSNumber(numberString);
|
||||
}
|
||||
//parse data end token
|
||||
Read(DATA_END_TOKEN);
|
||||
} else {
|
||||
string dataString = ReadInputUntil(DATA_END_TOKEN);
|
||||
dataString = Regex.Replace(dataString, "\\s+", "");
|
||||
|
||||
int numBytes = dataString.Length / 2;
|
||||
byte[] bytes = new byte[numBytes];
|
||||
for (int i = 0; i < bytes.Length; i++) {
|
||||
string byteString = dataString.Substring(i * 2, i * 2 + 2);
|
||||
int byteValue = Convert.ToInt32(byteString, 16);
|
||||
bytes[i] = (byte) byteValue;
|
||||
}
|
||||
obj = new NSData(bytes);
|
||||
|
||||
//skip end token
|
||||
Skip();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse a plain string as a date if possible.
|
||||
/// </summary>
|
||||
/// <returns>A NSDate if the string represents such an object. Otherwise a NSString is returned.</returns>
|
||||
private NSObject ParseDateString() {
|
||||
string numericalString = ParseString();
|
||||
if (numericalString.Length > 4 && numericalString[4] == DATE_DATE_FIELD_DELIMITER) {
|
||||
try {
|
||||
return new NSDate(numericalString);
|
||||
} catch(Exception ex) {
|
||||
//An exception occurs if the string is not a date but just a string
|
||||
}
|
||||
}
|
||||
return new NSString(numericalString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a plain string from the current parsing position.
|
||||
/// The string is made up of all characters to the next whitespace, delimiter token or assignment token.
|
||||
/// </summary>
|
||||
/// <returns>The string found at the current parsing position.</returns>
|
||||
private string ParseString() {
|
||||
return ReadInputUntil(WHITESPACE_SPACE, WHITESPACE_TAB, WHITESPACE_NEWLINE, WHITESPACE_CARRIAGE_RETURN,
|
||||
ARRAY_ITEM_DELIMITER_TOKEN, DICTIONARY_ITEM_DELIMITER_TOKEN, DICTIONARY_ASSIGN_TOKEN, ARRAY_END_TOKEN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a quoted string from the current parsing position.
|
||||
/// The prerequisite for calling this method is, that a quoted string begin token has been read.
|
||||
/// </summary>
|
||||
/// <returns>The quoted string found at the parsing method with all special characters unescaped.</returns>
|
||||
/// <exception cref="FormatException">If an error occured during parsing.</exception>
|
||||
private string ParseQuotedString() {
|
||||
//Skip begin token
|
||||
Skip();
|
||||
string quotedString = "";
|
||||
bool unescapedBackslash = true;
|
||||
//Read from opening quotation marks to closing quotation marks and skip escaped quotation marks
|
||||
while (data[index] != QUOTEDSTRING_END_TOKEN || (data[index - 1] == QUOTEDSTRING_ESCAPE_TOKEN && unescapedBackslash)) {
|
||||
quotedString += (char) data[index];
|
||||
if (Accept(QUOTEDSTRING_ESCAPE_TOKEN)) {
|
||||
unescapedBackslash = !(data[index - 1] == QUOTEDSTRING_ESCAPE_TOKEN && unescapedBackslash);
|
||||
}
|
||||
Skip();
|
||||
}
|
||||
string unescapedString;
|
||||
try {
|
||||
unescapedString = ParseQuotedString(quotedString);
|
||||
} catch (Exception ex) {
|
||||
throw new FormatException(String.Format("The quoted string could not be parsed at {0}.", index));
|
||||
}
|
||||
//skip end token
|
||||
Skip();
|
||||
return unescapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to encode the parsed strings
|
||||
*/
|
||||
private static Encoding asciiEncoder;
|
||||
|
||||
/// <summary>
|
||||
/// Parses a string according to the format specified for ASCII property lists.
|
||||
/// Such strings can contain escape sequences which are unescaped in this method.
|
||||
/// </summary>
|
||||
/// <returns>The unescaped string in UTF-8 or ASCII format, depending on the contained characters.</returns>
|
||||
/// <param name="s">The escaped string according to the ASCII property list format, without leading and trailing quotation marks.</param>
|
||||
/// <exception cref="ArgumentException">If the en-/decoder for the UTF-8 or ASCII encoding could not be loaded</exception>
|
||||
/// <exception cref="EncoderFallbackException">If the string is encoded neither in ASCII nor in UTF-8</exception>
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public static string ParseQuotedString(string s) {
|
||||
List<byte> strBytes = new List<byte>();
|
||||
CharEnumerator c = s.GetEnumerator();
|
||||
|
||||
while(c.MoveNext()) {
|
||||
switch (c.Current) {
|
||||
case '\\': { //An escaped sequence is following
|
||||
byte[] bts = Encoding.UTF8.GetBytes(ParseEscapedSequence(c));
|
||||
foreach (byte b in bts)
|
||||
strBytes.Add(b);
|
||||
break;
|
||||
}
|
||||
default: { //a normal ASCII char
|
||||
strBytes.Add((byte) 0);
|
||||
strBytes.Add((byte) c.Current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] bytArr = new byte[strBytes.Count];
|
||||
int i = 0;
|
||||
foreach (byte b in strBytes) {
|
||||
bytArr[i] = b;
|
||||
i++;
|
||||
}
|
||||
//Build string
|
||||
string result = Encoding.UTF8.GetString(bytArr);
|
||||
//emoryStream charBuf = CharBuffer.wrap(result);
|
||||
|
||||
//If the string can be represented in the ASCII codepage
|
||||
// --> use ASCII encoding
|
||||
try{
|
||||
if (asciiEncoder == null)
|
||||
asciiEncoder = Encoding.GetEncoding("ascii", EncoderExceptionFallback.ExceptionFallback, DecoderExceptionFallback.ExceptionFallback);
|
||||
return asciiEncoder.GetString(Encoding.Convert(Encoding.UTF8, asciiEncoder, bytArr));
|
||||
}
|
||||
catch
|
||||
{
|
||||
//The string contains characters outside the ASCII codepage
|
||||
// --> use the UTF-8 encoded string
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unescapes an escaped character sequence, e.g. \\u00FC.
|
||||
/// </summary>
|
||||
/// <returns>The unescaped character as a string.</returns>
|
||||
/// <param name="iterator">The string character iterator pointing to the first character after the backslash</param>
|
||||
/// <exception cref="EncoderFallbackException">If an invalid Unicode or ASCII escape sequence is found.</exception>
|
||||
private static string ParseEscapedSequence(CharEnumerator iterator) {
|
||||
iterator.MoveNext();
|
||||
char c = iterator.Current;
|
||||
if (c == '\\') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\\'});
|
||||
} else if (c == '"') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\"'});
|
||||
} else if (c == 'b') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\b'});
|
||||
} else if (c == 'n') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\n'});
|
||||
} else if (c == 'r') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\r'});
|
||||
} else if (c == 't') {
|
||||
return Encoding.UTF8.GetString(new byte[]{0, (byte)'\t'});
|
||||
} else if (c == 'U' || c == 'u') {
|
||||
//4 digit hex Unicode value
|
||||
string byte1 = "";
|
||||
iterator.MoveNext();
|
||||
byte1 += iterator.Current;
|
||||
iterator.MoveNext();
|
||||
byte1 += iterator.Current;
|
||||
string byte2 = "";
|
||||
iterator.MoveNext();
|
||||
byte2 += iterator.Current;
|
||||
iterator.MoveNext();
|
||||
byte2 += iterator.Current;
|
||||
byte[] stringBytes = {(byte) Convert.ToInt32(byte1, 16), (byte) Convert.ToInt32(byte2, 16)};
|
||||
return Encoding.UTF8.GetString(stringBytes);
|
||||
} else {
|
||||
//3 digit octal ASCII value
|
||||
string num = "";
|
||||
num += c;
|
||||
iterator.MoveNext();
|
||||
num += iterator.Current;
|
||||
iterator.MoveNext();
|
||||
num += iterator.Current;
|
||||
int asciiCode = Convert.ToInt32(num, 8);
|
||||
byte[] stringBytes = {0, (byte) asciiCode};
|
||||
return Encoding.UTF8.GetString(stringBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
2015-02-19 Natalia Portillo <claunia@claunia.com>
|
||||
|
||||
* UID.cs:
|
||||
* NSSet.cs:
|
||||
* NSDate.cs:
|
||||
* NSData.cs:
|
||||
* NSArray.cs:
|
||||
* NSNumber.cs:
|
||||
* NSObject.cs:
|
||||
* NSString.cs:
|
||||
* NSDictionary.cs:
|
||||
* plist-cil.csproj:
|
||||
* PropertyListParser.cs:
|
||||
* ASCIIPropertyListParser.cs:
|
||||
Implement ASCIIPropertyListParser
|
||||
|
||||
2015-02-18 Natalia Portillo <claunia@claunia.com>
|
||||
|
||||
* plist-cil.csproj:
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace Claunia.PropertyList
|
||||
/// Creates an empty array of the given length.
|
||||
/// </summary>
|
||||
/// <param name="length">The number of elements this array will be able to hold.</param>
|
||||
public NSArray(int length) {
|
||||
public NSArray(int length)
|
||||
{
|
||||
array = new NSObject[length];
|
||||
}
|
||||
|
||||
@@ -47,7 +48,8 @@ namespace Claunia.PropertyList
|
||||
/// Creates a array from an existing one
|
||||
/// </summary>
|
||||
/// <param name="a">The array which should be wrapped by the NSArray</param>
|
||||
public NSArray(params NSObject[] a) {
|
||||
public NSArray(params NSObject[] a)
|
||||
{
|
||||
array = a;
|
||||
}
|
||||
|
||||
@@ -56,7 +58,8 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns>The object at the given index.</returns>
|
||||
/// <param name="i">The index of the object.</param>
|
||||
public NSObject ObjectAtIndex(int i) {
|
||||
public NSObject ObjectAtIndex(int i)
|
||||
{
|
||||
return array[i];
|
||||
}
|
||||
|
||||
@@ -65,7 +68,8 @@ namespace Claunia.PropertyList
|
||||
/// The array will be resized.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the object</param>
|
||||
public void Remove(int i) {
|
||||
public void Remove(int i)
|
||||
{
|
||||
if ((i >= array.Length) || (i < 0))
|
||||
throw new IndexOutOfRangeException("invalid index:" + i + ";the array length is " + array.Length);
|
||||
NSObject[] newArray = new NSObject[array.Length - 1];
|
||||
@@ -80,8 +84,9 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <param name="key">The index where to store the object.</param>
|
||||
/// <param name="value">The object.</param>
|
||||
public void SetValue(int key, Object value) {
|
||||
if(value == null)
|
||||
public void SetValue(int key, Object value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("Cannot add null values to an NSArray!");
|
||||
array[key] = NSObject.Wrap(value);
|
||||
}
|
||||
@@ -91,7 +96,8 @@ namespace Claunia.PropertyList
|
||||
/// Any changes to the values of this array will also affect the NSArray.
|
||||
/// </summary>
|
||||
/// <returns>The actual array represented by this NSArray.</returns>
|
||||
public NSObject[] GetArray() {
|
||||
public NSObject[] GetArray()
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -99,7 +105,8 @@ namespace Claunia.PropertyList
|
||||
/// Returns the size of the array.
|
||||
/// </summary>
|
||||
/// <value>The number of elements that this array can store.</value>
|
||||
public int Count {
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return array.Length;
|
||||
@@ -112,10 +119,13 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, when the object could be found. <c>false</c> otherwise.</returns>
|
||||
/// <param name="obj">The object to look for.</param>
|
||||
public bool ContainsObject(Object obj) {
|
||||
public bool ContainsObject(Object obj)
|
||||
{
|
||||
NSObject nso = NSObject.Wrap(obj);
|
||||
foreach (NSObject elem in array) {
|
||||
if (elem.Equals(nso)) {
|
||||
foreach (NSObject elem in array)
|
||||
{
|
||||
if (elem.Equals(nso))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -129,10 +139,13 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
||||
/// <param name="obj">The object to look for.</param>
|
||||
public int IndexOfObject(Object obj) {
|
||||
public int IndexOfObject(Object obj)
|
||||
{
|
||||
NSObject nso = NSObject.Wrap(obj);
|
||||
for (int i = 0; i < array.Length; i++) {
|
||||
if (array[i].Equals(nso)) {
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
if (array[i].Equals(nso))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -147,10 +160,13 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
||||
/// <param name="obj">The object to look for.</param>
|
||||
public int IndexOfIdenticalObject(Object obj) {
|
||||
public int IndexOfIdenticalObject(Object obj)
|
||||
{
|
||||
NSObject nso = NSObject.Wrap(obj);
|
||||
for (int i = 0; i < array.Length; i++) {
|
||||
if (array[i] == nso) {
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
if (array[i] == nso)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -161,7 +177,8 @@ namespace Claunia.PropertyList
|
||||
/// Returns the last object contained in this array.
|
||||
/// </summary>
|
||||
/// <returns>The value of the highest index in the array.</returns>
|
||||
public NSObject LastObject() {
|
||||
public NSObject LastObject()
|
||||
{
|
||||
return array[array.Length - 1];
|
||||
}
|
||||
|
||||
@@ -171,7 +188,8 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the objects stored at the given indices.</returns>
|
||||
/// <param name="indexes">The indices of the objects.</param>
|
||||
public NSObject[] objectsAtIndexes(params int[] indexes) {
|
||||
public NSObject[] objectsAtIndexes(params int[] indexes)
|
||||
{
|
||||
NSObject[] result = new NSObject[indexes.Length];
|
||||
Array.Sort(indexes);
|
||||
for (int i = 0; i < indexes.Length; i++)
|
||||
@@ -179,29 +197,37 @@ namespace Claunia.PropertyList
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Equals(Object obj) {
|
||||
if(obj.GetType().Equals(typeof(NSArray))) {
|
||||
return Array.Equals(((NSArray) obj).GetArray(), this.array);
|
||||
} else {
|
||||
public override bool Equals(Object obj)
|
||||
{
|
||||
if (obj.GetType().Equals(typeof(NSArray)))
|
||||
{
|
||||
return Array.Equals(((NSArray)obj).GetArray(), this.array);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSObject nso = NSObject.Wrap(obj);
|
||||
if(nso.GetType().Equals(typeof(NSArray))) {
|
||||
return Array.Equals(((NSArray) nso).GetArray(), this.array);
|
||||
if (nso.GetType().Equals(typeof(NSArray)))
|
||||
{
|
||||
return Array.Equals(((NSArray)nso).GetArray(), this.array);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 89 * hash + array.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
internal override void ToXml(StringBuilder xml, int level) {
|
||||
internal override void ToXml(StringBuilder xml, int level)
|
||||
{
|
||||
Indent(xml, level);
|
||||
xml.Append("<array>");
|
||||
xml.Append(NSObject.NEWLINE);
|
||||
foreach (NSObject o in array) {
|
||||
foreach (NSObject o in array)
|
||||
{
|
||||
o.ToXml(xml, level + 1);
|
||||
xml.Append(NSObject.NEWLINE);
|
||||
}
|
||||
@@ -226,96 +252,103 @@ namespace Claunia.PropertyList
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Generates a valid ASCII property list which has this NSArray as its
|
||||
* root object. The generated property list complies with the format as
|
||||
* described in <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html">
|
||||
* Property List Programming Guide - Old-Style ASCII Property Lists</a>.
|
||||
*
|
||||
* @return ASCII representation of this object.
|
||||
*
|
||||
public string toASCIIPropertyList() {
|
||||
/// <summary>
|
||||
/// Generates a valid ASCII property list which has this NSArray as its
|
||||
/// root object. The generated property list complies with the format as
|
||||
/// described in <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html">
|
||||
/// Property List Programming Guide - Old-Style ASCII Property Lists</a>.
|
||||
/// </summary>
|
||||
/// <returns>ASCII representation of this object.</returns>
|
||||
public string ToASCIIPropertyList()
|
||||
{
|
||||
StringBuilder ascii = new StringBuilder();
|
||||
ToASCII(ascii, 0);
|
||||
ascii.Append(NEWLINE);
|
||||
return ascii.ToString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a valid ASCII property list in GnuStep format which has this
|
||||
* NSArray as its root object. The generated property list complies with
|
||||
* the format as described in <a href="http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html">
|
||||
* GnuStep - NSPropertyListSerialization class documentation
|
||||
* </a>
|
||||
*
|
||||
* @return GnuStep ASCII representation of this object.
|
||||
*
|
||||
public string ToGnuStepASCIIPropertyList() {
|
||||
/// <summary>
|
||||
/// Generates a valid ASCII property list in GnuStep format which has this
|
||||
/// NSArray as its root object. The generated property list complies with
|
||||
/// the format as described in <a href="http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html">
|
||||
/// GnuStep - NSPropertyListSerialization class documentation
|
||||
/// </a>
|
||||
/// </summary>
|
||||
/// <returns>GnuStep ASCII representation of this object.</returns>
|
||||
public string ToGnuStepASCIIPropertyList()
|
||||
{
|
||||
StringBuilder ascii = new StringBuilder();
|
||||
ToASCIIGnuStep(ascii, 0);
|
||||
ascii.Append(NEWLINE);
|
||||
return ascii.ToString();
|
||||
}*/
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
/*
|
||||
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Class<?> objClass = array[i].getClass();
|
||||
if ((objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class))
|
||||
&& indexOfLastNewLine != ascii.length()) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
array[i].toASCII(ascii, level + 1);
|
||||
} else {
|
||||
if (i != 0)
|
||||
ascii.append(" ");
|
||||
array[i].toASCII(ascii, 0);
|
||||
}
|
||||
|
||||
if (i != array.length - 1)
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
|
||||
if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
}
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN);*/
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
/*
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Class<?> objClass = array[i].getClass();
|
||||
if ((objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class))
|
||||
&& indexOfLastNewLine != ascii.length()) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
array[i].toASCIIGnuStep(ascii, level + 1);
|
||||
} else {
|
||||
internal override void ToASCII(StringBuilder ascii, int level)
|
||||
{
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
Type objClass = array[i].GetType();
|
||||
if ((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData)))
|
||||
&& indexOfLastNewLine != ascii.Length)
|
||||
{
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
array[i].ToASCII(ascii, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i != 0)
|
||||
ascii.append(" ");
|
||||
array[i].toASCIIGnuStep(ascii, 0);
|
||||
ascii.Append(" ");
|
||||
array[i].ToASCII(ascii, 0);
|
||||
}
|
||||
|
||||
if (i != array.length - 1)
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
if (i != array.Length - 1)
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
|
||||
if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
if (ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH)
|
||||
{
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
}
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN);*/
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||
}
|
||||
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||
{
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
Type objClass = array[i].GetType();
|
||||
if ((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData)))
|
||||
&& indexOfLastNewLine != ascii.Length)
|
||||
{
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
array[i].ToASCIIGnuStep(ascii, level + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i != 0)
|
||||
ascii.Append(" ");
|
||||
array[i].ToASCIIGnuStep(ascii, 0);
|
||||
}
|
||||
|
||||
if (i != array.Length - 1)
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
|
||||
if (ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH)
|
||||
{
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
}
|
||||
}
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,28 +145,24 @@ namespace Claunia.PropertyList
|
||||
out.write(bytes);
|
||||
}*/
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
/*
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.DATA_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.DATA_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < bytes.Length; i++) {
|
||||
int b = bytes[i] & 0xFF;
|
||||
if (b < 16)
|
||||
ascii.append("0");
|
||||
ascii.append(Integer.toHexString(b));
|
||||
if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
} else if ((i + 1) % 2 == 0 && i != bytes.length - 1) {
|
||||
ascii.append(" ");
|
||||
ascii.Append(String.Format("{0:x2}", b));
|
||||
if (ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
} else if ((i + 1) % 2 == 0 && i != bytes.Length - 1) {
|
||||
ascii.Append(" ");
|
||||
}
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.DATA_END_TOKEN);*/
|
||||
ascii.Append(ASCIIPropertyListParser.DATA_END_TOKEN);
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
ToASCII(ascii, level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +149,14 @@ namespace Claunia.PropertyList
|
||||
return date.ToString();
|
||||
}
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append("\"");
|
||||
ascii.Append(MakeDateString(date));
|
||||
ascii.Append("\"");
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append("<*D");
|
||||
ascii.Append(MakeDateStringGnuStep(date));
|
||||
|
||||
@@ -317,60 +317,54 @@ namespace Claunia.PropertyList
|
||||
return ascii.ToString();
|
||||
}
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
/*
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
|
||||
ascii.append(NEWLINE);
|
||||
String[] keys = allKeys();
|
||||
for (String key : keys) {
|
||||
NSObject val = objectForKey(key);
|
||||
indent(ascii, level + 1);
|
||||
ascii.append("\"");
|
||||
ascii.append(NSString.escapeStringForASCII(key));
|
||||
ascii.append("\" =");
|
||||
Class<?> objClass = val.getClass();
|
||||
if (objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) {
|
||||
ascii.append(NEWLINE);
|
||||
val.toASCII(ascii, level + 2);
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
|
||||
ascii.Append(NEWLINE);
|
||||
foreach (string key in Keys) {
|
||||
NSObject val = ObjectForKey(key);
|
||||
Indent(ascii, level + 1);
|
||||
ascii.Append("\"");
|
||||
ascii.Append(NSString.EscapeStringForASCII(key));
|
||||
ascii.Append("\" =");
|
||||
Type objClass = val.GetType();
|
||||
if (objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData))) {
|
||||
ascii.Append(NEWLINE);
|
||||
val.ToASCII(ascii, level + 2);
|
||||
} else {
|
||||
ascii.append(" ");
|
||||
val.toASCII(ascii, 0);
|
||||
ascii.Append(" ");
|
||||
val.ToASCII(ascii, 0);
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN);
|
||||
ascii.append(NEWLINE);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN);
|
||||
ascii.Append(NEWLINE);
|
||||
}
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);*/
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
/*
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
|
||||
ascii.append(NEWLINE);
|
||||
String[] keys = dict.keySet().toArray(new String[dict.size()]);
|
||||
for (String key : keys) {
|
||||
NSObject val = objectForKey(key);
|
||||
indent(ascii, level + 1);
|
||||
ascii.append("\"");
|
||||
ascii.append(NSString.escapeStringForASCII(key));
|
||||
ascii.append("\" =");
|
||||
Class<?> objClass = val.getClass();
|
||||
if (objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) {
|
||||
ascii.append(NEWLINE);
|
||||
val.toASCIIGnuStep(ascii, level + 2);
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
|
||||
ascii.Append(NEWLINE);
|
||||
foreach (string key in Keys) {
|
||||
NSObject val = ObjectForKey(key);
|
||||
Indent(ascii, level + 1);
|
||||
ascii.Append("\"");
|
||||
ascii.Append(NSString.EscapeStringForASCII(key));
|
||||
ascii.Append("\" =");
|
||||
Type objClass = val.GetType();
|
||||
if (objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData))) {
|
||||
ascii.Append(NEWLINE);
|
||||
val.ToASCIIGnuStep(ascii, level + 2);
|
||||
} else {
|
||||
ascii.append(" ");
|
||||
val.toASCIIGnuStep(ascii, 0);
|
||||
ascii.Append(" ");
|
||||
val.ToASCIIGnuStep(ascii, 0);
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN);
|
||||
ascii.append(NEWLINE);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN);
|
||||
ascii.Append(NEWLINE);
|
||||
}
|
||||
indent(ascii, level);
|
||||
ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);*/
|
||||
Indent(ascii, level);
|
||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
|
||||
}
|
||||
|
||||
#region IDictionary implementation
|
||||
|
||||
@@ -337,7 +337,7 @@ namespace Claunia.PropertyList
|
||||
}
|
||||
}*/
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
if (type == BOOLEAN) {
|
||||
ascii.Append(boolValue ? "YES" : "NO");
|
||||
@@ -346,7 +346,7 @@ namespace Claunia.PropertyList
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
switch (type) {
|
||||
case INTEGER: {
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Claunia.PropertyList
|
||||
/// ASCII property lists. But this number is only a guideline it is not
|
||||
/// guaranteed that it will not be overstepped.
|
||||
/// </summary>
|
||||
readonly static int ASCII_LINE_LENGTH = 80;
|
||||
internal readonly static int ASCII_LINE_LENGTH = 80;
|
||||
|
||||
/// <summary>
|
||||
/// Generates the XML representation of the object (without XML headers or enclosing plist-tags).
|
||||
@@ -107,7 +107,7 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
||||
/// <param name="level">The indentation level of the object.</param>
|
||||
protected abstract void ToASCII(StringBuilder ascii, int level);
|
||||
internal abstract void ToASCII(StringBuilder ascii, int level);
|
||||
|
||||
/// <summary>
|
||||
/// Generates the ASCII representation of this object in the GnuStep format.
|
||||
@@ -115,7 +115,7 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
||||
/// <param name="level">The indentation level of the object.</param>
|
||||
protected abstract void ToASCIIGnuStep(StringBuilder ascii, int level);
|
||||
internal abstract void ToASCIIGnuStep(StringBuilder ascii, int level);
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that adds correct identation to the xml output.
|
||||
|
||||
@@ -284,15 +284,14 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <param name="ascii">The ASCII file string builder</param>
|
||||
/// <param name="level">The indentation level</param>
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParse
|
||||
/* Indent(ascii, level);
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
NSObject[] array = AllObjects();
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE);
|
||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.Length; i++) {
|
||||
Type objClass = array[i].GetType();
|
||||
if ((objClass.Equals(NSDictionary.GetType()) || objClass.Equals(NSArray.GetType()) || objClass.Equals(NSData.GetType()))
|
||||
if ((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData)))
|
||||
&& indexOfLastNewLine != ascii.Length) {
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
@@ -311,7 +310,7 @@ namespace Claunia.PropertyList
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
}
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN);*/
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -321,35 +320,33 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
/// <param name="ascii">The ASCII file string builder</param>
|
||||
/// <param name="level">The indentation level</param>
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
// TODO: Implement ASCIIPropertyListParse
|
||||
/*
|
||||
indent(ascii, level);
|
||||
NSObject[] array = allObjects();
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
Class<?> objClass = array[i].getClass();
|
||||
if ((objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class))
|
||||
&& indexOfLastNewLine != ascii.length()) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
array[i].toASCIIGnuStep(ascii, level + 1);
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
NSObject[] array = AllObjects();
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
|
||||
for (int i = 0; i < array.Length; i++) {
|
||||
Type objClass = array[i].GetType();
|
||||
if ((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) || objClass.Equals(typeof(NSData)))
|
||||
&& indexOfLastNewLine != ascii.Length) {
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
array[i].ToASCIIGnuStep(ascii, level + 1);
|
||||
} else {
|
||||
if (i != 0)
|
||||
ascii.append(" ");
|
||||
array[i].toASCIIGnuStep(ascii, 0);
|
||||
ascii.Append(" ");
|
||||
array[i].ToASCIIGnuStep(ascii, 0);
|
||||
}
|
||||
|
||||
if (i != array.length - 1)
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
if (i != array.Length - 1)
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||
|
||||
if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.length();
|
||||
if (ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH) {
|
||||
ascii.Append(NEWLINE);
|
||||
indexOfLastNewLine = ascii.Length;
|
||||
}
|
||||
}
|
||||
ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN);*/
|
||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Claunia.PropertyList
|
||||
out.write(bytes);
|
||||
}*/
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append("\"");
|
||||
//According to https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
||||
@@ -194,25 +194,19 @@ namespace Claunia.PropertyList
|
||||
ascii.Append("\"");
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append("\"");
|
||||
ascii.Append(EscapeStringForASCII(content));
|
||||
ascii.Append("\"");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param s
|
||||
* @return The unescaped string.
|
||||
*/
|
||||
/// <summary>
|
||||
/// Escapes a string for use in ASCII property lists.
|
||||
/// </summary>
|
||||
/// <returns>The unescaped string.</returns>
|
||||
/// <param name="s">S.</param>
|
||||
static string EscapeStringForASCII(string s) {
|
||||
internal static string EscapeStringForASCII(string s) {
|
||||
string outString = "";
|
||||
char[] cArray = s.ToCharArray();
|
||||
for (int i = 0; i < cArray.Length; i++) {
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace Claunia.PropertyList
|
||||
/// a maximum count.
|
||||
/// </summary>
|
||||
/// <param name="fs">The Stream pointing to the data that should be stored in the array.</param>
|
||||
static byte[] ReadAll(Stream fs) {
|
||||
internal static byte[] ReadAll(Stream fs) {
|
||||
MemoryStream outputStream = new MemoryStream();
|
||||
byte[] buf = new byte[512];
|
||||
int read = 512;
|
||||
@@ -149,8 +149,7 @@ namespace Claunia.PropertyList
|
||||
// TODO: Implement XMLPropertyListParser
|
||||
//return XMLPropertyListParser.parse(f);
|
||||
case TYPE_ASCII:
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
//return ASCIIPropertyListParser.parse(f);
|
||||
return ASCIIPropertyListParser.Parse(f);
|
||||
default:
|
||||
throw new PropertyListFormatException("The given file is not a property list of a supported format.");
|
||||
}
|
||||
@@ -170,8 +169,7 @@ namespace Claunia.PropertyList
|
||||
// TODO: Implement XMLPropertyListParser
|
||||
//return XMLPropertyListParser.parse(bytes);
|
||||
case TYPE_ASCII:
|
||||
// TODO: Implement ASCIIPropertyListParser
|
||||
//return ASCIIPropertyListParser.parse(bytes);
|
||||
return ASCIIPropertyListParser.Parse(bytes);
|
||||
default:
|
||||
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||
}
|
||||
@@ -287,8 +285,7 @@ namespace Claunia.PropertyList
|
||||
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.Write(root.ToASCIIPropertyList());
|
||||
w.Close();
|
||||
fous.Close();
|
||||
}
|
||||
@@ -341,8 +338,7 @@ namespace Claunia.PropertyList
|
||||
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.Write(root.ToGnuStepASCIIPropertyList());
|
||||
w.Close();
|
||||
fous.Close();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Claunia.PropertyList
|
||||
out.write(bytes);
|
||||
}*/
|
||||
|
||||
protected override void ToASCII(StringBuilder ascii, int level) {
|
||||
internal override void ToASCII(StringBuilder ascii, int level) {
|
||||
Indent(ascii, level);
|
||||
ascii.Append("\"");
|
||||
for (int i = 0; i < bytes.Length; i++) {
|
||||
@@ -91,7 +91,7 @@ namespace Claunia.PropertyList
|
||||
ascii.Append("\"");
|
||||
}
|
||||
|
||||
protected override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
|
||||
ToASCII(ascii, level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<Compile Include="NSDictionary.cs" />
|
||||
<Compile Include="PropertyListFormatException.cs" />
|
||||
<Compile Include="PropertyListParser.cs" />
|
||||
<Compile Include="ASCIIPropertyListParser.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
|
||||
Reference in New Issue
Block a user