* plist-cil/UID.cs:

* plist-cil/NSData.cs:
	* plist-cil/NSDate.cs:
	* plist-cil/NSArray.cs:
	* plist-cil/NSNumber.cs:
	* plist-cil/NSString.cs:
	* plist-cil/NSObject.cs:
	* plist-cil/NSDictionary.cs:
	* plist-cil/plist-cil.csproj:
	* plist-cil/PropertyListParser.cs:
	* plist-cil/BinaryPropertyListWriter.cs:
	  Implement BinaryPropertyListWriter.

	* plist-cil/NSSet.cs:
	* plist-cil/BinaryPropertyListParser.cs:
	  Implement BinaryPropertyListWriter.
	Add way to make ordered NSSet.
This commit is contained in:
2015-02-19 17:41:33 +00:00
parent d7d76923cb
commit 018926d28a
14 changed files with 429 additions and 119 deletions

View File

@@ -304,7 +304,7 @@ namespace Claunia.PropertyList
int length = lenAndoffset[0];
int contentOffset = lenAndoffset[1];
NSSet set = new NSSet();
NSSet set = new NSSet(true);
for (int i = 0; i < length; i++) {
int objRef = (int) ParseUnsignedInt(CopyOfRange(bytes,
offset + contentOffset + i * objectRefSize,

View File

@@ -0,0 +1,301 @@
// 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;
namespace Claunia.PropertyList
{
/// <summary>
/// A BinaryPropertyListWriter is a helper class for writing out
/// binary property list files. It contains an output stream and
/// various structures for keeping track of which NSObjects have
/// already been serialized, and where they were put in the file.
/// </summary>
/// @author Keith Randall
public class BinaryPropertyListWriter
{
public const int VERSION_00 = 0;
public const int VERSION_10 = 10;
public const int VERSION_15 = 15;
public const int VERSION_20 = 20;
/// <summary>
/// Finds out the minimum binary property list format version that
/// can be used to save the given NSObject tree.
/// </summary>
/// <returns>Version code</returns>
/// <param name="root">Object root.</param>
private static int GetMinimumRequiredVersion(NSObject root) {
int minVersion = VERSION_00;
if (root == null) {
minVersion = VERSION_10;
}
if (root is NSDictionary) {
NSDictionary dict = (NSDictionary) root;
foreach (NSObject o in dict.GetDictionary().Values) {
int v = GetMinimumRequiredVersion(o);
if (v > minVersion)
minVersion = v;
}
} else if (root is NSArray) {
NSArray array = (NSArray) root;
foreach (NSObject o in array.GetArray()) {
int v = GetMinimumRequiredVersion(o);
if (v > minVersion)
minVersion = v;
}
} else if (root is NSSet) {
//Sets are only allowed in property lists v1+
minVersion = VERSION_10;
NSSet set = (NSSet) root;
foreach (NSObject o in set.AllObjects()) {
int v = GetMinimumRequiredVersion(o);
if (v > minVersion)
minVersion = v;
}
}
return minVersion;
}
/// <summary>
/// Writes a binary plist file with the given object as the root.
/// </summary>
/// <param name="file">the file to write to</param>
/// <param name="root">the source of the data to write to the file</param>
/// <exception cref="IOException"></exception>
public static void Write(FileInfo file, NSObject root) {
FileStream fous = file.OpenWrite();
Write(fous, root);
fous.Close();
}
/// <summary>
/// Writes a binary plist serialization of the given object as the root.
/// </summary>
/// <param name="outStream">the stream to write to</param>
/// <param name="root">the source of the data to write to the stream</param>
/// <exception cref="IOException"></exception>
public static void Write(Stream outStream, NSObject root) {
int minVersion = GetMinimumRequiredVersion(root);
if (minVersion > VERSION_00) {
string versionString = ((minVersion == VERSION_10) ? "v1.0" : ((minVersion == VERSION_15) ? "v1.5" : ((minVersion == VERSION_20) ? "v2.0" : "v0.0")));
throw new IOException("The given property list structure cannot be saved. " +
"The required version of the binary format (" + versionString + ") is not yet supported.");
}
BinaryPropertyListWriter w = new BinaryPropertyListWriter(outStream, minVersion);
w.Write(root);
}
/// <summary>
/// Writes a binary plist serialization of the given object as the root
/// into a byte array.
/// </summary>
/// <returns>The byte array containing the serialized property list</returns>
/// <param name="root">The root object of the property list</param>
/// <exception cref="IOException"></exception>
public static byte[] WriteToArray(NSObject root) {
MemoryStream bout = new MemoryStream();
Write(bout, root);
return bout.ToArray();
}
private int version = VERSION_00;
// raw output stream to result file
private Stream outStream;
// # of bytes written so far
private long count;
// map from object to its ID
private Dictionary<NSObject, int> idMap = new Dictionary<NSObject, int>();
private int idSizeInBytes;
/// <summary>
/// Creates a new binary property list writer
/// </summary>
/// <param name="outStr">The output stream into which the binary property list will be written</param>
/// <exception cref="IOException">If an error occured while writing to the stream</exception>
BinaryPropertyListWriter(Stream outStr) {
outStream = outStr;
}
BinaryPropertyListWriter(Stream outStr, int version) {
this.version = version;
outStream = outStr;
}
void Write(NSObject root) {
// magic bytes
Write(new byte[]{(byte)'b', (byte)'p', (byte)'l', (byte)'i', (byte)'s', (byte)'t'});
//version
switch (version) {
case VERSION_00: {
Write(new byte[]{(byte)'0', (byte)'0'});
break;
}
case VERSION_10: {
Write(new byte[]{(byte)'1', (byte)'0'});
break;
}
case VERSION_15: {
Write(new byte[]{(byte)'1', (byte)'5'});
break;
}
case VERSION_20: {
Write(new byte[]{(byte)'2', (byte)'0'});
break;
}
}
// assign IDs to all the objects.
root.AssignIDs(this);
idSizeInBytes = ComputeIdSizeInBytes(idMap.Count);
// offsets of each object, indexed by ID
long[] offsets = new long[idMap.Count];
// write each object, save offset
foreach (KeyValuePair<NSObject, int> entry in idMap) {
NSObject obj = entry.Key;
int id = entry.Value;
offsets[id] = count;
if (obj == null) {
Write(0x00);
} else {
obj.ToBinary(this);
}
}
// write offset table
long offsetTableOffset = count;
int offsetSizeInBytes = ComputeOffsetSizeInBytes(count);
foreach (long offset in offsets) {
WriteBytes(offset, offsetSizeInBytes);
}
if (version != VERSION_15) {
// write trailer
// 6 null bytes
Write(new byte[6]);
// size of an offset
Write(offsetSizeInBytes);
// size of a ref
Write(idSizeInBytes);
// number of objects
WriteLong(idMap.Count);
// top object
int rootID;
idMap.TryGetValue(root, out rootID);
WriteLong(rootID);
// offset table offset
WriteLong(offsetTableOffset);
}
outStream.Flush();
}
internal void AssignID(NSObject obj) {
if (!idMap.ContainsKey(obj)) {
idMap.Add(obj, idMap.Count);
}
}
internal int GetID(NSObject obj) {
int ID;
idMap.TryGetValue(obj, out ID);
return ID;
}
private static int ComputeIdSizeInBytes(int numberOfIds) {
if (numberOfIds < 256) return 1;
if (numberOfIds < 65536) return 2;
return 4;
}
private int ComputeOffsetSizeInBytes(long maxOffset) {
if (maxOffset < 256) return 1;
if (maxOffset < 65536) return 2;
if (maxOffset < 4294967296L) return 4;
return 8;
}
internal void WriteIntHeader(int kind, int value) {
if (value <= 0)
throw new ArgumentException("value must be greater than 0", "value");
if (value < 15) {
Write((kind << 4) + value);
} else if (value < 256) {
Write((kind << 4) + 15);
Write(0x10);
WriteBytes(value, 1);
} else if (value < 65536) {
Write((kind << 4) + 15);
Write(0x11);
WriteBytes(value, 2);
} else {
Write((kind << 4) + 15);
Write(0x12);
WriteBytes(value, 4);
}
}
internal void Write(int b) {
byte[] bBytes= new byte[1];
bBytes[0] = (byte)b;
outStream.Write(bBytes, 0, 1);
count++;
}
internal void Write(byte[] bytes) {
outStream.Write(bytes, 0, bytes.Length);
count += bytes.Length;
}
internal void WriteBytes(long value, int bytes) {
// write low-order bytes big-endian style
for (int i = bytes - 1; i >= 0; i--) {
Write((int) (value >> (8 * i)));
}
}
internal void WriteID(int id) {
WriteBytes(id, idSizeInBytes);
}
internal void WriteLong(long value) {
WriteBytes(value, 8);
}
internal void WriteDouble(double value) {
WriteLong(BitConverter.DoubleToInt64Bits(value));
}
}
}

View File

@@ -1,3 +1,23 @@
2015-02-19 Natalia Portillo <claunia@claunia.com>
* UID.cs:
* NSData.cs:
* NSDate.cs:
* NSArray.cs:
* NSNumber.cs:
* NSString.cs:
* NSObject.cs:
* NSDictionary.cs:
* plist-cil.csproj:
* PropertyListParser.cs:
* BinaryPropertyListWriter.cs:
Implement BinaryPropertyListWriter.
* NSSet.cs:
* BinaryPropertyListParser.cs:
Implement BinaryPropertyListWriter.
Add way to make ordered NSSet.
2015-02-19 Natalia Portillo <claunia@claunia.com>
* NSDate.cs:

View File

@@ -235,22 +235,19 @@ namespace Claunia.PropertyList
xml.Append("</array>");
}
// TODO: Implement BinaryPropertyListWriter
/*
override void assignIDs(BinaryPropertyListWriter out) {
super.assignIDs(out);
for (NSObject obj : array) {
obj.assignIDs(out);
internal override void AssignIDs(BinaryPropertyListWriter outPlist) {
base.AssignIDs(outPlist);
foreach (NSObject obj in array) {
obj.AssignIDs(outPlist);
}
}
override void toBinary(BinaryPropertyListWriter out) throws IOException {
out.writeIntHeader(0xA, array.length);
for (NSObject obj : array) {
out.writeID(out.getID(obj));
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
outPlist.WriteIntHeader(0xA, array.Length);
foreach (NSObject obj in array) {
outPlist.WriteID(outPlist.GetID(obj));
}
}*/
}
/// <summary>
/// Generates a valid ASCII property list which has this NSArray as its

View File

@@ -137,13 +137,10 @@ namespace Claunia.PropertyList
xml.Append("</data>");
}
// TODO: Implement BinaryPropertyListWriter
/*
@Override
void toBinary(BinaryPropertyListWriter out) throws IOException {
out.writeIntHeader(0x4, bytes.length);
out.write(bytes);
}*/
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
outPlist.WriteIntHeader(0x4, bytes.Length);
outPlist.Write(bytes);
}
internal override void ToASCII(StringBuilder ascii, int level) {
Indent(ascii, level);

View File

@@ -133,12 +133,10 @@ namespace Claunia.PropertyList
xml.Append("</date>");
}
// TODO: ImplementBinaryPropertyListWriter
/*
public override void toBinary(BinaryPropertyListWriter out) throws IOException {
out.write(0x33);
out.writeDouble((date.getTime() - EPOCH) / 1000.0);
}*/
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
outPlist.Write(0x33);
outPlist.WriteDouble((date - EPOCH).TotalSeconds);
}
/// <summary>
/// Generates a string representation of the date.

View File

@@ -268,26 +268,23 @@ namespace Claunia.PropertyList
xml.Append("</dict>");
}
// TODO: BinaryPropertyListWriter
/*
override void assignIDs(BinaryPropertyListWriter out) {
super.assignIDs(out);
for (Map.Entry<String, NSObject> entry : dict.entrySet()) {
new NSString(entry.getKey()).assignIDs(out);
entry.getValue().assignIDs(out);
internal override void AssignIDs(BinaryPropertyListWriter outPlist) {
base.AssignIDs(outPlist);
foreach (KeyValuePair<string, NSObject> entry in dict) {
new NSString(entry.Key).AssignIDs(outPlist);
entry.Value.AssignIDs(outPlist);
}
}
override void toBinary(BinaryPropertyListWriter out) throws IOException {
out.writeIntHeader(0xD, dict.size());
Set<Map.Entry<String, NSObject>> entries = dict.entrySet();
for (Map.Entry<String, NSObject> entry : entries) {
out.writeID(out.getID(new NSString(entry.getKey())));
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
outPlist.WriteIntHeader(0xD, dict.Count);
foreach (KeyValuePair<String, NSObject> entry in dict) {
outPlist.WriteID(outPlist.GetID(new NSString(entry.Key)));
}
for (Map.Entry<String, NSObject> entry : entries) {
out.writeID(out.getID(entry.getValue()));
foreach (KeyValuePair<String, NSObject> entry in dict) {
outPlist.WriteID(outPlist.GetID(entry.Value));
}
}*/
}
/// <summary>
/// Generates a valid ASCII property list which has this NSDictionary as its

View File

@@ -299,41 +299,38 @@ namespace Claunia.PropertyList
}
}
// TODO: Implement BinaryPropertyListWriter class
/*
@Override
void toBinary(BinaryPropertyListWriter out) throws IOException {
switch (type()) {
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
switch (GetNSNumberType()) {
case INTEGER: {
if (longValue() < 0) {
out.write(0x13);
out.writeBytes(longValue(), 8);
} else if (longValue() <= 0xff) {
out.write(0x10);
out.writeBytes(longValue(), 1);
} else if (longValue() <= 0xffff) {
out.write(0x11);
out.writeBytes(longValue(), 2);
} else if (longValue() <= 0xffffffffL) {
out.write(0x12);
out.writeBytes(longValue(), 4);
if (ToLong() < 0) {
outPlist.Write(0x13);
outPlist.WriteBytes(ToLong(), 8);
} else if (ToLong() <= 0xff) {
outPlist.Write(0x10);
outPlist.WriteBytes(ToLong(), 1);
} else if (ToLong() <= 0xffff) {
outPlist.Write(0x11);
outPlist.WriteBytes(ToLong(), 2);
} else if (ToLong() <= 0xffffffffL) {
outPlist.Write(0x12);
outPlist.WriteBytes(ToLong(), 4);
} else {
out.write(0x13);
out.writeBytes(longValue(), 8);
outPlist.Write(0x13);
outPlist.WriteBytes(ToLong(), 8);
}
break;
}
case REAL: {
out.write(0x23);
out.writeDouble(doubleValue());
outPlist.Write(0x23);
outPlist.WriteDouble(ToDouble());
break;
}
case BOOLEAN: {
out.write(boolValue() ? 0x09 : 0x08);
outPlist.Write(ToBool() ? 0x09 : 0x08);
break;
}
}
}*/
}
internal override void ToASCII(StringBuilder ascii, int level) {
Indent(ascii, level);

View File

@@ -70,18 +70,15 @@ namespace Claunia.PropertyList
/// Assigns IDs to all the objects in this NSObject subtree.
/// </summary>
/// <param name="out">The writer object that handles the binary serialization.</param>
// TODO: Port BinaryPropertyListWriter class
/*
void assignIDs(BinaryPropertyListWriter out) {
out.assignID(this);
}*/
internal virtual void AssignIDs(BinaryPropertyListWriter outPlist) {
outPlist.AssignID(this);
}
/// <summary>
/// Generates the binary representation of the object.
/// </summary>
/// <param name="out">The output stream to serialize the object to.</param>
// TODO: Port BinaryPropertyListWriter class
//abstract void toBinary(BinaryPropertyListWriter out);
internal abstract void ToBinary(BinaryPropertyListWriter outPlist);
/// <summary>
/// Generates a valid XML property list including headers using this object as root.

View File

@@ -40,8 +40,7 @@ namespace Claunia.PropertyList
{
List<NSObject> set;
// No need for this. It's easier in C# to just follow NSSet unordered as-is
// private bool ordered = false;
private bool ordered = false;
/// <summary>
/// Creates an empty unordered set.
@@ -67,6 +66,12 @@ namespace Claunia.PropertyList
set = new TreeSet<NSObject>();
}*/
public NSSet(bool ordered) {
this.ordered = ordered;
set = new List<NSObject>();
}
/// <summary>
/// Create a set and fill it with the given objects.
/// </summary>
@@ -93,6 +98,13 @@ namespace Claunia.PropertyList
set.addAll(Arrays.asList(objects));
}*/
public NSSet(bool ordered, params NSObject[] objects) {
this.ordered = ordered;
set = new List<NSObject>(objects);
if (ordered)
set.Sort();
}
/// <summary>
/// Adds an object to the set.
/// </summary>
@@ -100,6 +112,8 @@ namespace Claunia.PropertyList
[MethodImpl(MethodImplOptions.Synchronized)]
public void AddObject(NSObject obj) {
set.Add(obj);
if (ordered)
set.Sort();
}
/// <summary>
@@ -109,6 +123,8 @@ namespace Claunia.PropertyList
[MethodImpl(MethodImplOptions.Synchronized)]
public void RemoveObject(NSObject obj) {
set.Remove(obj);
if (ordered)
set.Sort();
}
/// <summary>
@@ -247,6 +263,8 @@ namespace Claunia.PropertyList
Indent(xml, level);
xml.Append("<array>");
xml.Append(NSObject.NEWLINE);
if (ordered)
set.Sort();
foreach (NSObject o in set) {
o.ToXml(xml, level + 1);
xml.Append(NSObject.NEWLINE);
@@ -255,27 +273,24 @@ namespace Claunia.PropertyList
xml.Append("</array>");
}
// TODO: Implement BinaryPropertyListWriter
/*
@Override
void assignIDs(BinaryPropertyListWriter out) {
super.assignIDs(out);
for (NSObject obj : set) {
obj.assignIDs(out);
internal override void AssignIDs(BinaryPropertyListWriter outPlist) {
base.AssignIDs(outPlist);
foreach (NSObject obj in set) {
obj.AssignIDs(outPlist);
}
}
@Override
void toBinary(BinaryPropertyListWriter out) throws IOException {
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
if (ordered) {
out.writeIntHeader(0xB, set.size());
set.Sort();
outPlist.WriteIntHeader(0xB, set.Count);
} else {
out.writeIntHeader(0xC, set.size());
outPlist.WriteIntHeader(0xC, set.Count);
}
for (NSObject obj : set) {
out.writeID(out.getID(obj));
foreach (NSObject obj in set) {
outPlist.WriteID(outPlist.GetID(obj));
}
}*/
}
/// <summary>
/// Returns the ASCII representation of this set.
@@ -286,6 +301,8 @@ namespace Claunia.PropertyList
/// <param name="level">The indentation level</param>
internal override void ToASCII(StringBuilder ascii, int level) {
Indent(ascii, level);
if (ordered)
set.Sort();
NSObject[] array = AllObjects();
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);
@@ -322,6 +339,8 @@ namespace Claunia.PropertyList
/// <param name="level">The indentation level</param>
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) {
Indent(ascii, level);
if (ordered)
set.Sort();
NSObject[] array = AllObjects();
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE);

View File

@@ -151,37 +151,27 @@ namespace Claunia.PropertyList
xml.Append("</string>");
}
// TODO: Implement BinaryPropertyListWriter
/*
@Override
public void toBinary(BinaryPropertyListWriter out) throws IOException {
CharBuffer charBuf = CharBuffer.wrap(content);
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
int kind;
ByteBuffer byteBuf;
synchronized (NSString.class) {
byte[] byteBuf;
lock (typeof(NSString)) {
if (asciiEncoder == null)
asciiEncoder = Charset.forName("ASCII").newEncoder();
else
asciiEncoder.reset();
asciiEncoder = Encoding.GetEncoding("ascii", EncoderExceptionFallback.ExceptionFallback, DecoderExceptionFallback.ExceptionFallback);
if (asciiEncoder.canEncode(charBuf)) {
try {
kind = 0x5; // standard ASCII
byteBuf = asciiEncoder.encode(charBuf);
} else {
byteBuf = asciiEncoder.GetBytes(content);
} catch {
if (utf16beEncoder == null)
utf16beEncoder = Charset.forName("UTF-16BE").newEncoder();
else
utf16beEncoder.reset();
utf16beEncoder = Encoding.BigEndianUnicode;
kind = 0x6; // UTF-16-BE
byteBuf = utf16beEncoder.encode(charBuf);
byteBuf = utf16beEncoder.GetBytes(content);
}
}
byte[] bytes = new byte[byteBuf.remaining()];
byteBuf.get(bytes);
out.writeIntHeader(kind, content.length());
out.write(bytes);
}*/
outPlist.WriteIntHeader(kind, content.Length);
outPlist.Write(byteBuf);
}
internal override void ToASCII(StringBuilder ascii, int level) {
Indent(ascii, level);

View File

@@ -229,8 +229,7 @@ namespace Claunia.PropertyList
string parent = outFile.DirectoryName;
if (!Directory.Exists(parent))
Directory.CreateDirectory(parent);
// TODO: Implement BinaryPropertyListWriter
//BinaryPropertyListWriter.write(out, root);
BinaryPropertyListWriter.Write(outFile, root);
}
/// <summary>
@@ -240,8 +239,7 @@ namespace Claunia.PropertyList
/// <param name="outStream">The output stream.</param>
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
public static void SaveAsBinary(NSObject root, Stream outStream) {
// TODO: Implement BinaryPropertyListWriter
//BinaryPropertyListWriter.write(out, root);
//BinaryPropertyListWriter.write(outStream, root);
}
/// <summary>

View File

@@ -74,12 +74,10 @@ namespace Claunia.PropertyList
xml.Append("</string>");
}
// TODO: Implement BinaryPropertyListWriter
/*
void override toBinary(BinaryPropertyListWriter out) throws IOException {
out.write(0x80 + bytes.length - 1);
out.write(bytes);
}*/
internal override void ToBinary(BinaryPropertyListWriter outPlist) {
outPlist.Write(0x80 + bytes.Length - 1);
outPlist.Write(bytes);
}
internal override void ToASCII(StringBuilder ascii, int level) {
Indent(ascii, level);

View File

@@ -46,6 +46,7 @@
<Compile Include="PropertyListParser.cs" />
<Compile Include="ASCIIPropertyListParser.cs" />
<Compile Include="BinaryPropertyListParser.cs" />
<Compile Include="BinaryPropertyListWriter.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>