mirror of
https://github.com/claunia/plist-cil.git
synced 2025-12-16 19:14:26 +00:00
General refactor and clean-up.
This commit is contained in:
51
README.md
51
README.md
@@ -7,15 +7,16 @@ It is mostly based on [dd-plist for Java](https://github.com/3breadt/dd-plist).
|
|||||||
And as it, this is licensed under the terms of the MIT license.
|
And as it, this is licensed under the terms of the MIT license.
|
||||||
|
|
||||||
Property lists are files used to store user settings and serialized objects.
|
Property lists are files used to store user settings and serialized objects.
|
||||||
They originate from the NeXTSTEP programming environment and are now a basic part of thhe Cocoa framework (macOS and iOS) as well as the GNUstep framework.
|
They originate from the NeXTSTEP programming environment and are now a basic part of thhe Cocoa framework (macOS and
|
||||||
|
iOS) as well as the GNUstep framework.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Read / write property lists from / to files, streams or byte arrays
|
* Read / write property lists from / to files, streams or byte arrays
|
||||||
* Convert between property lists formats
|
* Convert between property lists formats
|
||||||
* Property list contents are provided as objects from the NeXTSTEP environment (NSDictionary, NSArray, NSString, etc.)
|
* Property list contents are provided as objects from the NeXTSTEP environment (NSDictionary, NSArray, NSString, etc.)
|
||||||
* Serialize native .NET data structures to property list objects
|
* Serialize native .NET data structures to property list objects
|
||||||
* Deserialize from property list objects to native .NET data structures
|
* Deserialize from property list objects to native .NET data structures
|
||||||
|
|
||||||
## Supported formats
|
## Supported formats
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ They originate from the NeXTSTEP programming environment and are now a basic par
|
|||||||
* Cocoa / NeXTSTEP / GNUstep ASCII
|
* Cocoa / NeXTSTEP / GNUstep ASCII
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
plist-cil targets:
|
plist-cil targets:
|
||||||
|
|
||||||
- .NET Framework 4.5,
|
- .NET Framework 4.5,
|
||||||
@@ -32,16 +34,20 @@ plist-cil targets:
|
|||||||
- .NET Core 3.1.
|
- .NET Core 3.1.
|
||||||
- .NET 5.0
|
- .NET 5.0
|
||||||
|
|
||||||
This means it should be compatible with Mono, Xamarin.iOS, Xamarin.Mac, UWP, etc. If you find an incompatibility, please create an issue.
|
This means it should be compatible with Mono, Xamarin.iOS, Xamarin.Mac, UWP, etc. If you find an incompatibility, please
|
||||||
|
create an issue.
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
The latest releases can be downloaded [here](https://github.com/claunia/plist-cil/releases).
|
The latest releases can be downloaded [here](https://github.com/claunia/plist-cil/releases).
|
||||||
|
|
||||||
## NuGet support
|
## NuGet support
|
||||||
You can download the NuGet package directly from the [release](https://github.com/claunia/plist-cil/releases) page or from the [NuGet Gallery](https://www.nuget.org/) or from [here](https://www.nuget.org/packages/plist-cil/).
|
|
||||||
|
You can download the NuGet package directly from the [release](https://github.com/claunia/plist-cil/releases) page or
|
||||||
|
from the [NuGet Gallery](https://www.nuget.org/) or from [here](https://www.nuget.org/packages/plist-cil/).
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
|
|
||||||
The API documentation is included in the download.
|
The API documentation is included in the download.
|
||||||
|
|
||||||
When you encounter a bug please report it by on the [issue tracker](https://github.com/claunia/plist-cil/issues).
|
When you encounter a bug please report it by on the [issue tracker](https://github.com/claunia/plist-cil/issues).
|
||||||
@@ -50,28 +56,39 @@ When you encounter a bug please report it by on the [issue tracker](https://gith
|
|||||||
|
|
||||||
### Reading
|
### Reading
|
||||||
|
|
||||||
Parsing can be done with the PropertyListParser class. You can feed the `PropertyListParser` with a `FileInfo`, a `Stream` or a `byte` array.
|
Parsing can be done with the PropertyListParser class. You can feed the `PropertyListParser` with a `FileInfo`, a
|
||||||
The `Parse` method of the `PropertyListParser` will parse the input and give you a `NSObject` as result. Generally this is a `NSDictionary` but it can also be a `NSArray`.
|
`Stream` or a `byte` array.
|
||||||
|
The `Parse` method of the `PropertyListParser` will parse the input and give you a `NSObject` as result. Generally this
|
||||||
|
is a `NSDictionary` but it can also be a `NSArray`.
|
||||||
|
|
||||||
_Note: Property lists created by `NSKeyedArchiver` are not yet supported._
|
_Note: Property lists created by `NSKeyedArchiver` are not yet supported._
|
||||||
|
|
||||||
You can then navigate the contents of the property lists using the various classes extending `NSObject`. These are modeled in such a way as to closely resemble the respective Cocoa classes.
|
You can then navigate the contents of the property lists using the various classes extending `NSObject`. These are
|
||||||
|
modeled in such a way as to closely resemble the respective Cocoa classes.
|
||||||
|
|
||||||
You can also directly convert the contained `NSObject` objects into native .NET Objects with the `NSOBject.ToObject()` method. Using this method you can avoid working with `NSObject` instances altogether.
|
You can also directly convert the contained `NSObject` objects into native .NET Objects with the `NSOBject.ToObject()`
|
||||||
|
method. Using this method you can avoid working with `NSObject` instances altogether.
|
||||||
|
|
||||||
### Writing
|
### Writing
|
||||||
|
|
||||||
You can create your own property list using the various constructors of the different `NSObject` classes. Or you can wrap existing native .NET structures with the method `NSObject.Wrap(Object o)`. Just make sure that the root object of the property list is either a `NSDictionary` (can be created from objects of the type `Dictionary<string, Object>`) or a `NSArray` (can be created from object arrays).
|
You can create your own property list using the various constructors of the different `NSObject` classes. Or you can
|
||||||
|
wrap existing native .NET structures with the method `NSObject.Wrap(Object o)`. Just make sure that the root object of
|
||||||
|
the property list is either a `NSDictionary` (can be created from objects of the type `Dictionary<string, Object>`) or a
|
||||||
|
`NSArray` (can be created from object arrays).
|
||||||
|
|
||||||
For building a XML property list you can then call the `ToXml` method on the root object of your property list. It will give you an UTF-8 `string` containing the property list in XML format.
|
For building a XML property list you can then call the `ToXml` method on the root object of your property list. It will
|
||||||
|
give you an UTF-8 `string` containing the property list in XML format.
|
||||||
|
|
||||||
If you want to have the property list in binary format use the `BinaryPropertyListWriter` class. It can write the binary property list directly to a file or to a `Stream`.
|
If you want to have the property list in binary format use the `BinaryPropertyListWriter` class. It can write the binary
|
||||||
|
property list directly to a file or to a `Stream`.
|
||||||
|
|
||||||
When you directly want to save your property list to a file, you can also use the `SaveAsXml` or `SaveAsBinary` methods of the `PropertyListParser` class.
|
When you directly want to save your property list to a file, you can also use the `SaveAsXml` or `SaveAsBinary` methods
|
||||||
|
of the `PropertyListParser` class.
|
||||||
|
|
||||||
### Converting
|
### Converting
|
||||||
|
|
||||||
For converting a file into another format there exist convenience methods in the `PropertyListParser` class: `ConvertToXml`, `ConvertToBinary`, `ConvertToASCII` and `ConvertToGnuStepASCII`.
|
For converting a file into another format there exist convenience methods in the `PropertyListParser` class:
|
||||||
|
`ConvertToXml`, `ConvertToBinary`, `ConvertToASCII` and `ConvertToGnuStepASCII`.
|
||||||
|
|
||||||
## Code snippets
|
## Code snippets
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"projectId":"c0f6bf32-f9bb-44e9-8988-33fb7a11f847","projectName":"plist-cil"}
|
{"projectId": "c0f6bf32-f9bb-44e9-8988-33fb7a11f847", "projectName": "plist-cil"}
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|
||||||
namespace Claunia.PropertyList.Benchmark
|
namespace Claunia.PropertyList.Benchmark;
|
||||||
|
|
||||||
|
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class BinaryPropertyListParserBenchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50), MemoryDiagnoser]
|
|
||||||
public class BinaryPropertyListParserBenchmarks
|
|
||||||
{
|
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
@@ -19,5 +20,4 @@ namespace Claunia.PropertyList.Benchmark
|
|||||||
|
|
||||||
return nsObject;
|
return nsObject;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using BenchmarkDotNet.Jobs;
|
using BenchmarkDotNet.Jobs;
|
||||||
|
|
||||||
namespace Claunia.PropertyList.Benchmark
|
namespace Claunia.PropertyList.Benchmark;
|
||||||
|
|
||||||
|
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class BinaryPropertyListWriterBenchmarks
|
||||||
{
|
{
|
||||||
[SimpleJob(RuntimeMoniker.NetCoreApp50), MemoryDiagnoser]
|
|
||||||
public class BinaryPropertyListWriterBenchmarks
|
|
||||||
{
|
|
||||||
NSObject data;
|
NSObject data;
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
@@ -13,5 +14,4 @@ namespace Claunia.PropertyList.Benchmark
|
|||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public byte[] WriteLargePropertylistTest() => BinaryPropertyListWriter.WriteToArray(data);
|
public byte[] WriteLargePropertylistTest() => BinaryPropertyListWriter.WriteToArray(data);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
using BenchmarkDotNet.Running;
|
using BenchmarkDotNet.Running;
|
||||||
|
|
||||||
namespace Claunia.PropertyList.Benchmark
|
namespace Claunia.PropertyList.Benchmark;
|
||||||
|
|
||||||
|
internal class Program
|
||||||
{
|
{
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
BenchmarkRunner.Run<BinaryPropertyListParserBenchmarks>();
|
BenchmarkRunner.Run<BinaryPropertyListParserBenchmarks>();
|
||||||
BenchmarkRunner.Run<BinaryPropertyListWriterBenchmarks>();
|
BenchmarkRunner.Run<BinaryPropertyListWriterBenchmarks>();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -7,11 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.15.2" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.15.2"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\plist-cil\plist-cil.csproj" />
|
<ProjectReference Include="..\plist-cil\plist-cil.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||||
|
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xml:space="preserve">
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Claunia/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Claunia/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dreibrodt/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dreibrodt/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@@ -1,68 +1,99 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class BinaryPropertyListParserTests
|
||||||
{
|
{
|
||||||
public class BinaryPropertyListParserTests
|
[Theory]
|
||||||
{
|
[InlineData(new byte[]
|
||||||
[Theory, InlineData(new byte[]
|
|
||||||
{
|
{
|
||||||
0x08
|
0x08
|
||||||
}, 0x08), InlineData(new byte[]
|
},
|
||||||
|
0x08)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07
|
||||||
}, 7), InlineData(new byte[]
|
},
|
||||||
|
7)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x00, 0x0e, 0x47, 0x7b
|
0x00, 0x0e, 0x47, 0x7b
|
||||||
}, 0x00000000000e477b)]
|
},
|
||||||
|
0x00000000000e477b)]
|
||||||
public void ParseUnsignedIntTest(byte[] binaryValue, int expectedValue) =>
|
public void ParseUnsignedIntTest(byte[] binaryValue, int expectedValue) =>
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseUnsignedInt(binaryValue));
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseUnsignedInt(binaryValue));
|
||||||
|
|
||||||
[Theory, InlineData(new byte[]
|
[Theory]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x57
|
0x57
|
||||||
}, 0x57), InlineData(new byte[]
|
},
|
||||||
|
0x57)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34
|
0x12, 0x34
|
||||||
}, 0x1234), InlineData(new byte[]
|
},
|
||||||
|
0x1234)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34, 0x56
|
0x12, 0x34, 0x56
|
||||||
}, 0x123456), InlineData(new byte[]
|
},
|
||||||
|
0x123456)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x40, 0x2d, 0xf8, 0x4d
|
0x40, 0x2d, 0xf8, 0x4d
|
||||||
}, 0x402df84d), InlineData(new byte[]
|
},
|
||||||
|
0x402df84d)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a
|
0x12, 0x34, 0x56, 0x78, 0x9a
|
||||||
}, 0x123456789a), InlineData(new byte[]
|
},
|
||||||
|
0x123456789a)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc
|
||||||
}, 0x123456789abc), InlineData(new byte[]
|
},
|
||||||
|
0x123456789abc)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde
|
||||||
}, 0x123456789abcde), InlineData(new byte[]
|
},
|
||||||
|
0x123456789abcde)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
||||||
}, 0x41b483982a000000), InlineData(new byte[]
|
},
|
||||||
|
0x41b483982a000000)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
||||||
}, unchecked((long)0xfffffffffffffc19)), InlineData(new byte[]
|
},
|
||||||
|
unchecked((long)0xfffffffffffffc19))]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
||||||
}, unchecked((long)0xfffffffffffffc19))]
|
},
|
||||||
|
unchecked((long)0xfffffffffffffc19))]
|
||||||
public void ParseLongTest(byte[] binaryValue, long expectedValue) =>
|
public void ParseLongTest(byte[] binaryValue, long expectedValue) =>
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseLong(binaryValue));
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseLong(binaryValue));
|
||||||
|
|
||||||
[Theory, InlineData(new byte[]
|
[Theory]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
||||||
}, 344168490), InlineData(new byte[]
|
},
|
||||||
|
344168490)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x40, 0x09, 0x21, 0xf9, 0xf0, 0x1b, 0x86, 0x6e
|
0x40, 0x09, 0x21, 0xf9, 0xf0, 0x1b, 0x86, 0x6e
|
||||||
}, 3.14159), InlineData(new byte[]
|
},
|
||||||
|
3.14159)]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x40, 0x2d, 0xf8, 0x4d
|
0x40, 0x2d, 0xf8, 0x4d
|
||||||
}, 2.71828007698059)]
|
},
|
||||||
|
2.71828007698059)]
|
||||||
public void ParseDoubleTest(byte[] binaryValue, double expectedValue) =>
|
public void ParseDoubleTest(byte[] binaryValue, double expectedValue) =>
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseDouble(binaryValue), 14);
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseDouble(binaryValue), 14);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class BinaryPropertyListWriterTests
|
||||||
{
|
{
|
||||||
public class BinaryPropertyListWriterTests
|
|
||||||
{
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Roundtrip2Test()
|
public void Roundtrip2Test()
|
||||||
{
|
{
|
||||||
@@ -38,8 +38,11 @@ namespace plistcil.test
|
|||||||
|
|
||||||
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
||||||
|
|
||||||
var writer = new BinaryPropertyListWriter(validatingStream);
|
var writer = new BinaryPropertyListWriter(validatingStream)
|
||||||
writer.ReuseObjectIds = false;
|
{
|
||||||
|
ReuseObjectIds = false
|
||||||
|
};
|
||||||
|
|
||||||
writer.Write(root);
|
writer.Write(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,5 +85,4 @@ namespace plistcil.test
|
|||||||
|
|
||||||
writer.Write(root);
|
writer.Write(root);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,10 +27,10 @@ using System.IO;
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public static class IssueTest
|
||||||
{
|
{
|
||||||
public static class IssueTest
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Makes sure that binary data is line-wrapped correctly when being serialized, in a scenario where the binary
|
/// Makes sure that binary data is line-wrapped correctly when being serialized, in a scenario where the binary
|
||||||
/// data is not indented (no leading whitespace).
|
/// data is not indented (no leading whitespace).
|
||||||
@@ -128,17 +128,17 @@ namespace plistcil.test
|
|||||||
[Fact(Skip = "Support for property lists with a root element which is not plist is not implemented")]
|
[Fact(Skip = "Support for property lists with a root element which is not plist is not implemented")]
|
||||||
public static void TestIssue30()
|
public static void TestIssue30()
|
||||||
{
|
{
|
||||||
#pragma warning disable 219
|
#pragma warning disable 219
|
||||||
var arr = (NSArray)PropertyListParser.Parse(new FileInfo("test-files/issue30.plist"));
|
var arr = (NSArray)PropertyListParser.Parse(new FileInfo("test-files/issue30.plist"));
|
||||||
#pragma warning restore 219
|
#pragma warning restore 219
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue33()
|
public static void TestIssue33()
|
||||||
{
|
{
|
||||||
#pragma warning disable 219
|
#pragma warning disable 219
|
||||||
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue33.pbxproj"));
|
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue33.pbxproj"));
|
||||||
#pragma warning restore 219
|
#pragma warning restore 219
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -188,5 +188,4 @@ namespace plistcil.test
|
|||||||
Assert.IsType<double>(weight);
|
Assert.IsType<double>(weight);
|
||||||
Assert.Equal(10d, (double)weight);
|
Assert.Equal(10d, (double)weight);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class NSArrayTests
|
||||||
{
|
{
|
||||||
public class NSArrayTests
|
|
||||||
{
|
|
||||||
/// <summary>Tests the addition of a .NET object to the NSArray</summary>
|
/// <summary>Tests the addition of a .NET object to the NSArray</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddAndContainsObjectTest()
|
public void AddAndContainsObjectTest()
|
||||||
@@ -87,5 +87,4 @@ namespace plistcil.test
|
|||||||
|
|
||||||
Assert.Empty(array);
|
Assert.Empty(array);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class NSDateTests
|
||||||
{
|
{
|
||||||
public class NSDateTests
|
|
||||||
{
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void ConstructorTest()
|
public static void ConstructorTest()
|
||||||
{
|
{
|
||||||
@@ -23,5 +23,4 @@ namespace plistcil.test
|
|||||||
|
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class NSNumberTests
|
||||||
{
|
{
|
||||||
public class NSNumberTests
|
|
||||||
{
|
|
||||||
public static IEnumerable<object[]> SpanConstructorTestData() => new List<object[]>
|
public static IEnumerable<object[]> SpanConstructorTestData() => new List<object[]>
|
||||||
{
|
{
|
||||||
// INTEGER values
|
// INTEGER values
|
||||||
@@ -124,22 +124,14 @@ namespace plistcil.test
|
|||||||
// 4-byte value (float)
|
// 4-byte value (float)
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
"\0\0\0\0"u8.ToArray(),
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.0
|
NSNumber.REAL, false, 0, 0.0
|
||||||
},
|
},
|
||||||
|
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
"A \0\0"u8.ToArray(),
|
||||||
{
|
|
||||||
0x41, 0x20, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, true, 10, 10.0
|
NSNumber.REAL, true, 10, 10.0
|
||||||
},
|
},
|
||||||
|
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
new byte[]
|
||||||
@@ -152,22 +144,14 @@ namespace plistcil.test
|
|||||||
// 8-byte value (double)
|
// 8-byte value (double)
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
"\0\0\0\0\0\0\0\0"u8.ToArray(),
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.0
|
NSNumber.REAL, false, 0, 0.0
|
||||||
},
|
},
|
||||||
|
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
"@$\0\0\0\0\0\0"u8.ToArray(),
|
||||||
{
|
|
||||||
0x40, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, true, 10, 10.0
|
NSNumber.REAL, true, 10, 10.0
|
||||||
},
|
},
|
||||||
|
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new byte[]
|
new byte[]
|
||||||
@@ -178,7 +162,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(SpanConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(SpanConstructorTestData))]
|
||||||
public void SpanConstructorTest(byte[] data, int type, bool boolValue, long longValue, double doubleValue)
|
public void SpanConstructorTest(byte[] data, int type, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber((Span<byte>)data, type);
|
var number = new NSNumber((Span<byte>)data, type);
|
||||||
@@ -228,7 +213,8 @@ namespace plistcil.test
|
|||||||
// The value being used comes seen in a real property list:
|
// The value being used comes seen in a real property list:
|
||||||
// <key>TimeZoneOffsetFromUTC</key>
|
// <key>TimeZoneOffsetFromUTC</key>
|
||||||
// <real>7200.000000</real>
|
// <real>7200.000000</real>
|
||||||
[Fact, UseCulture("en-US")]
|
[Fact]
|
||||||
|
[UseCulture("en-US")]
|
||||||
public static void ParseNumberEnTest()
|
public static void ParseNumberEnTest()
|
||||||
{
|
{
|
||||||
var number = new NSNumber("7200.000001");
|
var number = new NSNumber("7200.000001");
|
||||||
@@ -236,7 +222,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(7200.000001d, number.ToDouble());
|
Assert.Equal(7200.000001d, number.ToDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, UseCulture("nl-BE")]
|
[Fact]
|
||||||
|
[UseCulture("nl-BE")]
|
||||||
public static void ParseNumberNlTest()
|
public static void ParseNumberNlTest()
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
// As seen in a real property list:
|
||||||
@@ -247,7 +234,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(7200.000001d, number.ToDouble());
|
Assert.Equal(7200.000001d, number.ToDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, UseCulture("en-US")]
|
[Fact]
|
||||||
|
[UseCulture("en-US")]
|
||||||
public static void ParseNumberEnTest2()
|
public static void ParseNumberEnTest2()
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
// As seen in a real property list:
|
||||||
@@ -258,7 +246,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(7200d, number.ToDouble());
|
Assert.Equal(7200d, number.ToDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact, UseCulture("nl-BE")]
|
[Fact]
|
||||||
|
[UseCulture("nl-BE")]
|
||||||
public static void ParseNumberNlTest2()
|
public static void ParseNumberNlTest2()
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
// As seen in a real property list:
|
||||||
@@ -338,7 +327,6 @@ namespace plistcil.test
|
|||||||
{
|
{
|
||||||
"TRUE", true, 1, 1
|
"TRUE", true, 1, 1
|
||||||
},
|
},
|
||||||
|
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
"no", false, 0, 0
|
"no", false, 0, 0
|
||||||
@@ -365,7 +353,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(StringConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(StringConstructorTestData))]
|
||||||
public void StringConstructorTest(string value, bool boolValue, long longValue, double doubleValue)
|
public void StringConstructorTest(string value, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
var number = new NSNumber(value);
|
||||||
@@ -406,7 +395,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(Int32ConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(Int32ConstructorTestData))]
|
||||||
public void Int32ConstructorTest(int value, bool boolValue, long longValue, double doubleValue)
|
public void Int32ConstructorTest(int value, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
var number = new NSNumber(value);
|
||||||
@@ -440,7 +430,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(Int64ConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(Int64ConstructorTestData))]
|
||||||
public void Int64ConstructorTest(long value, bool boolValue, long longValue, double doubleValue)
|
public void Int64ConstructorTest(long value, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
var number = new NSNumber(value);
|
||||||
@@ -478,7 +469,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(DoubleConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(DoubleConstructorTestData))]
|
||||||
public void DoubleConstructorTest(double value, bool boolValue, long longValue, double doubleValue)
|
public void DoubleConstructorTest(double value, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
var number = new NSNumber(value);
|
||||||
@@ -500,7 +492,8 @@ namespace plistcil.test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory, MemberData(nameof(BoolConstructorTestData))]
|
[Theory]
|
||||||
|
[MemberData(nameof(BoolConstructorTestData))]
|
||||||
public void BoolConstructorTest(bool value, bool boolValue, long longValue, double doubleValue)
|
public void BoolConstructorTest(bool value, bool boolValue, long longValue, double doubleValue)
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
var number = new NSNumber(value);
|
||||||
@@ -519,5 +512,4 @@ namespace plistcil.test
|
|||||||
Assert.True(a.Equals(b));
|
Assert.True(a.Equals(b));
|
||||||
Assert.True(b.Equals(a));
|
Assert.True(b.Equals(a));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class NSStringTests
|
||||||
{
|
{
|
||||||
public class NSStringTests
|
|
||||||
{
|
|
||||||
const string START_TOKEN = "<string>";
|
const string START_TOKEN = "<string>";
|
||||||
const string END_TOKEN = "</string>";
|
const string END_TOKEN = "</string>";
|
||||||
|
|
||||||
@@ -25,5 +25,4 @@ namespace plistcil.test
|
|||||||
|
|
||||||
Assert.Equal(content, actualContent);
|
Assert.Equal(content, actualContent);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -29,18 +29,16 @@ using System.IO;
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public static class ParseTest
|
||||||
{
|
{
|
||||||
public static class ParseTest
|
|
||||||
{
|
|
||||||
static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
||||||
{
|
{
|
||||||
if(arrayA.Length != arrayB.Length)
|
if(arrayA.Length != arrayB.Length) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < arrayA.Length; i++)
|
for(int i = 0; i < arrayA.Length; i++)
|
||||||
if(arrayA[i] != arrayB[i])
|
if(arrayA[i] != arrayB[i]) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -85,10 +83,10 @@ namespace plistcil.test
|
|||||||
|
|
||||||
Assert.Equal(actualDate.Date, expectedDate);
|
Assert.Equal(actualDate.Date, expectedDate);
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
{
|
[
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
}));
|
]));
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
Assert.True(a.Count == 4);
|
Assert.True(a.Count == 4);
|
||||||
@@ -147,13 +145,13 @@ namespace plistcil.test
|
|||||||
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
||||||
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
||||||
|
|
||||||
Assert.True(((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011, 11, 28, 9, 21, 30,
|
Assert.True(((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011, 11, 28, 9, 21, 30, DateTimeKind.Utc)
|
||||||
DateTimeKind.Utc).ToLocalTime()));
|
.ToLocalTime()));
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
{
|
[
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
}));
|
]));
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
Assert.True(a.Count == 4);
|
Assert.True(a.Count == 4);
|
||||||
@@ -188,19 +186,19 @@ namespace plistcil.test
|
|||||||
string strg = "Hello World";
|
string strg = "Hello World";
|
||||||
|
|
||||||
byte[] bytes =
|
byte[] bytes =
|
||||||
{
|
[
|
||||||
0x00, 0xAF, 0xAF
|
0x00, 0xAF, 0xAF
|
||||||
};
|
];
|
||||||
|
|
||||||
object[] array =
|
object[] array =
|
||||||
{
|
[
|
||||||
bl, byt, shrt, i, lng, flt, dbl, date, strg, bytes
|
bl, byt, shrt, i, lng, flt, dbl, date, strg, bytes
|
||||||
};
|
];
|
||||||
|
|
||||||
int[] array2 =
|
int[] array2 =
|
||||||
{
|
[
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3000
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3000
|
||||||
};
|
];
|
||||||
|
|
||||||
List<object> list = new(array);
|
List<object> list = new(array);
|
||||||
|
|
||||||
@@ -209,15 +207,21 @@ namespace plistcil.test
|
|||||||
map.Add("long", lng);
|
map.Add("long", lng);
|
||||||
map.Add("date", date);
|
map.Add("date", date);
|
||||||
|
|
||||||
List<Dictionary<string,object>> listOfMaps = new()
|
List<Dictionary<string, object>> listOfMaps =
|
||||||
{
|
[
|
||||||
new Dictionary<string, object>
|
new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
{ "int", i },
|
{
|
||||||
{ "long", lng },
|
"int", i
|
||||||
{ "date", date }
|
},
|
||||||
|
{
|
||||||
|
"long", lng
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date", date
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
];
|
||||||
|
|
||||||
var WrappedO = NSObject.Wrap((object)bl);
|
var WrappedO = NSObject.Wrap((object)bl);
|
||||||
Assert.True(WrappedO is (NSNumber));
|
Assert.True(WrappedO is (NSNumber));
|
||||||
@@ -260,8 +264,7 @@ namespace plistcil.test
|
|||||||
byte[] data = (byte[])WrappedO.ToObject();
|
byte[] data = (byte[])WrappedO.ToObject();
|
||||||
Assert.True(data.Length == bytes.Length);
|
Assert.True(data.Length == bytes.Length);
|
||||||
|
|
||||||
for(int x = 0; x < bytes.Length; x++)
|
for(int x = 0; x < bytes.Length; x++) Assert.True(data[x] == bytes[x]);
|
||||||
Assert.True(data[x] == bytes[x]);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)array);
|
WrappedO = NSObject.Wrap((object)array);
|
||||||
Assert.True(WrappedO is (NSArray));
|
Assert.True(WrappedO is (NSArray));
|
||||||
@@ -284,7 +287,7 @@ namespace plistcil.test
|
|||||||
Assert.True(((NSNumber)dict.ObjectForKey("long")).ToLong() == lng);
|
Assert.True(((NSNumber)dict.ObjectForKey("long")).ToLong() == lng);
|
||||||
Assert.True(((NSDate)dict.ObjectForKey("date")).Date.Equals(date));
|
Assert.True(((NSDate)dict.ObjectForKey("date")).Date.Equals(date));
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)listOfMaps);
|
WrappedO = NSObject.Wrap(listOfMaps);
|
||||||
Assert.True(WrappedO is (NSArray));
|
Assert.True(WrappedO is (NSArray));
|
||||||
var arrayOfMaps = (NSArray)WrappedO;
|
var arrayOfMaps = (NSArray)WrappedO;
|
||||||
Assert.True(arrayOfMaps.Count == 1);
|
Assert.True(arrayOfMaps.Count == 1);
|
||||||
@@ -317,15 +320,25 @@ namespace plistcil.test
|
|||||||
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
||||||
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
||||||
|
|
||||||
Assert.True(((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011, 11, 28, 10, 21, 30,
|
Assert.True(((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011,
|
||||||
|
11,
|
||||||
|
28,
|
||||||
|
10,
|
||||||
|
21,
|
||||||
|
30,
|
||||||
DateTimeKind.Utc)) ||
|
DateTimeKind.Utc)) ||
|
||||||
((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011, 11, 28, 9, 21, 30,
|
((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011,
|
||||||
|
11,
|
||||||
|
28,
|
||||||
|
9,
|
||||||
|
21,
|
||||||
|
30,
|
||||||
DateTimeKind.Utc)));
|
DateTimeKind.Utc)));
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
{
|
[
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
}));
|
]));
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
Assert.True(a.Count == 4);
|
Assert.True(a.Count == 4);
|
||||||
@@ -339,5 +352,4 @@ namespace plistcil.test
|
|||||||
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testXml.plist"));
|
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testXml.plist"));
|
||||||
Assert.True(x.Equals(y));
|
Assert.True(x.Equals(y));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class PropertyListParserTests
|
||||||
{
|
{
|
||||||
public class PropertyListParserTests
|
|
||||||
{
|
|
||||||
static void ParseEmptyStreamTestDelegate()
|
static void ParseEmptyStreamTestDelegate()
|
||||||
{
|
{
|
||||||
using var stream = new MemoryStream();
|
using var stream = new MemoryStream();
|
||||||
@@ -16,5 +16,4 @@ namespace plistcil.test
|
|||||||
[Fact]
|
[Fact]
|
||||||
public static void ParseEmptyStreamTest() =>
|
public static void ParseEmptyStreamTest() =>
|
||||||
Assert.Throws<PropertyListFormatException>(ParseEmptyStreamTestDelegate);
|
Assert.Throws<PropertyListFormatException>(ParseEmptyStreamTestDelegate);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,24 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class UIDTests
|
||||||
{
|
{
|
||||||
public class UIDTests
|
[Theory]
|
||||||
{
|
[InlineData(new byte[]
|
||||||
[Theory, InlineData(new byte[]
|
|
||||||
{
|
{
|
||||||
0xAB
|
0xAB
|
||||||
}), InlineData(new byte[]
|
})]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD
|
0xAB, 0xCD
|
||||||
}), InlineData(new byte[]
|
})]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0xFE
|
0xAB, 0xCD, 0xEF, 0xFE
|
||||||
}), InlineData(new byte[]
|
})]
|
||||||
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0xFE, 0xFE, 0xEF, 0xCD, 0xAB
|
0xAB, 0xCD, 0xEF, 0xFE, 0xFE, 0xEF, 0xCD, 0xAB
|
||||||
})]
|
})]
|
||||||
@@ -46,7 +50,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB
|
0xAB
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABu, uid.ToUInt64());
|
Assert.Equal(0xABu, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -59,7 +64,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0x00
|
0xAB, 0xCD, 0xEF, 0x00
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABCDEF00, uid.ToUInt64());
|
Assert.Equal(0xABCDEF00, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -72,7 +78,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB
|
0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABCDEF0000EFCDAB, uid.ToUInt64());
|
Assert.Equal(0xABCDEF0000EFCDAB, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -85,7 +92,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0x00
|
0xAB, 0xCD, 0xEF, 0x00
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABCDEF00u, uid.ToUInt64());
|
Assert.Equal(0xABCDEF00u, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -98,7 +106,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB
|
0xAB, 0xCD, 0xEF, 0x00, 0x00, 0xEF, 0xCD, 0xAB
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABCDEF0000EFCDABu, uid.ToUInt64());
|
Assert.Equal(0xABCDEF0000EFCDABu, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -111,7 +120,8 @@ namespace plistcil.test
|
|||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB, 0xCD
|
0xAB, 0xCD
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABCDu, uid.ToUInt64());
|
Assert.Equal(0xABCDu, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
@@ -128,5 +138,4 @@ namespace plistcil.test
|
|||||||
var roundtrip = XmlPropertyListParser.ParseString(plist) as UID;
|
var roundtrip = XmlPropertyListParser.ParseString(plist) as UID;
|
||||||
Assert.Equal(0xabcdUL, roundtrip.ToUInt64());
|
Assert.Equal(0xabcdUL, roundtrip.ToUInt64());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -52,19 +52,19 @@ public class UseCultureAttribute : BeforeAfterTestAttribute
|
|||||||
/// <param name="methodUnderTest">The method under test</param>
|
/// <param name="methodUnderTest">The method under test</param>
|
||||||
public override void Before(MethodInfo methodUnderTest)
|
public override void Before(MethodInfo methodUnderTest)
|
||||||
{
|
{
|
||||||
#if NETCORE
|
#if NETCORE
|
||||||
originalCulture = CultureInfo.CurrentCulture;
|
originalCulture = CultureInfo.CurrentCulture;
|
||||||
originalUICulture = CultureInfo.CurrentUICulture;
|
originalUICulture = CultureInfo.CurrentUICulture;
|
||||||
|
|
||||||
CultureInfo.CurrentCulture = Culture;
|
CultureInfo.CurrentCulture = Culture;
|
||||||
CultureInfo.CurrentUICulture = Culture;
|
CultureInfo.CurrentUICulture = Culture;
|
||||||
#else
|
#else
|
||||||
originalCulture = Thread.CurrentThread.CurrentCulture;
|
originalCulture = Thread.CurrentThread.CurrentCulture;
|
||||||
originalUICulture = Thread.CurrentThread.CurrentUICulture;
|
originalUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||||
|
|
||||||
Thread.CurrentThread.CurrentCulture = Culture;
|
Thread.CurrentThread.CurrentCulture = Culture;
|
||||||
Thread.CurrentThread.CurrentUICulture = UICulture;
|
Thread.CurrentThread.CurrentUICulture = UICulture;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -74,12 +74,12 @@ public class UseCultureAttribute : BeforeAfterTestAttribute
|
|||||||
/// <param name="methodUnderTest">The method under test</param>
|
/// <param name="methodUnderTest">The method under test</param>
|
||||||
public override void After(MethodInfo methodUnderTest)
|
public override void After(MethodInfo methodUnderTest)
|
||||||
{
|
{
|
||||||
#if NETCORE
|
#if NETCORE
|
||||||
CultureInfo.CurrentCulture = originalCulture;
|
CultureInfo.CurrentCulture = originalCulture;
|
||||||
CultureInfo.CurrentUICulture = originalUICulture;
|
CultureInfo.CurrentUICulture = originalUICulture;
|
||||||
#else
|
#else
|
||||||
Thread.CurrentThread.CurrentCulture = originalCulture;
|
Thread.CurrentThread.CurrentCulture = originalCulture;
|
||||||
Thread.CurrentThread.CurrentUICulture = originalUICulture;
|
Thread.CurrentThread.CurrentUICulture = originalUICulture;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,14 +9,14 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Stream" /> which writes its output to a <see cref="Stream" /> and validates that the data which
|
||||||
|
/// is being written to the output stream matches the data in a reference stream.
|
||||||
|
/// </summary>
|
||||||
|
internal class ValidatingStream : Stream
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="Stream" /> which writes its output to a <see cref="Stream" /> and validates that the data which
|
|
||||||
/// is being written to the output stream matches the data in a reference stream.
|
|
||||||
/// </summary>
|
|
||||||
internal class ValidatingStream : Stream
|
|
||||||
{
|
|
||||||
readonly Stream expectedOutput;
|
readonly Stream expectedOutput;
|
||||||
readonly Stream output;
|
readonly Stream output;
|
||||||
|
|
||||||
@@ -55,8 +55,7 @@ namespace plistcil.test
|
|||||||
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<int>
|
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
|
||||||
ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
|
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -97,5 +96,4 @@ namespace plistcil.test
|
|||||||
|
|
||||||
await output.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
await output.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ using System.Linq;
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public static class ValuePreprocessorTests
|
||||||
{
|
{
|
||||||
public static class ValuePreprocessorTests
|
|
||||||
{
|
|
||||||
// lock tests to make sure temporarily added / replaced preprocessors don't interfere with the other tests in this suite
|
// lock tests to make sure temporarily added / replaced preprocessors don't interfere with the other tests in this suite
|
||||||
private static readonly object _testLock = new();
|
private static readonly object _testLock = new();
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ namespace plistcil.test
|
|||||||
lock(_testLock)
|
lock(_testLock)
|
||||||
{
|
{
|
||||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||||
byte[] testByteArray = [0x42,];
|
byte[] testByteArray = [0x42];
|
||||||
string testString = "TestString";
|
string testString = "TestString";
|
||||||
|
|
||||||
var testType = (ValuePreprocessor.Type)42;
|
var testType = (ValuePreprocessor.Type)42;
|
||||||
@@ -121,5 +121,4 @@ namespace plistcil.test
|
|||||||
// there's no registered preprocessor for byte array arguments for STRING
|
// there's no registered preprocessor for byte array arguments for STRING
|
||||||
Assert.Throws<ArgumentException>(() => ValuePreprocessor.Preprocess(testArray, ValuePreprocessor.Type.STRING));
|
Assert.Throws<ArgumentException>(() => ValuePreprocessor.Preprocess(testArray, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\plist-cil\plist-cil.csproj" />
|
<ProjectReference Include="..\plist-cil\plist-cil.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>files</key>
|
<key>files</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>PkgInfo</key>
|
<key>PkgInfo</key>
|
||||||
@@ -76,5 +76,5 @@
|
|||||||
<real>0.0</real>
|
<real>0.0</real>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<data>
|
<data>
|
||||||
MjAxMy0wMi0wMiAyMDoxNjo0MiBHTVQ6IGhhbmRsZV9tZXNzYWdlOiBBbmQgeW91IHdp
|
MjAxMy0wMi0wMiAyMDoxNjo0MiBHTVQ6IGhhbmRsZV9tZXNzYWdlOiBBbmQgeW91IHdp
|
||||||
bGwga25vdyBteSBuYW1lIGlzIHRoZSBMb3JkIHdoZW4gSSBsYXkgbXkgdmVuZ2VhbmNl
|
bGwga25vdyBteSBuYW1lIGlzIHRoZSBMb3JkIHdoZW4gSSBsYXkgbXkgdmVuZ2VhbmNl
|
||||||
IHVwb24gdGhlZS4=
|
IHVwb24gdGhlZS4=
|
||||||
</data>
|
</data>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>keyA</key>
|
<key>keyA</key>
|
||||||
<data>
|
<data>
|
||||||
MjAxMy0wMi0wMiAyMDoxNjo0MiBHTVQ6IGhhbmRsZV9tZXNzYWdlOiBBbmQgeW91IHdp
|
MjAxMy0wMi0wMiAyMDoxNjo0MiBHTVQ6IGhhbmRsZV9tZXNzYWdlOiBBbmQgeW91IHdp
|
||||||
bGwga25vdyBteSBuYW1lIGlzIHRoZSBMb3JkIHdoZW4gSSBsYXkgbXkgdmVuZ2VhbmNl
|
bGwga25vdyBteSBuYW1lIGlzIHRoZSBMb3JkIHdoZW4gSSBsYXkgbXkgdmVuZ2VhbmNl
|
||||||
IHVwb24gdGhlZS4=
|
IHVwb24gdGhlZS4=
|
||||||
</data>
|
</data>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<real>1360155352.748765</real>
|
<real>1360155352.748765</real>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -29,23 +29,23 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// 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 explicitly defined.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Resources on ASCII property list format:</para>
|
||||||
|
/// <para>https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html</para>
|
||||||
|
/// <para>Property List Programming Guide - Old-Style ASCII Property Lists</para>
|
||||||
|
/// <para>http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html</para>
|
||||||
|
/// <para>GnuStep - NSPropertyListSerialization class documentation</para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class ASCIIPropertyListParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// 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 explicitly defined.
|
|
||||||
/// </para>
|
|
||||||
/// <para>Resources on ASCII property list format:</para>
|
|
||||||
/// <para>https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html</para>
|
|
||||||
/// <para>Property List Programming Guide - Old-Style ASCII Property Lists</para>
|
|
||||||
/// <para>http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html</para>
|
|
||||||
/// <para>GnuStep - NSPropertyListSerialization class documentation</para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class ASCIIPropertyListParser
|
|
||||||
{
|
|
||||||
/// <summary>A space</summary>
|
/// <summary>A space</summary>
|
||||||
public const char WHITESPACE_SPACE = ' ';
|
public const char WHITESPACE_SPACE = ' ';
|
||||||
/// <summary>A tabulator</summary>
|
/// <summary>A tabulator</summary>
|
||||||
@@ -177,11 +177,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
/// <exception cref="FormatException">When an error occurs during parsing.</exception>
|
||||||
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
#if NATIVE_SPAN
|
#if NATIVE_SPAN
|
||||||
return ParseString(Encoding.UTF8.GetString(bytes));
|
return ParseString(Encoding.UTF8.GetString(bytes));
|
||||||
#else
|
#else
|
||||||
return ParseString(Encoding.UTF8.GetString(bytes.ToArray()));
|
return ParseString(Encoding.UTF8.GetString(bytes.ToArray()));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Parses an ASCII property list from a string.</summary>
|
/// <summary>Parses an ASCII property list from a string.</summary>
|
||||||
@@ -201,8 +201,7 @@ namespace Claunia.PropertyList
|
|||||||
bool AcceptSequence(params char[] sequence)
|
bool AcceptSequence(params char[] sequence)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < sequence.Length; i++)
|
for(int i = 0; i < sequence.Length; i++)
|
||||||
if(data[index + i] != sequence[i])
|
if(data[index + i] != sequence[i]) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -217,8 +216,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
bool symbolPresent = false;
|
bool symbolPresent = false;
|
||||||
|
|
||||||
foreach(char c in acceptableSymbols)
|
foreach(char c in acceptableSymbols) symbolPresent |= data[index] == c;
|
||||||
symbolPresent |= data[index] == c;
|
|
||||||
|
|
||||||
return symbolPresent;
|
return symbolPresent;
|
||||||
}
|
}
|
||||||
@@ -236,13 +234,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <exception cref="FormatException">If none of the expected symbols could be found.</exception>
|
/// <exception cref="FormatException">If none of the expected symbols could be found.</exception>
|
||||||
void Expect(params char[] expectedSymbols)
|
void Expect(params char[] expectedSymbols)
|
||||||
{
|
{
|
||||||
if(Accept(expectedSymbols))
|
if(Accept(expectedSymbols)) return;
|
||||||
return;
|
|
||||||
|
|
||||||
string excString = "Expected '" + expectedSymbols[0] + "'";
|
string excString = "Expected '" + expectedSymbols[0] + "'";
|
||||||
|
|
||||||
for(int i = 1; i < expectedSymbols.Length; i++)
|
for(int i = 1; i < expectedSymbols.Length; i++) excString += " or '" + expectedSymbols[i] + "'";
|
||||||
excString += " or '" + expectedSymbols[i] + "'";
|
|
||||||
|
|
||||||
excString += " but found '" + data[index] + "'";
|
excString += " but found '" + data[index] + "'";
|
||||||
|
|
||||||
@@ -288,8 +284,7 @@ namespace Claunia.PropertyList
|
|||||||
commentSkipped = false;
|
commentSkipped = false;
|
||||||
|
|
||||||
//Skip whitespaces
|
//Skip whitespaces
|
||||||
while(Accept(WHITESPACE_CARRIAGE_RETURN, WHITESPACE_NEWLINE, WHITESPACE_SPACE, WHITESPACE_TAB))
|
while(Accept(WHITESPACE_CARRIAGE_RETURN, WHITESPACE_NEWLINE, WHITESPACE_SPACE, WHITESPACE_TAB)) Skip();
|
||||||
Skip();
|
|
||||||
|
|
||||||
//Skip single line comments "//..."
|
//Skip single line comments "//..."
|
||||||
if(AcceptSequence(COMMENT_BEGIN_TOKEN, SINGLELINE_COMMENT_SECOND_TOKEN))
|
if(AcceptSequence(COMMENT_BEGIN_TOKEN, SINGLELINE_COMMENT_SECOND_TOKEN))
|
||||||
@@ -318,8 +313,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
commentSkipped = true;
|
commentSkipped = true;
|
||||||
}
|
}
|
||||||
} while(
|
} while(commentSkipped); //if a comment was skipped more whitespace or another comment can follow, so skip again
|
||||||
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>
|
/// <summary>Reads input until one of the given symbols is found.</summary>
|
||||||
@@ -362,10 +356,7 @@ namespace Claunia.PropertyList
|
|||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
//Skip Unicode byte order mark (BOM)
|
//Skip Unicode byte order mark (BOM)
|
||||||
if(data.Length >= 3 &&
|
if(data.Length >= 3 && (data[0] & 0xFF) == 0xEF && (data[1] & 0xFF) == 0xBB && (data[2] & 0xFF) == 0xBF)
|
||||||
(data[0] & 0xFF) == 0xEF &&
|
|
||||||
(data[1] & 0xFF) == 0xBB &&
|
|
||||||
(data[2] & 0xFF) == 0xBF)
|
|
||||||
Skip(3);
|
Skip(3);
|
||||||
|
|
||||||
SkipWhitespacesAndComments();
|
SkipWhitespacesAndComments();
|
||||||
@@ -405,8 +396,8 @@ namespace Claunia.PropertyList
|
|||||||
string quotedString = ParseQuotedString();
|
string quotedString = ParseQuotedString();
|
||||||
|
|
||||||
//apple dates are quoted strings of length 20 and after the 4 year digits a dash is found
|
//apple dates are quoted strings of length 20 and after the 4 year digits a dash is found
|
||||||
if(quotedString.Length == 20 &&
|
if(quotedString.Length == 20 && quotedString[4] == DATE_DATE_FIELD_DELIMITER)
|
||||||
quotedString[4] == DATE_DATE_FIELD_DELIMITER)
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new NSDate(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.DATE));
|
return new NSDate(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.DATE));
|
||||||
@@ -416,15 +407,14 @@ namespace Claunia.PropertyList
|
|||||||
//not a date? --> return string
|
//not a date? --> return string
|
||||||
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
//0-9
|
//0-9
|
||||||
if(data[index] > 0x2F &&
|
if(data[index] > 0x2F && data[index] < 0x3A) return ParseDateString();
|
||||||
data[index] < 0x3A)
|
|
||||||
return ParseDateString();
|
|
||||||
|
|
||||||
//non-numerical -> string or boolean
|
//non-numerical -> string or boolean
|
||||||
string parsedString = ParseString();
|
string parsedString = ParseString();
|
||||||
@@ -444,7 +434,7 @@ namespace Claunia.PropertyList
|
|||||||
//Skip begin token
|
//Skip begin token
|
||||||
Skip();
|
Skip();
|
||||||
SkipWhitespacesAndComments();
|
SkipWhitespacesAndComments();
|
||||||
List<NSObject> objects = new();
|
List<NSObject> objects = [];
|
||||||
|
|
||||||
while(!Accept(ARRAY_END_TOKEN))
|
while(!Accept(ARRAY_END_TOKEN))
|
||||||
{
|
{
|
||||||
@@ -519,8 +509,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
Skip();
|
Skip();
|
||||||
|
|
||||||
Expect(DATA_GSBOOL_BEGIN_TOKEN, DATA_GSDATE_BEGIN_TOKEN, DATA_GSINT_BEGIN_TOKEN,
|
Expect(DATA_GSBOOL_BEGIN_TOKEN, DATA_GSDATE_BEGIN_TOKEN, DATA_GSINT_BEGIN_TOKEN, DATA_GSREAL_BEGIN_TOKEN);
|
||||||
DATA_GSREAL_BEGIN_TOKEN);
|
|
||||||
|
|
||||||
if(Accept(DATA_GSBOOL_BEGIN_TOKEN))
|
if(Accept(DATA_GSBOOL_BEGIN_TOKEN))
|
||||||
{
|
{
|
||||||
@@ -584,8 +573,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string numericalString = ParseString();
|
string numericalString = ParseString();
|
||||||
|
|
||||||
if(numericalString.Length <= 4 ||
|
if(numericalString.Length <= 4 || numericalString[4] != DATE_DATE_FIELD_DELIMITER)
|
||||||
numericalString[4] != DATE_DATE_FIELD_DELIMITER)
|
|
||||||
return new NSString(ValuePreprocessor.Preprocess(numericalString, ValuePreprocessor.Type.STRING));
|
return new NSString(ValuePreprocessor.Preprocess(numericalString, ValuePreprocessor.Type.STRING));
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -605,9 +593,13 @@ namespace Claunia.PropertyList
|
|||||||
/// whitespace, delimiter token or assignment token.
|
/// whitespace, delimiter token or assignment token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The string found at the current parsing position.</returns>
|
/// <returns>The string found at the current parsing position.</returns>
|
||||||
string ParseString() => ReadInputUntil(WHITESPACE_SPACE, WHITESPACE_TAB, WHITESPACE_NEWLINE,
|
string ParseString() => ReadInputUntil(WHITESPACE_SPACE,
|
||||||
WHITESPACE_CARRIAGE_RETURN, ARRAY_ITEM_DELIMITER_TOKEN,
|
WHITESPACE_TAB,
|
||||||
DICTIONARY_ITEM_DELIMITER_TOKEN, DICTIONARY_ASSIGN_TOKEN,
|
WHITESPACE_NEWLINE,
|
||||||
|
WHITESPACE_CARRIAGE_RETURN,
|
||||||
|
ARRAY_ITEM_DELIMITER_TOKEN,
|
||||||
|
DICTIONARY_ITEM_DELIMITER_TOKEN,
|
||||||
|
DICTIONARY_ASSIGN_TOKEN,
|
||||||
ARRAY_END_TOKEN);
|
ARRAY_END_TOKEN);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -625,7 +617,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
//Read from opening quotation marks to closing quotation marks and skip escaped quotation marks
|
//Read from opening quotation marks to closing quotation marks and skip escaped quotation marks
|
||||||
while(data[index] != QUOTEDSTRING_END_TOKEN ||
|
while(data[index] != QUOTEDSTRING_END_TOKEN ||
|
||||||
(data[index - 1] == QUOTEDSTRING_ESCAPE_TOKEN && unescapedBackslash))
|
data[index - 1] == QUOTEDSTRING_ESCAPE_TOKEN && unescapedBackslash)
|
||||||
{
|
{
|
||||||
quotedString += data[index];
|
quotedString += data[index];
|
||||||
|
|
||||||
@@ -665,12 +657,13 @@ namespace Claunia.PropertyList
|
|||||||
/// <exception cref="EncoderFallbackException">If the string is encoded neither in ASCII nor in UTF-8</exception>
|
/// <exception cref="EncoderFallbackException">If the string is encoded neither in ASCII nor in UTF-8</exception>
|
||||||
public static string ParseQuotedString(string s)
|
public static string ParseQuotedString(string s)
|
||||||
{
|
{
|
||||||
List<byte> strBytes = new();
|
List<byte> strBytes = [];
|
||||||
|
|
||||||
IEnumerable<char> characters = s.ToCharArray();
|
IEnumerable<char> characters = s.ToCharArray();
|
||||||
IEnumerator<char> c = characters.GetEnumerator();
|
IEnumerator<char> c = characters.GetEnumerator();
|
||||||
|
|
||||||
while(c.MoveNext())
|
while(c.MoveNext())
|
||||||
|
{
|
||||||
switch(c.Current)
|
switch(c.Current)
|
||||||
{
|
{
|
||||||
case '\\':
|
case '\\':
|
||||||
@@ -678,22 +671,21 @@ namespace Claunia.PropertyList
|
|||||||
//An escaped sequence is following
|
//An escaped sequence is following
|
||||||
byte[] bts = Encoding.UTF8.GetBytes(ParseEscapedSequence(c));
|
byte[] bts = Encoding.UTF8.GetBytes(ParseEscapedSequence(c));
|
||||||
|
|
||||||
foreach(byte b in bts)
|
strBytes.AddRange(bts);
|
||||||
strBytes.Add(b);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
//a normal ASCII char
|
//a normal ASCII char
|
||||||
strBytes.AddRange(Encoding.BigEndianUnicode.GetBytes(new[]
|
strBytes.AddRange(Encoding.BigEndianUnicode.GetBytes([
|
||||||
{
|
|
||||||
c.Current
|
c.Current
|
||||||
}));
|
]));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byte[] bytArr = new byte[strBytes.Count];
|
byte[] bytArr = new byte[strBytes.Count];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -729,35 +721,20 @@ namespace Claunia.PropertyList
|
|||||||
switch(c)
|
switch(c)
|
||||||
{
|
{
|
||||||
case '\\':
|
case '\\':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString("\0\\"u8.ToArray());
|
||||||
{
|
|
||||||
0, (byte)'\\'
|
|
||||||
});
|
|
||||||
case '"':
|
case '"':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString("\0\""u8.ToArray());
|
||||||
{
|
|
||||||
0, (byte)'\"'
|
|
||||||
});
|
|
||||||
case 'b':
|
case 'b':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString(new byte[]
|
||||||
{
|
{
|
||||||
0, (byte)'\b'
|
0, (byte)'\b'
|
||||||
});
|
});
|
||||||
case 'n':
|
case 'n':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString("\0\n"u8.ToArray());
|
||||||
{
|
|
||||||
0, (byte)'\n'
|
|
||||||
});
|
|
||||||
case 'r':
|
case 'r':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString("\0\r"u8.ToArray());
|
||||||
{
|
|
||||||
0, (byte)'\r'
|
|
||||||
});
|
|
||||||
case 't':
|
case 't':
|
||||||
return Encoding.UTF8.GetString(new byte[]
|
return Encoding.UTF8.GetString("\0\t"u8.ToArray());
|
||||||
{
|
|
||||||
0, (byte)'\t'
|
|
||||||
});
|
|
||||||
case 'U':
|
case 'U':
|
||||||
case 'u':
|
case 'u':
|
||||||
{
|
{
|
||||||
@@ -774,9 +751,9 @@ namespace Claunia.PropertyList
|
|||||||
byte2 += iterator.Current;
|
byte2 += iterator.Current;
|
||||||
|
|
||||||
byte[] stringBytes =
|
byte[] stringBytes =
|
||||||
{
|
[
|
||||||
(byte)Convert.ToInt32(byte1, 16), (byte)Convert.ToInt32(byte2, 16)
|
(byte)Convert.ToInt32(byte1, 16), (byte)Convert.ToInt32(byte2, 16)
|
||||||
};
|
];
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(stringBytes);
|
return Encoding.UTF8.GetString(stringBytes);
|
||||||
}
|
}
|
||||||
@@ -792,9 +769,9 @@ namespace Claunia.PropertyList
|
|||||||
int asciiCode = Convert.ToInt32(num, 8);
|
int asciiCode = Convert.ToInt32(num, 8);
|
||||||
|
|
||||||
byte[] stringBytes =
|
byte[] stringBytes =
|
||||||
{
|
[
|
||||||
0, (byte)asciiCode
|
0, (byte)asciiCode
|
||||||
};
|
];
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(stringBytes);
|
return Encoding.UTF8.GetString(stringBytes);
|
||||||
}
|
}
|
||||||
@@ -804,10 +781,8 @@ namespace Claunia.PropertyList
|
|||||||
internal static bool IsASCIIEncodable(string text)
|
internal static bool IsASCIIEncodable(string text)
|
||||||
{
|
{
|
||||||
foreach(char c in text)
|
foreach(char c in text)
|
||||||
if(c > 0x7F)
|
if(c > 0x7F) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -30,22 +30,22 @@ using System.IO;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// Parses property lists that are in Apple's binary format. Use this class when you are sure about the format of
|
||||||
|
/// the property list. Otherwise use the PropertyListParser class.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Parsing is done by calling the static <see cref="Parse(byte[])" />, <see cref="Parse(FileInfo)" /> and
|
||||||
|
/// <see cref="Parse(Stream)" /> methods.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class BinaryPropertyListParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// Parses property lists that are in Apple's binary format. Use this class when you are sure about the format of
|
|
||||||
/// the property list. Otherwise use the PropertyListParser class.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Parsing is done by calling the static <see cref="Parse(byte[])" />, <see cref="Parse(FileInfo)" /> and
|
|
||||||
/// <see cref="Parse(Stream)" /> methods.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class BinaryPropertyListParser
|
|
||||||
{
|
|
||||||
static readonly Encoding utf16BigEndian = Encoding.GetEncoding("UTF-16BE");
|
static readonly Encoding utf16BigEndian = Encoding.GetEncoding("UTF-16BE");
|
||||||
|
|
||||||
/// <summary>Major version of the property list format</summary>
|
/// <summary>Major version of the property list format</summary>
|
||||||
@@ -119,9 +119,14 @@ namespace Claunia.PropertyList
|
|||||||
// 2.0 - Snow Lion
|
// 2.0 - Snow Lion
|
||||||
|
|
||||||
if(majorVersion > 0)
|
if(majorVersion > 0)
|
||||||
throw new PropertyListFormatException("Unsupported binary property list format: v" + majorVersion +
|
{
|
||||||
"." + minorVersion + ". " +
|
throw new PropertyListFormatException("Unsupported binary property list format: v" +
|
||||||
|
majorVersion +
|
||||||
|
"." +
|
||||||
|
minorVersion +
|
||||||
|
". " +
|
||||||
"Version 1.0 and later are not yet supported.");
|
"Version 1.0 and later are not yet supported.");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle trailer, last 32 bytes of the file
|
* Handle trailer, last 32 bytes of the file
|
||||||
@@ -142,7 +147,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
for(int i = 0; i < numObjects; i++)
|
for(int i = 0; i < numObjects; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> offsetBytes = bytes.Slice(offsetTableOffset + (i * offsetSize), offsetSize);
|
ReadOnlySpan<byte> offsetBytes = bytes.Slice(offsetTableOffset + i * offsetSize, offsetSize);
|
||||||
offsetTable[i] = (int)ParseUnsignedInt(offsetBytes);
|
offsetTable[i] = (int)ParseUnsignedInt(offsetBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,38 +248,51 @@ namespace Claunia.PropertyList
|
|||||||
//integer
|
//integer
|
||||||
int length = 1 << objInfo;
|
int length = 1 << objInfo;
|
||||||
|
|
||||||
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(), ValuePreprocessor.Type.INTEGER), NSNumber.INTEGER);
|
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(),
|
||||||
|
ValuePreprocessor.Type.INTEGER),
|
||||||
|
NSNumber.INTEGER);
|
||||||
}
|
}
|
||||||
case 0x2:
|
case 0x2:
|
||||||
{
|
{
|
||||||
//real
|
//real
|
||||||
int length = 1 << objInfo;
|
int length = 1 << objInfo;
|
||||||
|
|
||||||
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(), ValuePreprocessor.Type.FLOATING_POINT), NSNumber.REAL);
|
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(),
|
||||||
|
ValuePreprocessor.Type.FLOATING_POINT),
|
||||||
|
NSNumber.REAL);
|
||||||
}
|
}
|
||||||
case 0x3:
|
case 0x3:
|
||||||
{
|
{
|
||||||
//Date
|
//Date
|
||||||
if(objInfo != 0x3)
|
if(objInfo != 0x3)
|
||||||
|
{
|
||||||
throw new
|
throw new
|
||||||
PropertyListFormatException("The given binary property list contains a date object of an unknown type (" +
|
PropertyListFormatException("The given binary property list contains a date object of an unknown type (" +
|
||||||
objInfo + ")");
|
objInfo +
|
||||||
|
")");
|
||||||
|
}
|
||||||
|
|
||||||
return new NSDate(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, 8).ToArray(), ValuePreprocessor.Type.DATE));
|
return new NSDate(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, 8).ToArray(),
|
||||||
|
ValuePreprocessor.Type.DATE));
|
||||||
}
|
}
|
||||||
case 0x4:
|
case 0x4:
|
||||||
{
|
{
|
||||||
//Data
|
//Data
|
||||||
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int dataoffset);
|
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int dataoffset);
|
||||||
|
|
||||||
return new NSData(ValuePreprocessor.Preprocess(CopyOfRange(bytes, offset + dataoffset, offset + dataoffset + length), ValuePreprocessor.Type.DATA));
|
return new NSData(ValuePreprocessor.Preprocess(CopyOfRange(bytes,
|
||||||
|
offset + dataoffset,
|
||||||
|
offset + dataoffset + length),
|
||||||
|
ValuePreprocessor.Type.DATA));
|
||||||
}
|
}
|
||||||
case 0x5:
|
case 0x5:
|
||||||
{
|
{
|
||||||
//ASCII String, each character is 1 byte
|
//ASCII String, each character is 1 byte
|
||||||
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);
|
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);
|
||||||
|
|
||||||
return new NSString(ValuePreprocessor.Preprocess(bytes.Slice(offset + stroffset, length).ToArray(), ValuePreprocessor.Type.STRING), Encoding.ASCII);
|
return new NSString(ValuePreprocessor.Preprocess(bytes.Slice(offset + stroffset, length).ToArray(),
|
||||||
|
ValuePreprocessor.Type.STRING),
|
||||||
|
Encoding.ASCII);
|
||||||
}
|
}
|
||||||
case 0x6:
|
case 0x6:
|
||||||
{
|
{
|
||||||
@@ -315,8 +333,7 @@ namespace Claunia.PropertyList
|
|||||||
for(int i = 0; i < length; i++)
|
for(int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
int objRef =
|
int objRef =
|
||||||
(int)ParseUnsignedInt(bytes.Slice(offset + arrayOffset + (i * objectRefSize),
|
(int)ParseUnsignedInt(bytes.Slice(offset + arrayOffset + i * objectRefSize, objectRefSize));
|
||||||
objectRefSize));
|
|
||||||
|
|
||||||
array.Add(ParseObject(bytes, objRef));
|
array.Add(ParseObject(bytes, objRef));
|
||||||
}
|
}
|
||||||
@@ -333,8 +350,7 @@ namespace Claunia.PropertyList
|
|||||||
for(int i = 0; i < length; i++)
|
for(int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
int objRef =
|
int objRef =
|
||||||
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + (i * objectRefSize),
|
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize, objectRefSize));
|
||||||
objectRefSize));
|
|
||||||
|
|
||||||
set.AddObject(ParseObject(bytes, objRef));
|
set.AddObject(ParseObject(bytes, objRef));
|
||||||
}
|
}
|
||||||
@@ -351,8 +367,7 @@ namespace Claunia.PropertyList
|
|||||||
for(int i = 0; i < length; i++)
|
for(int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
int objRef =
|
int objRef =
|
||||||
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + (i * objectRefSize),
|
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize, objectRefSize));
|
||||||
objectRefSize));
|
|
||||||
|
|
||||||
set.AddObject(ParseObject(bytes, objRef));
|
set.AddObject(ParseObject(bytes, objRef));
|
||||||
}
|
}
|
||||||
@@ -370,12 +385,13 @@ namespace Claunia.PropertyList
|
|||||||
for(int i = 0; i < length; i++)
|
for(int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
int keyRef =
|
int keyRef =
|
||||||
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + (i * objectRefSize),
|
(int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize, objectRefSize));
|
||||||
objectRefSize));
|
|
||||||
|
|
||||||
int valRef =
|
int valRef =
|
||||||
(int)
|
(int)ParseUnsignedInt(bytes.Slice(offset +
|
||||||
ParseUnsignedInt(bytes.Slice(offset + contentOffset + (length * objectRefSize) + (i * objectRefSize),
|
contentOffset +
|
||||||
|
length * objectRefSize +
|
||||||
|
i * objectRefSize,
|
||||||
objectRefSize));
|
objectRefSize));
|
||||||
|
|
||||||
NSObject key = ParseObject(bytes, keyRef);
|
NSObject key = ParseObject(bytes, keyRef);
|
||||||
@@ -388,7 +404,8 @@ namespace Claunia.PropertyList
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Debug.WriteLine("WARNING: The given binary property list contains an object of unknown type (" +
|
Debug.WriteLine("WARNING: The given binary property list contains an object of unknown type (" +
|
||||||
objType + ")");
|
objType +
|
||||||
|
")");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -413,8 +430,11 @@ namespace Claunia.PropertyList
|
|||||||
int intType = (int_type & 0xF0) >> 4;
|
int intType = (int_type & 0xF0) >> 4;
|
||||||
|
|
||||||
if(intType != 0x1)
|
if(intType != 0x1)
|
||||||
Debug.WriteLine("BinaryPropertyListParser: Length integer has an unexpected type" + intType +
|
{
|
||||||
|
Debug.WriteLine("BinaryPropertyListParser: Length integer has an unexpected type" +
|
||||||
|
intType +
|
||||||
". Attempting to parse anyway...");
|
". Attempting to parse anyway...");
|
||||||
|
}
|
||||||
|
|
||||||
int intInfo = int_type & 0x0F;
|
int intInfo = int_type & 0x0F;
|
||||||
int intLength = 1 << intInfo;
|
int intLength = 1 << intInfo;
|
||||||
@@ -448,26 +468,21 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
int tempOffset = offset + length;
|
int tempOffset = offset + length;
|
||||||
|
|
||||||
if(bytes.Length <= tempOffset)
|
if(bytes.Length <= tempOffset) return numCharacters;
|
||||||
return numCharacters;
|
|
||||||
|
|
||||||
if(bytes[tempOffset] < 0x80)
|
if(bytes[tempOffset] < 0x80) length++;
|
||||||
length++;
|
|
||||||
|
|
||||||
if(bytes[tempOffset] < 0xC2)
|
if(bytes[tempOffset] < 0xC2) return numCharacters;
|
||||||
return numCharacters;
|
|
||||||
|
|
||||||
if(bytes[tempOffset] < 0xE0)
|
if(bytes[tempOffset] < 0xE0)
|
||||||
{
|
{
|
||||||
if((bytes[tempOffset + 1] & 0xC0) != 0x80)
|
if((bytes[tempOffset + 1] & 0xC0) != 0x80) return numCharacters;
|
||||||
return numCharacters;
|
|
||||||
|
|
||||||
length += 2;
|
length += 2;
|
||||||
}
|
}
|
||||||
else if(bytes[tempOffset] < 0xF0)
|
else if(bytes[tempOffset] < 0xF0)
|
||||||
{
|
{
|
||||||
if((bytes[tempOffset + 1] & 0xC0) != 0x80 ||
|
if((bytes[tempOffset + 1] & 0xC0) != 0x80 || (bytes[tempOffset + 2] & 0xC0) != 0x80)
|
||||||
(bytes[tempOffset + 2] & 0xC0) != 0x80)
|
|
||||||
return numCharacters;
|
return numCharacters;
|
||||||
|
|
||||||
length += 3;
|
length += 3;
|
||||||
@@ -491,8 +506,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="bytes">The unsigned integer represented by the given bytes.</param>
|
/// <param name="bytes">The unsigned integer represented by the given bytes.</param>
|
||||||
public static long ParseUnsignedInt(ReadOnlySpan<byte> bytes)
|
public static long ParseUnsignedInt(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
if(bytes.Length <= 4)
|
if(bytes.Length <= 4) return ParseLong(bytes);
|
||||||
return ParseLong(bytes);
|
|
||||||
|
|
||||||
return ParseLong(bytes) & 0xFFFFFFFFL;
|
return ParseLong(bytes) & 0xFFFFFFFFL;
|
||||||
}
|
}
|
||||||
@@ -507,15 +521,9 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="bytes">The bytes representing the long integer.</param>
|
/// <param name="bytes">The bytes representing the long integer.</param>
|
||||||
public static long ParseLong(ReadOnlySpan<byte> bytes)
|
public static long ParseLong(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
if(bytes == null)
|
if(bytes == null) throw new ArgumentNullException(nameof(bytes));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bytes.Length == 0)
|
if(bytes.Length == 0) throw new ArgumentOutOfRangeException(nameof(bytes));
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://opensource.apple.com/source/CF/CF-1153.18/CFBinaryPList.c,
|
// https://opensource.apple.com/source/CF/CF-1153.18/CFBinaryPList.c,
|
||||||
// __CFBinaryPlistCreateObjectFiltered, case kCFBinaryPlistMarkerInt:
|
// __CFBinaryPlistCreateObjectFiltered, case kCFBinaryPlistMarkerInt:
|
||||||
@@ -527,31 +535,31 @@ namespace Claunia.PropertyList
|
|||||||
// but only the last 64 bits are significant currently
|
// but only the last 64 bits are significant currently
|
||||||
switch(bytes.Length)
|
switch(bytes.Length)
|
||||||
{
|
{
|
||||||
case 1: return bytes[0];
|
case 1:
|
||||||
|
return bytes[0];
|
||||||
|
|
||||||
case 2: return BinaryPrimitives.ReadUInt16BigEndian(bytes);
|
case 2:
|
||||||
|
return BinaryPrimitives.ReadUInt16BigEndian(bytes);
|
||||||
|
|
||||||
case 4: return BinaryPrimitives.ReadUInt32BigEndian(bytes);
|
case 4:
|
||||||
|
return BinaryPrimitives.ReadUInt32BigEndian(bytes);
|
||||||
|
|
||||||
// Transition from unsigned to signed
|
// Transition from unsigned to signed
|
||||||
case 8: return BinaryPrimitives.ReadInt64BigEndian(bytes);
|
case 8:
|
||||||
|
return BinaryPrimitives.ReadInt64BigEndian(bytes);
|
||||||
|
|
||||||
// Only the last 64 bits are significant currently
|
// Only the last 64 bits are significant currently
|
||||||
case 16: return BinaryPrimitives.ReadInt64BigEndian(bytes.Slice(8));
|
case 16:
|
||||||
|
return BinaryPrimitives.ReadInt64BigEndian(bytes.Slice(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bytes.Length >= 8)
|
if(bytes.Length >= 8) throw new ArgumentOutOfRangeException(nameof(bytes), $"Cannot read a byte span of length {bytes.Length}");
|
||||||
throw new ArgumentOutOfRangeException(nameof(bytes),
|
|
||||||
$"Cannot read a byte span of length {bytes.Length}");
|
|
||||||
|
|
||||||
// Compatibility with existing archives, including anything with a non-power-of-2
|
// Compatibility with existing archives, including anything with a non-power-of-2
|
||||||
// size and 16-byte values, and architectures that don't support unaligned access
|
// size and 16-byte values, and architectures that don't support unaligned access
|
||||||
long value = 0;
|
long value = 0;
|
||||||
|
|
||||||
for(int i = 0; i < bytes.Length; i++)
|
for(int i = 0; i < bytes.Length; i++) value = (value << 8) + bytes[i];
|
||||||
{
|
|
||||||
value = (value << 8) + bytes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
@@ -565,10 +573,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="bytes">The bytes representing the double.</param>
|
/// <param name="bytes">The bytes representing the double.</param>
|
||||||
public static double ParseDouble(ReadOnlySpan<byte> bytes)
|
public static double ParseDouble(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
if(bytes == null)
|
if(bytes == null) throw new ArgumentNullException(nameof(bytes));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.Length switch
|
return bytes.Length switch
|
||||||
{
|
{
|
||||||
@@ -587,11 +592,8 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
int length = endIndex - startIndex;
|
int length = endIndex - startIndex;
|
||||||
|
|
||||||
if(length < 0)
|
if(length < 0) throw new ArgumentOutOfRangeException("startIndex (" + startIndex + ")" + " > endIndex (" + endIndex + ")");
|
||||||
throw new ArgumentOutOfRangeException("startIndex (" + startIndex + ")" + " > endIndex (" + endIndex +
|
|
||||||
")");
|
|
||||||
|
|
||||||
return src.Slice(startIndex, endIndex - startIndex).ToArray();
|
return src.Slice(startIndex, endIndex - startIndex).ToArray();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
public partial class BinaryPropertyListWriter
|
||||||
{
|
{
|
||||||
public partial class BinaryPropertyListWriter
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The equality comparer which is used when adding an object to the <see cref="BinaryPropertyListWriter.idMap" />
|
/// The equality comparer which is used when adding an object to the <see cref="BinaryPropertyListWriter.idMap" />
|
||||||
/// . In most cases, objects are always added. The only exception are very specific strings, which are only added once.
|
/// . In most cases, objects are always added. The only exception are very specific strings, which are only added once.
|
||||||
@@ -13,25 +13,18 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
public override bool Equals(NSObject x, NSObject y)
|
public override bool Equals(NSObject x, NSObject y)
|
||||||
{
|
{
|
||||||
if(x is not NSString a ||
|
if(x is not NSString a || y is not NSString b) return ReferenceEquals(x, y);
|
||||||
y is not NSString b)
|
|
||||||
return ReferenceEquals(x, y);
|
|
||||||
|
|
||||||
if(!IsSerializationPrimitive(a) ||
|
if(!IsSerializationPrimitive(a) || !IsSerializationPrimitive(b)) return ReferenceEquals(x, y);
|
||||||
!IsSerializationPrimitive(b))
|
|
||||||
return ReferenceEquals(x, y);
|
|
||||||
|
|
||||||
return string.Equals(a.Content, b.Content, StringComparison.Ordinal);
|
return string.Equals(a.Content, b.Content, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode(NSObject obj)
|
public override int GetHashCode(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is NSString s &&
|
if(obj is NSString s && IsSerializationPrimitive(s)) return s.Content.GetHashCode();
|
||||||
IsSerializationPrimitive(s))
|
|
||||||
return s.Content.GetHashCode();
|
|
||||||
|
|
||||||
return obj.GetHashCode();
|
return obj.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
public partial class BinaryPropertyListWriter
|
||||||
{
|
{
|
||||||
public partial class BinaryPropertyListWriter
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The equality comparer which is used when retrieving objects in the
|
/// The equality comparer which is used when retrieving objects in the
|
||||||
/// <see cref="BinaryPropertyListWriter.idMap" />. The logic is slightly different from
|
/// <see cref="BinaryPropertyListWriter.idMap" />. The logic is slightly different from
|
||||||
@@ -22,18 +22,22 @@ namespace Claunia.PropertyList
|
|||||||
// The exceptions are UIDs, where we always compare by value, and "primitive" strings (a list of well-known
|
// The exceptions are UIDs, where we always compare by value, and "primitive" strings (a list of well-known
|
||||||
// strings), which are treaded specially and "recycled".
|
// strings), which are treaded specially and "recycled".
|
||||||
UID => x.Equals(y),
|
UID => x.Equals(y),
|
||||||
NSNumber number when IsSerializationPrimitive(number) => number.Equals(y),
|
NSNumber number when IsSerializationPrimitive(number)
|
||||||
NSString nsString when IsSerializationPrimitive(nsString) => nsString.Equals(y),
|
=> number.Equals(y),
|
||||||
|
NSString nsString when
|
||||||
|
IsSerializationPrimitive(nsString) => nsString
|
||||||
|
.Equals(y),
|
||||||
_ => ReferenceEquals(x, y)
|
_ => ReferenceEquals(x, y)
|
||||||
};
|
};
|
||||||
|
|
||||||
public override int GetHashCode(NSObject obj) => obj switch
|
public override int GetHashCode(NSObject obj) => obj switch
|
||||||
{
|
{
|
||||||
UID u => u.GetHashCode(),
|
UID u => u.GetHashCode(),
|
||||||
NSNumber n when IsSerializationPrimitive(n) => n.ToObject().GetHashCode(),
|
NSNumber n when IsSerializationPrimitive(n) => n.ToObject()
|
||||||
NSString s when IsSerializationPrimitive(s) => s.Content.GetHashCode(),
|
.GetHashCode(),
|
||||||
|
NSString s when IsSerializationPrimitive(s) => s.Content
|
||||||
|
.GetHashCode(),
|
||||||
_ => obj.GetHashCode()
|
_ => obj.GetHashCode()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,19 +27,19 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>A BinaryPropertyListWriter is a helper class for writing out binary property list files.</para>
|
||||||
|
/// <para>
|
||||||
|
/// 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.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Keith Randall
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public partial class BinaryPropertyListWriter
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>A BinaryPropertyListWriter is a helper class for writing out binary property list files.</para>
|
|
||||||
/// <para>
|
|
||||||
/// 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.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Keith Randall
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public partial class BinaryPropertyListWriter
|
|
||||||
{
|
|
||||||
/// <summary>Binary property list version 0.0</summary>
|
/// <summary>Binary property list version 0.0</summary>
|
||||||
public const int VERSION_00 = 0;
|
public const int VERSION_00 = 0;
|
||||||
/// <summary>Binary property list version 1.0</summary>
|
/// <summary>Binary property list version 1.0</summary>
|
||||||
@@ -74,8 +74,7 @@ namespace Claunia.PropertyList
|
|||||||
outStream = outStr;
|
outStream = outStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BinaryPropertyListWriter(Stream outStr, int version,
|
public BinaryPropertyListWriter(Stream outStr, int version, IEqualityComparer<NSObject> addObjectEqualityComparer,
|
||||||
IEqualityComparer<NSObject> addObjectEqualityComparer,
|
|
||||||
IEqualityComparer<NSObject> getObjectEqualityComparer)
|
IEqualityComparer<NSObject> getObjectEqualityComparer)
|
||||||
{
|
{
|
||||||
this.version = version;
|
this.version = version;
|
||||||
@@ -115,8 +114,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
int v = GetMinimumRequiredVersion(o);
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
if(v > minVersion)
|
if(v > minVersion) minVersion = v;
|
||||||
minVersion = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -127,8 +125,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
int v = GetMinimumRequiredVersion(o);
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
if(v > minVersion)
|
if(v > minVersion) minVersion = v;
|
||||||
minVersion = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -142,8 +139,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
int v = GetMinimumRequiredVersion(o);
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
if(v > minVersion)
|
if(v > minVersion) minVersion = v;
|
||||||
minVersion = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -183,7 +179,8 @@ namespace Claunia.PropertyList
|
|||||||
: "v0.0";
|
: "v0.0";
|
||||||
|
|
||||||
throw new IOException("The given property list structure cannot be saved. " +
|
throw new IOException("The given property list structure cannot be saved. " +
|
||||||
"The required version of the binary format (" + versionString +
|
"The required version of the binary format (" +
|
||||||
|
versionString +
|
||||||
") is not yet supported.");
|
") is not yet supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,47 +203,32 @@ namespace Claunia.PropertyList
|
|||||||
public void Write(NSObject root)
|
public void Write(NSObject root)
|
||||||
{
|
{
|
||||||
// magic bytes
|
// magic bytes
|
||||||
Write(new[]
|
Write("bplist"u8.ToArray());
|
||||||
{
|
|
||||||
(byte)'b', (byte)'p', (byte)'l', (byte)'i', (byte)'s', (byte)'t'
|
|
||||||
});
|
|
||||||
|
|
||||||
//version
|
//version
|
||||||
switch(version)
|
switch(version)
|
||||||
{
|
{
|
||||||
case VERSION_00:
|
case VERSION_00:
|
||||||
{
|
{
|
||||||
Write(new[]
|
Write("00"u8.ToArray());
|
||||||
{
|
|
||||||
(byte)'0', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VERSION_10:
|
case VERSION_10:
|
||||||
{
|
{
|
||||||
Write(new[]
|
Write("10"u8.ToArray());
|
||||||
{
|
|
||||||
(byte)'1', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VERSION_15:
|
case VERSION_15:
|
||||||
{
|
{
|
||||||
Write(new[]
|
Write("15"u8.ToArray());
|
||||||
{
|
|
||||||
(byte)'1', (byte)'5'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VERSION_20:
|
case VERSION_20:
|
||||||
{
|
{
|
||||||
Write(new[]
|
Write("20"u8.ToArray());
|
||||||
{
|
|
||||||
(byte)'2', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -277,8 +259,7 @@ namespace Claunia.PropertyList
|
|||||||
long offsetTableOffset = count;
|
long offsetTableOffset = count;
|
||||||
int offsetSizeInBytes = ComputeOffsetSizeInBytes(count);
|
int offsetSizeInBytes = ComputeOffsetSizeInBytes(count);
|
||||||
|
|
||||||
foreach(long offset in offsets)
|
foreach(long offset in offsets) WriteBytes(offset, offsetSizeInBytes);
|
||||||
WriteBytes(offset, offsetSizeInBytes);
|
|
||||||
|
|
||||||
if(version != VERSION_15)
|
if(version != VERSION_15)
|
||||||
{
|
{
|
||||||
@@ -310,16 +291,13 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
if(ReuseObjectIds)
|
if(ReuseObjectIds)
|
||||||
{
|
{
|
||||||
if(!idDict.ContainsKey(obj))
|
if(!idDict.ContainsKey(obj)) idDict.Add(obj, currentId++);
|
||||||
idDict.Add(obj, currentId++);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(!idDict2.ContainsKey(obj))
|
if(!idDict2.ContainsKey(obj)) idDict2.Add(obj, currentId);
|
||||||
idDict2.Add(obj, currentId);
|
|
||||||
|
|
||||||
if(!idDict.ContainsKey(obj))
|
if(!idDict.ContainsKey(obj)) idDict.Add(obj, currentId++);
|
||||||
idDict.Add(obj, currentId++);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +305,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
static int ComputeIdSizeInBytes(int numberOfIds)
|
static int ComputeIdSizeInBytes(int numberOfIds)
|
||||||
{
|
{
|
||||||
if(numberOfIds < 256)
|
if(numberOfIds < 256) return 1;
|
||||||
return 1;
|
|
||||||
|
|
||||||
return numberOfIds < 65536 ? 2 : 4;
|
return numberOfIds < 65536 ? 2 : 4;
|
||||||
}
|
}
|
||||||
@@ -345,7 +322,8 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
switch(value)
|
switch(value)
|
||||||
{
|
{
|
||||||
case < 0: throw new ArgumentException("value must be greater than or equal to 0", "value");
|
case < 0:
|
||||||
|
throw new ArgumentException("value must be greater than or equal to 0", "value");
|
||||||
case < 15:
|
case < 15:
|
||||||
Write((kind << 4) + value);
|
Write((kind << 4) + value);
|
||||||
|
|
||||||
@@ -385,19 +363,18 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
internal void Write(Span<byte> bytes)
|
internal void Write(Span<byte> bytes)
|
||||||
{
|
{
|
||||||
#if NATIVE_SPAN
|
#if NATIVE_SPAN
|
||||||
outStream.Write(bytes);
|
outStream.Write(bytes);
|
||||||
count += bytes.Length;
|
count += bytes.Length;
|
||||||
#else
|
#else
|
||||||
Write(bytes.ToArray());
|
Write(bytes.ToArray());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteBytes(long value, int bytes)
|
internal void WriteBytes(long value, int bytes)
|
||||||
{
|
{
|
||||||
// write low-order bytes big-endian style
|
// write low-order bytes big-endian style
|
||||||
for(int i = bytes - 1; i >= 0; i--)
|
for(int i = bytes - 1; i >= 0; i--) Write((int)(value >> 8 * i));
|
||||||
Write((int)(value >> (8 * i)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteID(int id) => WriteBytes(id, idSizeInBytes);
|
internal void WriteID(int id) => WriteBytes(id, idSizeInBytes);
|
||||||
@@ -412,11 +389,24 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
// This is a list of "special" values which are only added once to a binary property
|
// This is a list of "special" values which are only added once to a binary property
|
||||||
// list, and can be referenced multiple times.
|
// list, and can be referenced multiple times.
|
||||||
return content is "$class" or "$classes" or "$classname" or "NS.objects" or "NS.keys" or "NS.base" or
|
return content is "$class"
|
||||||
"NS.relative" or "NS.string" or "NSURL" or "NSDictionary" or "NSObject" or "NSMutableDictionary"
|
or "$classes"
|
||||||
or "NSMutableArray" or "NSArray" or "NSUUID" or "NSKeyedArchiver" or "NSMutableString";
|
or "$classname"
|
||||||
|
or "NS.objects"
|
||||||
|
or "NS.keys"
|
||||||
|
or "NS.base"
|
||||||
|
or "NS.relative"
|
||||||
|
or "NS.string"
|
||||||
|
or "NSURL"
|
||||||
|
or "NSDictionary"
|
||||||
|
or "NSObject"
|
||||||
|
or "NSMutableDictionary"
|
||||||
|
or "NSMutableArray"
|
||||||
|
or "NSArray"
|
||||||
|
or "NSUUID"
|
||||||
|
or "NSKeyedArchiver"
|
||||||
|
or "NSMutableString";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsSerializationPrimitive(NSNumber n) => n.isBoolean();
|
internal static bool IsSerializationPrimitive(NSNumber n) => n.isBoolean();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -22,10 +22,10 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
partial class NSArray : IList<NSObject>
|
||||||
{
|
{
|
||||||
partial class NSArray : IList<NSObject>
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public NSObject this[int index]
|
public NSObject this[int index]
|
||||||
{
|
{
|
||||||
@@ -76,5 +76,4 @@ namespace Claunia.PropertyList
|
|||||||
public void Insert(int index, object item) => Insert(index, Wrap(item));
|
public void Insert(int index, object item) => Insert(index, Wrap(item));
|
||||||
|
|
||||||
public bool Remove(object item) => Remove(Wrap(item));
|
public bool Remove(object item) => Remove(Wrap(item));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>Represents an Array.</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public partial class NSArray : NSObject
|
||||||
{
|
{
|
||||||
/// <summary>Represents an Array.</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public partial class NSArray : NSObject
|
|
||||||
{
|
|
||||||
readonly List<NSObject> array;
|
readonly List<NSObject> array;
|
||||||
|
|
||||||
/// <summary>Creates an empty array of the given length.</summary>
|
/// <summary>Creates an empty array of the given length.</summary>
|
||||||
@@ -42,7 +42,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
/// <summary>Creates a array from an existing one</summary>
|
/// <summary>Creates a array from an existing one</summary>
|
||||||
/// <param name="a">The array which should be wrapped by the NSArray.</param>
|
/// <param name="a">The array which should be wrapped by the NSArray.</param>
|
||||||
public NSArray(params NSObject[] a) => array = new List<NSObject>(a);
|
public NSArray(params NSObject[] a) => array = [..a];
|
||||||
|
|
||||||
/// <summary>Returns the size of the array.</summary>
|
/// <summary>Returns the size of the array.</summary>
|
||||||
/// <value>The number of elements that this array can store.</value>
|
/// <value>The number of elements that this array can store.</value>
|
||||||
@@ -65,8 +65,7 @@ namespace Claunia.PropertyList
|
|||||||
[Obsolete]
|
[Obsolete]
|
||||||
public void SetValue(int key, object value)
|
public void SetValue(int key, object value)
|
||||||
{
|
{
|
||||||
if(value == null)
|
if(value == null) throw new ArgumentNullException("value", "Cannot add null values to an NSArray!");
|
||||||
throw new ArgumentNullException("value", "Cannot add null values to an NSArray!");
|
|
||||||
|
|
||||||
array[key] = Wrap(value);
|
array[key] = Wrap(value);
|
||||||
}
|
}
|
||||||
@@ -88,8 +87,7 @@ namespace Claunia.PropertyList
|
|||||||
NSObject nso = Wrap(obj);
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
foreach(NSObject elem in array)
|
foreach(NSObject elem in array)
|
||||||
if(elem.Equals(nso))
|
if(elem.Equals(nso)) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -106,8 +104,7 @@ namespace Claunia.PropertyList
|
|||||||
NSObject nso = Wrap(obj);
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
for(int i = 0; i < array.Count; i++)
|
||||||
if(array[i].Equals(nso))
|
if(array[i].Equals(nso)) return i;
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -125,8 +122,7 @@ namespace Claunia.PropertyList
|
|||||||
NSObject nso = Wrap(obj);
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
for(int i = 0; i < array.Count; i++)
|
||||||
if(array[i] == nso)
|
if(array[i] == nso) return i;
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -143,11 +139,10 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="indexes">The indices of the objects.</param>
|
/// <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];
|
var result = new NSObject[indexes.Length];
|
||||||
Array.Sort(indexes);
|
Array.Sort(indexes);
|
||||||
|
|
||||||
for(int i = 0; i < indexes.Length; i++)
|
for(int i = 0; i < indexes.Length; i++) result[i] = array[indexes[i]];
|
||||||
result[i] = array[indexes[i]];
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -166,13 +161,11 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if(obj is NSArray nsArray)
|
if(obj is NSArray nsArray) return ArrayEquals(nsArray, this);
|
||||||
return ArrayEquals(nsArray, this);
|
|
||||||
|
|
||||||
NSObject nso = Wrap(obj);
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
if(nso is NSArray nsoArray)
|
if(nso is NSArray nsoArray) return ArrayEquals(nsoArray, this);
|
||||||
return ArrayEquals(nsoArray, this);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -185,7 +178,7 @@ namespace Claunia.PropertyList
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
hash = (89 * hash) + array.GetHashCode();
|
hash = 89 * hash + array.GetHashCode();
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -210,16 +203,14 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
base.AssignIDs(outPlist);
|
base.AssignIDs(outPlist);
|
||||||
|
|
||||||
foreach(NSObject obj in array)
|
foreach(NSObject obj in array) obj.AssignIDs(outPlist);
|
||||||
obj.AssignIDs(outPlist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
{
|
{
|
||||||
outPlist.WriteIntHeader(0xA, array.Count);
|
outPlist.WriteIntHeader(0xA, array.Count);
|
||||||
|
|
||||||
foreach(NSObject obj in array)
|
foreach(NSObject obj in array) outPlist.WriteID(outPlist.GetID(obj));
|
||||||
outPlist.WriteID(outPlist.GetID(obj));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -275,17 +266,14 @@ namespace Claunia.PropertyList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(i != 0)
|
if(i != 0) ascii.Append(" ");
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCII(ascii, 0);
|
array[i].ToASCII(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i != array.Count - 1)
|
if(i != array.Count - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
@@ -313,17 +301,14 @@ namespace Claunia.PropertyList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(i != 0)
|
if(i != 0) ascii.Append(" ");
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCIIGnuStep(ascii, 0);
|
array[i].ToASCIIGnuStep(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i != array.Count - 1)
|
if(i != array.Count - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
@@ -346,17 +331,13 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSArray nsArray)
|
if(obj is not NSArray nsArray) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(array.Count != nsArray.array.Count)
|
if(array.Count != nsArray.array.Count) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
for(int i = 0; i < array.Count; i++)
|
||||||
if(!array[i].Equals(nsArray[i]))
|
if(!array[i].Equals(nsArray[i])) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>NSData objects are wrappers for byte buffers</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSData : NSObject
|
||||||
{
|
{
|
||||||
/// <summary>NSData objects are wrappers for byte buffers</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSData : NSObject
|
|
||||||
{
|
|
||||||
// In the XML property list format, the base-64 encoded data is split across multiple lines.
|
// In the XML property list format, the base-64 encoded data is split across multiple lines.
|
||||||
// Each line contains 68 characters.
|
// Each line contains 68 characters.
|
||||||
const int DataLineLength = 68;
|
const int DataLineLength = 68;
|
||||||
@@ -111,7 +111,7 @@ namespace Claunia.PropertyList
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
int hash = 5;
|
int hash = 5;
|
||||||
hash = (67 * hash) + Bytes.GetHashCode();
|
hash = 67 * hash + Bytes.GetHashCode();
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -124,12 +124,14 @@ namespace Claunia.PropertyList
|
|||||||
string base64 = GetBase64EncodedData();
|
string base64 = GetBase64EncodedData();
|
||||||
|
|
||||||
foreach(string line in base64.Split('\n'))
|
foreach(string line in base64.Split('\n'))
|
||||||
|
{
|
||||||
for(int offset = 0; offset < base64.Length; offset += DataLineLength)
|
for(int offset = 0; offset < base64.Length; offset += DataLineLength)
|
||||||
{
|
{
|
||||||
Indent(xml, level);
|
Indent(xml, level);
|
||||||
xml.Append(line.Substring(offset, Math.Min(DataLineLength, line.Length - offset)));
|
xml.Append(line.Substring(offset, Math.Min(DataLineLength, line.Length - offset)));
|
||||||
xml.Append(NEWLINE);
|
xml.Append(NEWLINE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Indent(xml, level);
|
Indent(xml, level);
|
||||||
xml.Append("</data>");
|
xml.Append("</data>");
|
||||||
@@ -157,9 +159,7 @@ namespace Claunia.PropertyList
|
|||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
}
|
}
|
||||||
else if((i + 1) % 2 == 0 &&
|
else if((i + 1) % 2 == 0 && i != Bytes.Length - 1) ascii.Append(" ");
|
||||||
i != Bytes.Length - 1)
|
|
||||||
ascii.Append(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.DATA_END_TOKEN);
|
ascii.Append(ASCIIPropertyListParser.DATA_END_TOKEN);
|
||||||
@@ -184,5 +184,4 @@ namespace Claunia.PropertyList
|
|||||||
public static explicit operator byte[](NSData value) => value.Bytes;
|
public static explicit operator byte[](NSData value) => value.Bytes;
|
||||||
|
|
||||||
public static explicit operator NSData(byte[] value) => new(value);
|
public static explicit operator NSData(byte[] value) => new(value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ using System;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>Represents a date</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSDate : NSObject
|
||||||
{
|
{
|
||||||
/// <summary>Represents a date</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSDate : NSObject
|
|
||||||
{
|
|
||||||
static readonly DateTime EPOCH = new(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
static readonly DateTime EPOCH = new(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
|
||||||
// The datetime ends with 'Z', which indicates UTC time. To make sure .NET
|
// The datetime ends with 'Z', which indicates UTC time. To make sure .NET
|
||||||
@@ -41,9 +41,9 @@ namespace Claunia.PropertyList
|
|||||||
static readonly string sdfDefault = "yyyy-MM-dd'T'HH:mm:ssK";
|
static readonly string sdfDefault = "yyyy-MM-dd'T'HH:mm:ssK";
|
||||||
static readonly string sdfGnuStep = "yyyy-MM-dd HH:mm:ss zzz";
|
static readonly string sdfGnuStep = "yyyy-MM-dd HH:mm:ss zzz";
|
||||||
static readonly string[] sdfAll =
|
static readonly string[] sdfAll =
|
||||||
{
|
[
|
||||||
sdfDefault, sdfGnuStep
|
sdfDefault, sdfGnuStep
|
||||||
};
|
];
|
||||||
|
|
||||||
static readonly CultureInfo provider = CultureInfo.InvariantCulture;
|
static readonly CultureInfo provider = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
@@ -162,8 +162,7 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSDate date)
|
if(obj is not NSDate date) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
int equality = DateTime.Compare(Date, date.Date);
|
int equality = DateTime.Compare(Date, date.Date);
|
||||||
|
|
||||||
@@ -173,5 +172,4 @@ namespace Claunia.PropertyList
|
|||||||
public static explicit operator DateTime(NSDate value) => value.Date;
|
public static explicit operator DateTime(NSDate value) => value.Date;
|
||||||
|
|
||||||
public static explicit operator NSDate(DateTime value) => new(value);
|
public static explicit operator NSDate(DateTime value) => new(value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -28,20 +28,20 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// A NSDictionary is a collection of keys and values, essentially a Dictionary. The keys are simple Strings
|
||||||
|
/// whereas the values can be any kind of NSObject.
|
||||||
|
/// </para>
|
||||||
|
/// <para>You can access the keys through the function <see cref="Keys" />.</para>
|
||||||
|
/// <para>Access to the objects stored for each key is given through the function <see cref="ObjectForKey" />.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSDictionary : NSObject, IDictionary<string, NSObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>
|
|
||||||
/// A NSDictionary is a collection of keys and values, essentially a Dictionary. The keys are simple Strings
|
|
||||||
/// whereas the values can be any kind of NSObject.
|
|
||||||
/// </para>
|
|
||||||
/// <para>You can access the keys through the function <see cref="Keys" />.</para>
|
|
||||||
/// <para>Access to the objects stored for each key is given through the function <see cref="ObjectForKey" />.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSDictionary : NSObject, IDictionary<string, NSObject>
|
|
||||||
{
|
|
||||||
readonly Dictionary<string, NSObject> dict;
|
readonly Dictionary<string, NSObject> dict;
|
||||||
|
|
||||||
// Maps the keys in this dictionary to their NSString equivalent. Makes sure the NSString
|
// Maps the keys in this dictionary to their NSString equivalent. Makes sure the NSString
|
||||||
@@ -52,8 +52,8 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="capacity">The capacity of the dictionary.</param>
|
/// <param name="capacity">The capacity of the dictionary.</param>
|
||||||
public NSDictionary(int capacity)
|
public NSDictionary(int capacity)
|
||||||
{
|
{
|
||||||
dict = new Dictionary<string, NSObject>(capacity);
|
dict = [];
|
||||||
keys = new Dictionary<string, NSString>(capacity);
|
keys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a new empty NSDictionary.</summary>
|
/// <summary>Creates a new empty NSDictionary.</summary>
|
||||||
@@ -63,15 +63,19 @@ namespace Claunia.PropertyList
|
|||||||
/// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value>
|
||||||
public bool IsEmpty => dict.Count == 0;
|
public bool IsEmpty => dict.Count == 0;
|
||||||
|
|
||||||
#region IEnumerable implementation
|
#region IEnumerable implementation
|
||||||
|
|
||||||
/// <summary>Gets the enumerator.</summary>
|
/// <summary>Gets the enumerator.</summary>
|
||||||
/// <returns>The enumerator.</returns>
|
/// <returns>The enumerator.</returns>
|
||||||
public IEnumerator<KeyValuePair<string, NSObject>> GetEnumerator() => dict.GetEnumerator();
|
public IEnumerator<KeyValuePair<string, NSObject>> GetEnumerator() => dict.GetEnumerator();
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IEnumerable implementation
|
#endregion
|
||||||
|
|
||||||
|
#region IEnumerable implementation
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator();
|
||||||
#endregion
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the hashmap which stores the keys and values of this dictionary. Changes to the hashmap's contents are
|
/// Gets the hashmap which stores the keys and values of this dictionary. Changes to the hashmap's contents are
|
||||||
@@ -105,8 +109,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <returns>The object corresponding to the specified key, null if not found in the current instance.</returns>
|
/// <returns>The object corresponding to the specified key, null if not found in the current instance.</returns>
|
||||||
public NSObject Get(object key)
|
public NSObject Get(object key)
|
||||||
{
|
{
|
||||||
if(key is string s)
|
if(key is string s) return ObjectForKey(s);
|
||||||
return ObjectForKey(s);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -116,8 +119,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="value">Object to search up in the current instance.</param>
|
/// <param name="value">Object to search up in the current instance.</param>
|
||||||
public bool ContainsValue(object value)
|
public bool ContainsValue(object value)
|
||||||
{
|
{
|
||||||
if(value == null)
|
if(value == null) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
NSObject wrap = Wrap(value);
|
NSObject wrap = Wrap(value);
|
||||||
|
|
||||||
@@ -135,8 +137,7 @@ namespace Claunia.PropertyList
|
|||||||
/// </param>
|
/// </param>
|
||||||
public void Add(string key, object obj)
|
public void Add(string key, object obj)
|
||||||
{
|
{
|
||||||
if(obj == null)
|
if(obj == null) return;
|
||||||
return;
|
|
||||||
|
|
||||||
Add(key, Wrap(obj));
|
Add(key, Wrap(obj));
|
||||||
}
|
}
|
||||||
@@ -162,9 +163,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(string val)
|
public bool ContainsValue(string val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSString str &&
|
if(o is NSString str && str.Content.Equals(val)) return true;
|
||||||
str.Content.Equals(val))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -175,10 +174,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(long val)
|
public bool ContainsValue(long val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSNumber num &&
|
if(o is NSNumber num && num.isInteger() && num.ToInt() == val) return true;
|
||||||
num.isInteger() &&
|
|
||||||
num.ToInt() == val)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -189,10 +185,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(double val)
|
public bool ContainsValue(double val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSNumber num &&
|
if(o is NSNumber num && num.isReal() && num.ToDouble() == val) return true;
|
||||||
num.isReal() &&
|
|
||||||
num.ToDouble() == val)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -203,10 +196,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(bool val)
|
public bool ContainsValue(bool val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSNumber num &&
|
if(o is NSNumber num && num.isBoolean() && num.ToBool() == val) return true;
|
||||||
num.isBoolean() &&
|
|
||||||
num.ToBool() == val)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -217,9 +207,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(DateTime val)
|
public bool ContainsValue(DateTime val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSDate dat &&
|
if(o is NSDate dat && dat.Date.Equals(val)) return true;
|
||||||
dat.Date.Equals(val))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -230,9 +218,7 @@ namespace Claunia.PropertyList
|
|||||||
public bool ContainsValue(byte[] val)
|
public bool ContainsValue(byte[] val)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in dict.Values)
|
foreach(NSObject o in dict.Values)
|
||||||
if(o is NSData dat &&
|
if(o is NSData dat && ArrayEquals(dat.Bytes, val)) return true;
|
||||||
ArrayEquals(dat.Bytes, val))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -251,21 +237,17 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSDictionary dictionary)
|
if(obj is not NSDictionary dictionary) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(dictionary.dict.Count != dict.Count)
|
if(dictionary.dict.Count != dict.Count) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> kvp in dict)
|
foreach(KeyValuePair<string, NSObject> kvp in dict)
|
||||||
{
|
{
|
||||||
bool found = dictionary.dict.TryGetValue(kvp.Key, out NSObject nsoB);
|
bool found = dictionary.dict.TryGetValue(kvp.Key, out NSObject nsoB);
|
||||||
|
|
||||||
if(!found)
|
if(!found) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(!kvp.Value.Equals(nsoB))
|
if(!kvp.Value.Equals(nsoB)) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -279,7 +261,7 @@ namespace Claunia.PropertyList
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
hash = (83 * hash) + (dict != null ? dict.GetHashCode() : 0);
|
hash = 83 * hash + (dict != null ? dict.GetHashCode() : 0);
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -297,9 +279,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
//According to http://www.w3.org/TR/REC-xml/#syntax node values must not
|
//According to http://www.w3.org/TR/REC-xml/#syntax node values must not
|
||||||
//contain the characters < or &. Also the > character should be escaped.
|
//contain the characters < or &. Also the > character should be escaped.
|
||||||
if(kvp.Key.Contains("&") ||
|
if(kvp.Key.Contains("&") || kvp.Key.Contains("<") || kvp.Key.Contains(">"))
|
||||||
kvp.Key.Contains("<") ||
|
|
||||||
kvp.Key.Contains(">"))
|
|
||||||
{
|
{
|
||||||
xml.Append("<![CDATA[");
|
xml.Append("<![CDATA[");
|
||||||
xml.Append(kvp.Key.Replace("]]>", "]]]]><![CDATA[>"));
|
xml.Append(kvp.Key.Replace("]]>", "]]]]><![CDATA[>"));
|
||||||
@@ -322,22 +302,18 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
base.AssignIDs(outPlist);
|
base.AssignIDs(outPlist);
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> entry in dict)
|
foreach(KeyValuePair<string, NSObject> entry in dict) keys[entry.Key].AssignIDs(outPlist);
|
||||||
keys[entry.Key].AssignIDs(outPlist);
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> entry in dict)
|
foreach(KeyValuePair<string, NSObject> entry in dict) entry.Value.AssignIDs(outPlist);
|
||||||
entry.Value.AssignIDs(outPlist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
{
|
{
|
||||||
outPlist.WriteIntHeader(0xD, dict.Count);
|
outPlist.WriteIntHeader(0xD, dict.Count);
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> entry in dict)
|
foreach(KeyValuePair<string, NSObject> entry in dict) outPlist.WriteID(outPlist.GetID(keys[entry.Key]));
|
||||||
outPlist.WriteID(outPlist.GetID(keys[entry.Key]));
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> entry in dict)
|
foreach(KeyValuePair<string, NSObject> entry in dict) outPlist.WriteID(outPlist.GetID(entry.Value));
|
||||||
outPlist.WriteID(outPlist.GetID(entry.Value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -438,7 +414,8 @@ namespace Claunia.PropertyList
|
|||||||
ascii.Append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
|
ascii.Append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDictionary implementation
|
#region IDictionary implementation
|
||||||
|
|
||||||
/// <summary>Add the specified key and value.</summary>
|
/// <summary>Add the specified key and value.</summary>
|
||||||
/// <param name="key">Key.</param>
|
/// <param name="key">Key.</param>
|
||||||
/// <param name="value">Value.</param>
|
/// <param name="value">Value.</param>
|
||||||
@@ -480,8 +457,7 @@ namespace Claunia.PropertyList
|
|||||||
get => dict[index];
|
get => dict[index];
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if(!keys.ContainsKey(index))
|
if(!keys.ContainsKey(index)) keys.Add(index, new NSString(index));
|
||||||
keys.Add(index, new NSString(index));
|
|
||||||
|
|
||||||
dict[index] = value;
|
dict[index] = value;
|
||||||
}
|
}
|
||||||
@@ -494,9 +470,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <summary>Gets an array with all the objects contained in the current instance.</summary>
|
/// <summary>Gets an array with all the objects contained in the current instance.</summary>
|
||||||
/// <value>The objects.</value>
|
/// <value>The objects.</value>
|
||||||
public ICollection<NSObject> Values => dict.Values;
|
public ICollection<NSObject> Values => dict.Values;
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ICollection implementation
|
#endregion
|
||||||
|
|
||||||
|
#region ICollection implementation
|
||||||
|
|
||||||
/// <summary>Adds the specified item.</summary>
|
/// <summary>Adds the specified item.</summary>
|
||||||
/// <param name="item">Item.</param>
|
/// <param name="item">Item.</param>
|
||||||
public void Add(KeyValuePair<string, NSObject> item)
|
public void Add(KeyValuePair<string, NSObject> item)
|
||||||
@@ -549,6 +527,6 @@ namespace Claunia.PropertyList
|
|||||||
/// <summary>Gets a value indicating whether this instance is read only.</summary>
|
/// <summary>Gets a value indicating whether this instance is read only.</summary>
|
||||||
/// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
|
||||||
public bool IsReadOnly => false;
|
public bool IsReadOnly => false;
|
||||||
#endregion
|
|
||||||
}
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ using System;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>A number whose value is either an integer, a real number or bool.</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSNumber : NSObject, IComparable
|
||||||
{
|
{
|
||||||
/// <summary>A number whose value is either an integer, a real number or bool.</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSNumber : NSObject, IComparable
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the number's value is an integer. The number is stored as a .NET <see cref="long" />. Its
|
/// Indicates that the number's value is an integer. The number is stored as a .NET <see cref="long" />. Its
|
||||||
/// original value could have been char, short, int, long or even long long.
|
/// original value could have been char, short, int, long or even long long.
|
||||||
@@ -79,7 +79,8 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: throw new ArgumentException("Type argument is not valid.", nameof(type));
|
default:
|
||||||
|
throw new ArgumentException("Type argument is not valid.", nameof(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -118,12 +119,10 @@ namespace Claunia.PropertyList
|
|||||||
/// <seealso cref="double.Parse(string, IFormatProvider)" />
|
/// <seealso cref="double.Parse(string, IFormatProvider)" />
|
||||||
public NSNumber(string text)
|
public NSNumber(string text)
|
||||||
{
|
{
|
||||||
if(text == null)
|
if(text == null) throw new ArgumentException("The given string is null and cannot be parsed as number.");
|
||||||
throw new ArgumentException("The given string is null and cannot be parsed as number.");
|
|
||||||
|
|
||||||
if(text.StartsWith("0x") &&
|
if(text.StartsWith("0x") &&
|
||||||
long.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture,
|
long.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long l))
|
||||||
out long l))
|
|
||||||
{
|
{
|
||||||
doubleValue = longValue = l;
|
doubleValue = longValue = l;
|
||||||
type = INTEGER;
|
type = INTEGER;
|
||||||
@@ -154,8 +153,7 @@ namespace Claunia.PropertyList
|
|||||||
doubleValue = longValue = boolValue ? 1 : 0;
|
doubleValue = longValue = boolValue ? 1 : 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new
|
throw new ArgumentException("The given string neither represents a double, an int nor a bool value.");
|
||||||
ArgumentException("The given string neither represents a double, an int nor a bool value.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +212,7 @@ namespace Claunia.PropertyList
|
|||||||
: 1;
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!IsNumber(o))
|
if(!IsNumber(o)) return -1;
|
||||||
return -1;
|
|
||||||
|
|
||||||
y = GetDoubleFromObject(o);
|
y = GetDoubleFromObject(o);
|
||||||
|
|
||||||
@@ -249,8 +246,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <returns><c>true</c> if the value is true or non-zero, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if the value is true or non-zero, <c>false</c> otherwise.</returns>
|
||||||
public bool ToBool()
|
public bool ToBool()
|
||||||
{
|
{
|
||||||
if(type == BOOLEAN)
|
if(type == BOOLEAN) return boolValue;
|
||||||
return boolValue;
|
|
||||||
|
|
||||||
return longValue != 0;
|
return longValue != 0;
|
||||||
}
|
}
|
||||||
@@ -282,10 +278,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <returns>Whether the objects are equal in terms of numeric value and type.</returns>
|
/// <returns>Whether the objects are equal in terms of numeric value and type.</returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSNumber number)
|
if(obj is not NSNumber number) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return type == number.type && longValue == number.longValue && doubleValue == number.doubleValue &&
|
return type == number.type &&
|
||||||
|
longValue == number.longValue &&
|
||||||
|
doubleValue == number.doubleValue &&
|
||||||
boolValue == number.boolValue;
|
boolValue == number.boolValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,12 +294,13 @@ namespace Claunia.PropertyList
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
int hash = type;
|
int hash = type;
|
||||||
hash = (37 * hash) + (int)(longValue ^ ((uint)longValue >> 32));
|
hash = 37 * hash + (int)(longValue ^ (uint)longValue >> 32);
|
||||||
|
|
||||||
hash = (37 * hash) + (int)(BitConverter.DoubleToInt64Bits(doubleValue) ^
|
hash = 37 * hash +
|
||||||
|
(int)(BitConverter.DoubleToInt64Bits(doubleValue) ^
|
||||||
(uint)(BitConverter.DoubleToInt64Bits(doubleValue) >> 32));
|
(uint)(BitConverter.DoubleToInt64Bits(doubleValue) >> 32));
|
||||||
|
|
||||||
hash = (37 * hash) + (ToBool() ? 1 : 0);
|
hash = 37 * hash + (ToBool() ? 1 : 0);
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -483,11 +481,9 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSNumber number)
|
if(obj is not NSNumber number) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(number.GetNSNumberType() != type)
|
if(number.GetNSNumberType() != type) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
@@ -541,5 +537,4 @@ namespace Claunia.PropertyList
|
|||||||
public static explicit operator NSNumber(float value) => new(value);
|
public static explicit operator NSNumber(float value) => new(value);
|
||||||
|
|
||||||
public static explicit operator NSNumber(bool value) => new(value);
|
public static explicit operator NSNumber(bool value) => new(value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,16 +27,16 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Abstract interface for any object contained in a property list.</para>
|
||||||
|
/// <para>The names and functions of the various objects orient themselves towards Apple's Cocoa API.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public abstract class NSObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>Abstract interface for any object contained in a property list.</para>
|
|
||||||
/// <para>The names and functions of the various objects orient themselves towards Apple's Cocoa API.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public abstract class NSObject
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The newline character used for generating the XML output. To maintain compatibility with the Apple format,
|
/// The newline character used for generating the XML output. To maintain compatibility with the Apple format,
|
||||||
/// only a newline character is used (as opposed to cr+lf which is normally used on Windows).
|
/// only a newline character is used (as opposed to cr+lf which is normally used on Windows).
|
||||||
@@ -108,8 +108,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="level">The level of indentation.</param>
|
/// <param name="level">The level of indentation.</param>
|
||||||
internal static void Indent(StringBuilder xml, int level)
|
internal static void Indent(StringBuilder xml, int level)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < level; i++)
|
for(int i = 0; i < level; i++) xml.Append(INDENT);
|
||||||
xml.Append(INDENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Wraps the given value inside a NSObject.</summary>
|
/// <summary>Wraps the given value inside a NSObject.</summary>
|
||||||
@@ -140,8 +139,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
var arr = new NSArray(value.Length);
|
var arr = new NSArray(value.Length);
|
||||||
|
|
||||||
for(int i = 0; i < value.Length; i++)
|
for(int i = 0; i < value.Length; i++) arr.Add(Wrap(value[i]));
|
||||||
arr.Add(Wrap(value[i]));
|
|
||||||
|
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
@@ -154,8 +152,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
var dict = new NSDictionary();
|
var dict = new NSDictionary();
|
||||||
|
|
||||||
foreach(KeyValuePair<string, object> kvp in value)
|
foreach(KeyValuePair<string, object> kvp in value) dict.Add(kvp.Key, Wrap(kvp.Value));
|
||||||
dict.Add(kvp.Key, Wrap(kvp.Value));
|
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
@@ -168,8 +165,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
var set = new NSSet();
|
var set = new NSSet();
|
||||||
|
|
||||||
foreach(object o in value)
|
foreach(object o in value) set.AddObject(Wrap(o));
|
||||||
set.AddObject(Wrap(o));
|
|
||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
@@ -196,55 +192,42 @@ namespace Claunia.PropertyList
|
|||||||
/// <returns>A NSObject equivalent to the given object.</returns>
|
/// <returns>A NSObject equivalent to the given object.</returns>
|
||||||
public static NSObject Wrap(object o)
|
public static NSObject Wrap(object o)
|
||||||
{
|
{
|
||||||
if(o == null)
|
if(o == null) throw new NullReferenceException("A null object cannot be wrapped as a NSObject");
|
||||||
throw new NullReferenceException("A null object cannot be wrapped as a NSObject");
|
|
||||||
|
|
||||||
if(o is NSObject nsObject)
|
if(o is NSObject nsObject) return nsObject;
|
||||||
return nsObject;
|
|
||||||
|
|
||||||
Type c = o.GetType();
|
Type c = o.GetType();
|
||||||
|
|
||||||
if(typeof(bool).Equals(c))
|
if(typeof(bool).Equals(c)) return Wrap((bool)o);
|
||||||
return Wrap((bool)o);
|
|
||||||
|
|
||||||
if(typeof(byte).Equals(c))
|
if(typeof(byte).Equals(c)) return Wrap((byte)o);
|
||||||
return Wrap((byte)o);
|
|
||||||
|
|
||||||
if(typeof(short).Equals(c))
|
if(typeof(short).Equals(c)) return Wrap((short)o);
|
||||||
return Wrap((short)o);
|
|
||||||
|
|
||||||
if(typeof(int).Equals(c))
|
if(typeof(int).Equals(c)) return Wrap((int)o);
|
||||||
return Wrap((int)o);
|
|
||||||
|
|
||||||
if(typeof(long).IsAssignableFrom(c))
|
if(typeof(long).IsAssignableFrom(c)) return Wrap((long)o);
|
||||||
return Wrap((long)o);
|
|
||||||
|
|
||||||
if(typeof(float).Equals(c))
|
if(typeof(float).Equals(c)) return Wrap((float)o);
|
||||||
return Wrap((float)o);
|
|
||||||
|
|
||||||
if(typeof(double).IsAssignableFrom(c))
|
if(typeof(double).IsAssignableFrom(c)) return Wrap((double)o);
|
||||||
return Wrap((double)o);
|
|
||||||
|
|
||||||
if(typeof(string).Equals(c))
|
if(typeof(string).Equals(c)) return new NSString((string)o);
|
||||||
return new NSString((string)o);
|
|
||||||
|
|
||||||
if(typeof(DateTime).Equals(c))
|
if(typeof(DateTime).Equals(c)) return new NSDate((DateTime)o);
|
||||||
return new NSDate((DateTime)o);
|
|
||||||
|
|
||||||
if(c.IsArray)
|
if(c.IsArray)
|
||||||
{
|
{
|
||||||
Type cc = c.GetElementType();
|
Type cc = c.GetElementType();
|
||||||
|
|
||||||
if(cc.Equals(typeof(byte)))
|
if(cc.Equals(typeof(byte))) return Wrap((byte[])o);
|
||||||
return Wrap((byte[])o);
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(bool)))
|
if(cc.Equals(typeof(bool)))
|
||||||
{
|
{
|
||||||
bool[] array = (bool[])o;
|
bool[] array = (bool[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -254,8 +237,7 @@ namespace Claunia.PropertyList
|
|||||||
float[] array = (float[])o;
|
float[] array = (float[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -265,8 +247,7 @@ namespace Claunia.PropertyList
|
|||||||
double[] array = (double[])o;
|
double[] array = (double[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -276,8 +257,7 @@ namespace Claunia.PropertyList
|
|||||||
short[] array = (short[])o;
|
short[] array = (short[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -287,8 +267,7 @@ namespace Claunia.PropertyList
|
|||||||
int[] array = (int[])o;
|
int[] array = (int[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -298,8 +277,7 @@ namespace Claunia.PropertyList
|
|||||||
long[] array = (long[])o;
|
long[] array = (long[])o;
|
||||||
var nsa = new NSArray(array.Length);
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
return nsa;
|
||||||
}
|
}
|
||||||
@@ -309,23 +287,21 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
if(typeof(Dictionary<string, object>).IsAssignableFrom(c))
|
if(typeof(Dictionary<string, object>).IsAssignableFrom(c))
|
||||||
{
|
{
|
||||||
Dictionary<string, object> netDict = (Dictionary<string, object>)o;
|
var netDict = (Dictionary<string, object>)o;
|
||||||
var dict = new NSDictionary();
|
var dict = new NSDictionary();
|
||||||
|
|
||||||
foreach(KeyValuePair<string, object> kvp in netDict)
|
foreach(KeyValuePair<string, object> kvp in netDict) dict.Add(kvp.Key, Wrap(kvp.Value));
|
||||||
dict.Add(kvp.Key, Wrap(kvp.Value));
|
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof(List<object>).IsAssignableFrom(c))
|
if(typeof(List<object>).IsAssignableFrom(c)) return Wrap(((List<object>)o).ToArray());
|
||||||
return Wrap(((List<object>)o).ToArray());
|
|
||||||
|
|
||||||
if(typeof(List<Dictionary<string, object>>).IsAssignableFrom(c))
|
if(typeof(List<Dictionary<string, object>>).IsAssignableFrom(c))
|
||||||
{
|
{
|
||||||
var list = new NSArray();
|
var list = new NSArray();
|
||||||
foreach(Dictionary<string, object> dict in (List<Dictionary<string, object>>)o)
|
foreach(Dictionary<string, object> dict in (List<Dictionary<string, object>>)o) list.Add(Wrap(dict));
|
||||||
list.Add(Wrap(dict));
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +335,7 @@ namespace Claunia.PropertyList
|
|||||||
var nsArray = (NSArray)this;
|
var nsArray = (NSArray)this;
|
||||||
object[] array = new object[nsArray.Count];
|
object[] array = new object[nsArray.Count];
|
||||||
|
|
||||||
for(int i = 0; i < nsArray.Count; i++)
|
for(int i = 0; i < nsArray.Count; i++) array[i] = nsArray[i].ToObject();
|
||||||
array[i] = nsArray[i].ToObject();
|
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
@@ -369,8 +344,7 @@ namespace Claunia.PropertyList
|
|||||||
Dictionary<string, NSObject> dictA = ((NSDictionary)this).GetDictionary();
|
Dictionary<string, NSObject> dictA = ((NSDictionary)this).GetDictionary();
|
||||||
Dictionary<string, object> dictB = new(dictA.Count);
|
Dictionary<string, object> dictB = new(dictA.Count);
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> kvp in dictA)
|
foreach(KeyValuePair<string, NSObject> kvp in dictA) dictB.Add(kvp.Key, kvp.Value.ToObject());
|
||||||
dictB.Add(kvp.Key, kvp.Value.ToObject());
|
|
||||||
|
|
||||||
return dictB;
|
return dictB;
|
||||||
}
|
}
|
||||||
@@ -379,8 +353,7 @@ namespace Claunia.PropertyList
|
|||||||
List<NSObject> setA = ((NSSet)this).GetSet();
|
List<NSObject> setA = ((NSSet)this).GetSet();
|
||||||
List<object> setB = new();
|
List<object> setB = new();
|
||||||
|
|
||||||
foreach(NSObject o in setA)
|
foreach(NSObject o in setA) setB.Add(o.ToObject());
|
||||||
setB.Add(o.ToObject());
|
|
||||||
|
|
||||||
return setB;
|
return setB;
|
||||||
}
|
}
|
||||||
@@ -394,46 +367,49 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
long longVal = num.ToLong();
|
long longVal = num.ToLong();
|
||||||
|
|
||||||
if(longVal is > int.MaxValue or < int.MinValue)
|
if(longVal is > int.MaxValue or < int.MinValue) return longVal;
|
||||||
return longVal;
|
|
||||||
|
|
||||||
return num.ToInt();
|
return num.ToInt();
|
||||||
}
|
}
|
||||||
case NSNumber.REAL: return num.ToDouble();
|
case NSNumber.REAL:
|
||||||
case NSNumber.BOOLEAN: return num.ToBool();
|
return num.ToDouble();
|
||||||
default: return num.ToDouble();
|
case NSNumber.BOOLEAN:
|
||||||
|
return num.ToBool();
|
||||||
|
default:
|
||||||
|
return num.ToDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NSString: return ((NSString)this).Content;
|
case NSString:
|
||||||
case NSData: return ((NSData)this).Bytes;
|
return ((NSString)this).Content;
|
||||||
case NSDate: return ((NSDate)this).Date;
|
case NSData:
|
||||||
case UID: return ((UID)this).Bytes;
|
return ((NSData)this).Bytes;
|
||||||
default: return this;
|
case NSDate:
|
||||||
|
return ((NSDate)this).Date;
|
||||||
|
case UID:
|
||||||
|
return ((UID)this).Bytes;
|
||||||
|
default:
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
internal static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
||||||
{
|
{
|
||||||
if(arrayA.Length != arrayB.Length)
|
if(arrayA.Length != arrayB.Length) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < arrayA.Length; i++)
|
for(int i = 0; i < arrayA.Length; i++)
|
||||||
if(arrayA[i] != arrayB[i])
|
if(arrayA[i] != arrayB[i]) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ArrayEquals(IList<NSObject> arrayA, IList<NSObject> arrayB)
|
internal static bool ArrayEquals(IList<NSObject> arrayA, IList<NSObject> arrayB)
|
||||||
{
|
{
|
||||||
if(arrayA.Count != arrayB.Count)
|
if(arrayA.Count != arrayB.Count) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < arrayA.Count; i++)
|
for(int i = 0; i < arrayA.Count; i++)
|
||||||
if(arrayA[i] != arrayB[i])
|
if(arrayA[i] != arrayB[i]) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -448,5 +424,4 @@ namespace Claunia.PropertyList
|
|||||||
/// <see cref="Claunia.PropertyList.NSObject" />; otherwise, <c>false</c>.
|
/// <see cref="Claunia.PropertyList.NSObject" />; otherwise, <c>false</c>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public abstract bool Equals(NSObject obj);
|
public abstract bool Equals(NSObject obj);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -28,33 +28,33 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>A set is an interface to an unordered collection of objects.</para>
|
||||||
|
/// <para>This implementation uses a <see cref="List{T}" />as the underlying data structure.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSSet : NSObject, IEnumerable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// <para>A set is an interface to an unordered collection of objects.</para>
|
|
||||||
/// <para>This implementation uses a <see cref="List{T}" />as the underlying data structure.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSSet : NSObject, IEnumerable
|
|
||||||
{
|
|
||||||
readonly bool ordered;
|
readonly bool ordered;
|
||||||
readonly List<NSObject> set;
|
readonly List<NSObject> set;
|
||||||
|
|
||||||
/// <summary>Creates an empty unordered set.</summary>
|
/// <summary>Creates an empty unordered set.</summary>
|
||||||
public NSSet() => set = new List<NSObject>();
|
public NSSet() => set = [];
|
||||||
|
|
||||||
/// <summary>Creates an empty set.</summary>
|
/// <summary>Creates an empty set.</summary>
|
||||||
/// <param name="ordered">Should the set be ordered on operations?</param>
|
/// <param name="ordered">Should the set be ordered on operations?</param>
|
||||||
public NSSet(bool ordered)
|
public NSSet(bool ordered)
|
||||||
{
|
{
|
||||||
this.ordered = ordered;
|
this.ordered = ordered;
|
||||||
set = new List<NSObject>();
|
set = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a set and fill it with the given objects.</summary>
|
/// <summary>Creates a set and fill it with the given objects.</summary>
|
||||||
/// <param name="objects">The objects to populate the set.</param>
|
/// <param name="objects">The objects to populate the set.</param>
|
||||||
public NSSet(params NSObject[] objects) => set = new List<NSObject>(objects);
|
public NSSet(params NSObject[] objects) => set = [..objects];
|
||||||
|
|
||||||
/// <summary>Creates a set and fill it with the given objects.</summary>
|
/// <summary>Creates a set and fill it with the given objects.</summary>
|
||||||
/// <param name="objects">The objects to populate the set.</param>
|
/// <param name="objects">The objects to populate the set.</param>
|
||||||
@@ -64,8 +64,7 @@ namespace Claunia.PropertyList
|
|||||||
this.ordered = ordered;
|
this.ordered = ordered;
|
||||||
set = new List<NSObject>(objects);
|
set = new List<NSObject>(objects);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Gets the number of elements in the set.</summary>
|
/// <summary>Gets the number of elements in the set.</summary>
|
||||||
@@ -75,9 +74,11 @@ namespace Claunia.PropertyList
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock(set)
|
lock(set)
|
||||||
|
{
|
||||||
return set.Count;
|
return set.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an enumerator object that lets you iterate over all elements of the set. This is the equivalent to
|
/// Returns an enumerator object that lets you iterate over all elements of the set. This is the equivalent to
|
||||||
@@ -87,8 +88,10 @@ namespace Claunia.PropertyList
|
|||||||
public IEnumerator GetEnumerator()
|
public IEnumerator GetEnumerator()
|
||||||
{
|
{
|
||||||
lock(set)
|
lock(set)
|
||||||
|
{
|
||||||
return set.GetEnumerator();
|
return set.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Adds an object to the set.</summary>
|
/// <summary>Adds an object to the set.</summary>
|
||||||
/// <param name="obj">The object to add.</param>
|
/// <param name="obj">The object to add.</param>
|
||||||
@@ -98,8 +101,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
set.Add(obj);
|
set.Add(obj);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +113,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
set.Remove(obj);
|
set.Remove(obj);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,16 +122,20 @@ namespace Claunia.PropertyList
|
|||||||
public NSObject[] AllObjects()
|
public NSObject[] AllObjects()
|
||||||
{
|
{
|
||||||
lock(set)
|
lock(set)
|
||||||
|
{
|
||||||
return set.ToArray();
|
return set.ToArray();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Returns one of the objects in the set, or <c>null</c> if the set contains no objects.</summary>
|
/// <summary>Returns one of the objects in the set, or <c>null</c> if the set contains no objects.</summary>
|
||||||
/// <returns>The first object in the set, or <c>null</c> if the set is empty.</returns>
|
/// <returns>The first object in the set, or <c>null</c> if the set is empty.</returns>
|
||||||
public NSObject AnyObject()
|
public NSObject AnyObject()
|
||||||
{
|
{
|
||||||
lock(set)
|
lock(set)
|
||||||
|
{
|
||||||
return set.Count == 0 ? null : set[0];
|
return set.Count == 0 ? null : set[0];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Finds out whether a given object is contained in the set.</summary>
|
/// <summary>Finds out whether a given object is contained in the set.</summary>
|
||||||
/// <returns><c>true</c>, when the object was found, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c>, when the object was found, <c>false</c> otherwise.</returns>
|
||||||
@@ -148,8 +153,7 @@ namespace Claunia.PropertyList
|
|||||||
lock(set)
|
lock(set)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in set)
|
foreach(NSObject o in set)
|
||||||
if(o.Equals(obj))
|
if(o.Equals(obj)) return o;
|
||||||
return o;
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -163,8 +167,7 @@ namespace Claunia.PropertyList
|
|||||||
lock(set)
|
lock(set)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in set)
|
foreach(NSObject o in set)
|
||||||
if(otherSet.ContainsObject(o))
|
if(otherSet.ContainsObject(o)) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -178,8 +181,7 @@ namespace Claunia.PropertyList
|
|||||||
lock(set)
|
lock(set)
|
||||||
{
|
{
|
||||||
foreach(NSObject o in set)
|
foreach(NSObject o in set)
|
||||||
if(!otherSet.ContainsObject(o))
|
if(!otherSet.ContainsObject(o)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -197,7 +199,7 @@ namespace Claunia.PropertyList
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
hash = (29 * hash) + (set != null ? set.GetHashCode() : 0);
|
hash = 29 * hash + (set != null ? set.GetHashCode() : 0);
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -216,11 +218,9 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if(obj == null)
|
if(obj == null) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(GetType() != obj.GetType())
|
if(GetType() != obj.GetType()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
var other = (NSSet)obj;
|
var other = (NSSet)obj;
|
||||||
|
|
||||||
@@ -239,8 +239,7 @@ namespace Claunia.PropertyList
|
|||||||
xml.Append("<array>");
|
xml.Append("<array>");
|
||||||
xml.Append(NEWLINE);
|
xml.Append(NEWLINE);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
foreach(NSObject o in set)
|
foreach(NSObject o in set)
|
||||||
{
|
{
|
||||||
@@ -256,8 +255,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
base.AssignIDs(outPlist);
|
base.AssignIDs(outPlist);
|
||||||
|
|
||||||
foreach(NSObject obj in set)
|
foreach(NSObject obj in set) obj.AssignIDs(outPlist);
|
||||||
obj.AssignIDs(outPlist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
@@ -270,8 +268,7 @@ namespace Claunia.PropertyList
|
|||||||
else
|
else
|
||||||
outPlist.WriteIntHeader(0xC, set.Count);
|
outPlist.WriteIntHeader(0xC, set.Count);
|
||||||
|
|
||||||
foreach(NSObject obj in set)
|
foreach(NSObject obj in set) outPlist.WriteID(outPlist.GetID(obj));
|
||||||
outPlist.WriteID(outPlist.GetID(obj));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -284,8 +281,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
Indent(ascii, level);
|
Indent(ascii, level);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
NSObject[] array = AllObjects();
|
NSObject[] array = AllObjects();
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
@@ -295,7 +291,8 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
Type objClass = array[i].GetType();
|
Type objClass = array[i].GetType();
|
||||||
|
|
||||||
if((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) ||
|
if((objClass.Equals(typeof(NSDictionary)) ||
|
||||||
|
objClass.Equals(typeof(NSArray)) ||
|
||||||
objClass.Equals(typeof(NSData))) &&
|
objClass.Equals(typeof(NSData))) &&
|
||||||
indexOfLastNewLine != ascii.Length)
|
indexOfLastNewLine != ascii.Length)
|
||||||
{
|
{
|
||||||
@@ -305,17 +302,14 @@ namespace Claunia.PropertyList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(i != 0)
|
if(i != 0) ascii.Append(" ");
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCII(ascii, 0);
|
array[i].ToASCII(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i != array.Length - 1)
|
if(i != array.Length - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
@@ -334,8 +328,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
Indent(ascii, level);
|
Indent(ascii, level);
|
||||||
|
|
||||||
if(ordered)
|
if(ordered) set.Sort();
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
NSObject[] array = AllObjects();
|
NSObject[] array = AllObjects();
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
@@ -343,8 +336,7 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
for(int i = 0; i < array.Length; i++)
|
||||||
{
|
{
|
||||||
if(array[i] is NSDictionary or NSArray or NSData &&
|
if(array[i] is NSDictionary or NSArray or NSData && indexOfLastNewLine != ascii.Length)
|
||||||
indexOfLastNewLine != ascii.Length)
|
|
||||||
{
|
{
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
@@ -352,17 +344,14 @@ namespace Claunia.PropertyList
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(i != 0)
|
if(i != 0) ascii.Append(" ");
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCIIGnuStep(ascii, 0);
|
array[i].ToASCIIGnuStep(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i != array.Length - 1)
|
if(i != array.Length - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
@@ -385,17 +374,13 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSSet nsSet)
|
if(obj is not NSSet nsSet) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
if(set.Count != nsSet.Count)
|
if(set.Count != nsSet.Count) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach(NSObject objS in nsSet)
|
foreach(NSObject objS in nsSet)
|
||||||
if(!set.Contains(objS))
|
if(!set.Contains(objS)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,13 @@ using System;
|
|||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>A NSString contains a string.</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class NSString : NSObject, IComparable
|
||||||
{
|
{
|
||||||
/// <summary>A NSString contains a string.</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSString : NSObject, IComparable
|
|
||||||
{
|
|
||||||
static Encoding asciiEncoder, utf16beEncoder, utf8Encoder;
|
static Encoding asciiEncoder, utf16beEncoder, utf8Encoder;
|
||||||
|
|
||||||
/// <summary>Creates a NSString from its binary representation.</summary>
|
/// <summary>Creates a NSString from its binary representation.</summary>
|
||||||
@@ -48,11 +48,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
||||||
public NSString(ReadOnlySpan<byte> bytes, Encoding encoding)
|
public NSString(ReadOnlySpan<byte> bytes, Encoding encoding)
|
||||||
{
|
{
|
||||||
#if NATIVE_SPAN
|
#if NATIVE_SPAN
|
||||||
Content = encoding.GetString(bytes);
|
Content = encoding.GetString(bytes);
|
||||||
#else
|
#else
|
||||||
Content = encoding.GetString(bytes.ToArray());
|
Content = encoding.GetString(bytes.ToArray());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a NSString from a string.</summary>
|
/// <summary>Creates a NSString from a string.</summary>
|
||||||
@@ -68,7 +68,9 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSString" />.</param>
|
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSString" />.</param>
|
||||||
public int CompareTo(object o) => o switch
|
public int CompareTo(object o) => o switch
|
||||||
{
|
{
|
||||||
NSString nsString => string.Compare(Content, nsString.Content, StringComparison.Ordinal),
|
NSString nsString => string.Compare(Content,
|
||||||
|
nsString.Content,
|
||||||
|
StringComparison.Ordinal),
|
||||||
string s => string.Compare(Content, s, StringComparison.Ordinal),
|
string s => string.Compare(Content, s, StringComparison.Ordinal),
|
||||||
_ => -1
|
_ => -1
|
||||||
};
|
};
|
||||||
@@ -150,7 +152,8 @@ namespace Claunia.PropertyList
|
|||||||
lock(typeof(NSString))
|
lock(typeof(NSString))
|
||||||
{
|
{
|
||||||
// Not much use, because some characters do not fallback to exception, even if not ASCII
|
// Not much use, because some characters do not fallback to exception, even if not ASCII
|
||||||
asciiEncoder ??= Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback,
|
asciiEncoder ??= Encoding.GetEncoding("ascii",
|
||||||
|
EncoderFallback.ExceptionFallback,
|
||||||
DecoderFallback.ExceptionFallback);
|
DecoderFallback.ExceptionFallback);
|
||||||
|
|
||||||
if(IsASCIIEncodable(Content))
|
if(IsASCIIEncodable(Content))
|
||||||
@@ -201,18 +204,19 @@ namespace Claunia.PropertyList
|
|||||||
char[] cArray = s.ToCharArray();
|
char[] cArray = s.ToCharArray();
|
||||||
|
|
||||||
foreach(char c in cArray)
|
foreach(char c in cArray)
|
||||||
|
{
|
||||||
if(c > 127)
|
if(c > 127)
|
||||||
{
|
{
|
||||||
//non-ASCII Unicode
|
//non-ASCII Unicode
|
||||||
outString += "\\U";
|
outString += "\\U";
|
||||||
string hex = $"{c:x}";
|
string hex = $"{c:x}";
|
||||||
|
|
||||||
while(hex.Length < 4)
|
while(hex.Length < 4) hex = "0" + hex;
|
||||||
hex = "0" + hex;
|
|
||||||
|
|
||||||
outString += hex;
|
outString += hex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
outString += c switch
|
outString += c switch
|
||||||
{
|
{
|
||||||
'\\' => "\\\\",
|
'\\' => "\\\\",
|
||||||
@@ -223,6 +227,8 @@ namespace Claunia.PropertyList
|
|||||||
'\t' => "\\t",
|
'\t' => "\\t",
|
||||||
_ => c
|
_ => c
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return outString;
|
return outString;
|
||||||
}
|
}
|
||||||
@@ -241,8 +247,7 @@ namespace Claunia.PropertyList
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override bool Equals(NSObject obj)
|
public override bool Equals(NSObject obj)
|
||||||
{
|
{
|
||||||
if(obj is not NSString nsString)
|
if(obj is not NSString nsString) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return Content == nsString.Content;
|
return Content == nsString.Content;
|
||||||
}
|
}
|
||||||
@@ -250,8 +255,7 @@ namespace Claunia.PropertyList
|
|||||||
internal static bool IsASCIIEncodable(string text)
|
internal static bool IsASCIIEncodable(string text)
|
||||||
{
|
{
|
||||||
foreach(char c in text)
|
foreach(char c in text)
|
||||||
if(c > 0x7F)
|
if(c > 0x7F) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -259,5 +263,4 @@ namespace Claunia.PropertyList
|
|||||||
public static explicit operator string(NSString value) => value.Content;
|
public static explicit operator string(NSString value) => value.Content;
|
||||||
|
|
||||||
public static explicit operator NSString(string value) => new(value);
|
public static explicit operator NSString(string value) => new(value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,12 +27,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>The exception that is thrown when an property list file could not be processed correctly.</summary>
|
||||||
|
[Serializable]
|
||||||
|
public class PropertyListException : Exception
|
||||||
{
|
{
|
||||||
/// <summary>The exception that is thrown when an property list file could not be processed correctly.</summary>
|
|
||||||
[Serializable]
|
|
||||||
public class PropertyListException : Exception
|
|
||||||
{
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
||||||
public PropertyListException() {}
|
public PropertyListException() {}
|
||||||
|
|
||||||
@@ -49,5 +49,4 @@ namespace Claunia.PropertyList
|
|||||||
public PropertyListException(string message, Exception inner) : base(message, inner) {}
|
public PropertyListException(string message, Exception inner) : base(message, inner) {}
|
||||||
|
|
||||||
protected PropertyListException(SerializationInfo info, StreamingContext context) : base(info, context) {}
|
protected PropertyListException(SerializationInfo info, StreamingContext context) : base(info, context) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -23,18 +23,17 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A PropertyListFormatException is thrown by the various property list format parsers when an error in the
|
||||||
|
/// format of the given property list is encountered.
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class PropertyListFormatException : PropertyListException
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A PropertyListFormatException is thrown by the various property list format parsers when an error in the
|
|
||||||
/// format of the given property list is encountered.
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class PropertyListFormatException : PropertyListException
|
|
||||||
{
|
|
||||||
/// <summary>Creates a new exception with the given message.</summary>
|
/// <summary>Creates a new exception with the given message.</summary>
|
||||||
/// <param name="message">A message containing information about the nature of the exception.</param>
|
/// <param name="message">A message containing information about the nature of the exception.</param>
|
||||||
public PropertyListFormatException(string message) : base(message) {}
|
public PropertyListFormatException(string message) : base(message) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,16 +27,16 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public static class PropertyListParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public static class PropertyListParser
|
|
||||||
{
|
|
||||||
const int TYPE_XML = 0;
|
const int TYPE_XML = 0;
|
||||||
const int TYPE_BINARY = 1;
|
const int TYPE_BINARY = 1;
|
||||||
const int TYPE_ASCII = 2;
|
const int TYPE_ASCII = 2;
|
||||||
@@ -48,16 +48,11 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="dataBeginning">The very first bytes of data of the property list (minus any whitespace) as a string</param>
|
/// <param name="dataBeginning">The very first bytes of data of the property list (minus any whitespace) as a string</param>
|
||||||
static int DetermineTypeExact(ReadOnlySpan<byte> dataBeginning)
|
static int DetermineTypeExact(ReadOnlySpan<byte> dataBeginning)
|
||||||
{
|
{
|
||||||
if(dataBeginning.Length == 0)
|
if(dataBeginning.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
if(dataBeginning[0] == '(' ||
|
if(dataBeginning[0] == '(' || dataBeginning[0] == '{' || dataBeginning[0] == '/') return TYPE_ASCII;
|
||||||
dataBeginning[0] == '{' ||
|
|
||||||
dataBeginning[0] == '/')
|
|
||||||
return TYPE_ASCII;
|
|
||||||
|
|
||||||
if(dataBeginning[0] == '<')
|
if(dataBeginning[0] == '<') return TYPE_XML;
|
||||||
return TYPE_XML;
|
|
||||||
|
|
||||||
if(dataBeginning.Length >= 6 &&
|
if(dataBeginning.Length >= 6 &&
|
||||||
dataBeginning[0] == 'b' &&
|
dataBeginning[0] == 'b' &&
|
||||||
@@ -76,20 +71,19 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="bytes">The type of the property list</param>
|
/// <param name="bytes">The type of the property list</param>
|
||||||
static int DetermineType(ReadOnlySpan<byte> bytes)
|
static int DetermineType(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
if(bytes.Length == 0)
|
if(bytes.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
//Skip any possible whitespace at the beginning of the file
|
//Skip any possible whitespace at the beginning of the file
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
if(bytes.Length >= 3 &&
|
if(bytes.Length >= 3 && (bytes[0] & 0xFF) == 0xEF && (bytes[1] & 0xFF) == 0xBB && (bytes[2] & 0xFF) == 0xBF)
|
||||||
(bytes[0] & 0xFF) == 0xEF &&
|
|
||||||
(bytes[1] & 0xFF) == 0xBB &&
|
|
||||||
(bytes[2] & 0xFF) == 0xBF)
|
|
||||||
offset += 3;
|
offset += 3;
|
||||||
|
|
||||||
while(offset < bytes.Length &&
|
while(offset < bytes.Length &&
|
||||||
(bytes[offset] == ' ' || bytes[offset] == '\t' || bytes[offset] == '\r' || bytes[offset] == '\n' ||
|
(bytes[offset] == ' ' ||
|
||||||
|
bytes[offset] == '\t' ||
|
||||||
|
bytes[offset] == '\r' ||
|
||||||
|
bytes[offset] == '\n' ||
|
||||||
bytes[offset] == '\f'))
|
bytes[offset] == '\f'))
|
||||||
offset++;
|
offset++;
|
||||||
|
|
||||||
@@ -106,8 +100,7 @@ namespace Claunia.PropertyList
|
|||||||
/// </param>
|
/// </param>
|
||||||
static int DetermineType(Stream fs, long offset = 0)
|
static int DetermineType(Stream fs, long offset = 0)
|
||||||
{
|
{
|
||||||
if(fs.Length == 0)
|
if(fs.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
long index = offset;
|
long index = offset;
|
||||||
long readLimit = index + 1024;
|
long readLimit = index + 1024;
|
||||||
@@ -129,13 +122,10 @@ namespace Claunia.PropertyList
|
|||||||
b = fs.ReadByte();
|
b = fs.ReadByte();
|
||||||
|
|
||||||
//Check if we are reading the Unicode byte order mark (BOM) and skip it
|
//Check if we are reading the Unicode byte order mark (BOM) and skip it
|
||||||
bom = index < 3 && ((index == 0 && b == 0xEF) ||
|
bom = index < 3 && (index == 0 && b == 0xEF || bom && (index == 1 && b == 0xBB || index == 2 && b == 0xBF));
|
||||||
(bom && ((index == 1 && b == 0xBB) || (index == 2 && b == 0xBF))));
|
} while(b != -1 && (b is ' ' or '\t' or '\r' or '\n' or '\f' || bom));
|
||||||
} while(b != -1 &&
|
|
||||||
(b is ' ' or '\t' or '\r' or '\n' or '\f' || bom));
|
|
||||||
|
|
||||||
if(b == -1)
|
if(b == -1) return TYPE_ERROR_BLANK;
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
byte[] magicBytes = new byte[8];
|
byte[] magicBytes = new byte[8];
|
||||||
magicBytes[0] = (byte)b;
|
magicBytes[0] = (byte)b;
|
||||||
@@ -186,12 +176,14 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
switch(DetermineType(bytes))
|
switch(DetermineType(bytes))
|
||||||
{
|
{
|
||||||
case TYPE_BINARY: return BinaryPropertyListParser.Parse(bytes);
|
case TYPE_BINARY:
|
||||||
case TYPE_XML: return XmlPropertyListParser.Parse(bytes);
|
return BinaryPropertyListParser.Parse(bytes);
|
||||||
case TYPE_ASCII: return ASCIIPropertyListParser.Parse(bytes);
|
case TYPE_XML:
|
||||||
|
return XmlPropertyListParser.Parse(bytes);
|
||||||
|
case TYPE_ASCII:
|
||||||
|
return ASCIIPropertyListParser.Parse(bytes);
|
||||||
default:
|
default:
|
||||||
throw new
|
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||||
PropertyListFormatException("The given data is not a property list of a supported format.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,12 +201,14 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
switch(DetermineType(bytes))
|
switch(DetermineType(bytes))
|
||||||
{
|
{
|
||||||
case TYPE_BINARY: return BinaryPropertyListParser.Parse(bytes);
|
case TYPE_BINARY:
|
||||||
case TYPE_XML: return XmlPropertyListParser.Parse(bytes.ToArray());
|
return BinaryPropertyListParser.Parse(bytes);
|
||||||
case TYPE_ASCII: return ASCIIPropertyListParser.Parse(bytes);
|
case TYPE_XML:
|
||||||
|
return XmlPropertyListParser.Parse(bytes.ToArray());
|
||||||
|
case TYPE_ASCII:
|
||||||
|
return ASCIIPropertyListParser.Parse(bytes);
|
||||||
default:
|
default:
|
||||||
throw new
|
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||||
PropertyListFormatException("The given data is not a property list of a supported format.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +225,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
// Use Create here -- to make sure that when the updated file is shorter than
|
// Use Create here -- to make sure that when the updated file is shorter than
|
||||||
// the original file, no "obsolete" data is left at the end.
|
// the original file, no "obsolete" data is left at the end.
|
||||||
@@ -269,8 +262,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
BinaryPropertyListWriter.Write(outFile, root);
|
BinaryPropertyListWriter.Write(outFile, root);
|
||||||
}
|
}
|
||||||
@@ -279,8 +271,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="root">The root object.</param>
|
/// <param name="root">The root object.</param>
|
||||||
/// <param name="outStream">The output stream.</param>
|
/// <param name="outStream">The output stream.</param>
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
public static void SaveAsBinary(NSObject root, Stream outStream) =>
|
public static void SaveAsBinary(NSObject root, Stream outStream) => BinaryPropertyListWriter.Write(outStream, root);
|
||||||
BinaryPropertyListWriter.Write(outStream, root);
|
|
||||||
|
|
||||||
/// <summary>Converts a given property list file into the OS X and iOS binary format.</summary>
|
/// <summary>Converts a given property list file into the OS X and iOS binary format.</summary>
|
||||||
/// <param name="inFile">The source file.</param>
|
/// <param name="inFile">The source file.</param>
|
||||||
@@ -299,8 +290,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
@@ -317,8 +307,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
@@ -339,9 +328,11 @@ namespace Claunia.PropertyList
|
|||||||
else if(root is NSArray array)
|
else if(root is NSArray array)
|
||||||
SaveAsASCII(array, outFile);
|
SaveAsASCII(array, outFile);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
throw new PropertyListFormatException("The root of the given input property list " +
|
throw new PropertyListFormatException("The root of the given input property list " +
|
||||||
"is neither a Dictionary nor an Array!");
|
"is neither a Dictionary nor an Array!");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root into a GnuStep ASCII file.</summary>
|
/// <summary>Saves a property list with the given object as root into a GnuStep ASCII file.</summary>
|
||||||
/// <param name="root">The root object.</param>
|
/// <param name="root">The root object.</param>
|
||||||
@@ -351,8 +342,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
@@ -369,8 +359,7 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
@@ -401,5 +390,4 @@ namespace Claunia.PropertyList
|
|||||||
"is neither a Dictionary nor an Array!");
|
"is neither a Dictionary nor an Array!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,23 +27,20 @@ using System;
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>An UID. Only found in binary property lists that are keyed archives.</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public class UID : NSObject
|
||||||
{
|
{
|
||||||
/// <summary>An UID. Only found in binary property lists that are keyed archives.</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class UID : NSObject
|
|
||||||
{
|
|
||||||
readonly ulong value;
|
readonly ulong value;
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class.</summary>
|
||||||
/// <param name="bytes">Bytes.</param>
|
/// <param name="bytes">Bytes.</param>
|
||||||
public UID(ReadOnlySpan<byte> bytes)
|
public UID(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
if(bytes.Length != 1 &&
|
if(bytes.Length != 1 && bytes.Length != 2 && bytes.Length != 4 && bytes.Length != 8)
|
||||||
bytes.Length != 2 &&
|
|
||||||
bytes.Length != 4 &&
|
|
||||||
bytes.Length != 8)
|
|
||||||
throw new ArgumentException("Type argument is not valid.");
|
throw new ArgumentException("Type argument is not valid.");
|
||||||
|
|
||||||
value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
|
value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
|
||||||
@@ -126,7 +123,8 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: throw new InvalidOperationException();
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +167,7 @@ namespace Claunia.PropertyList
|
|||||||
Span<byte> bytes = stackalloc byte[ByteCount];
|
Span<byte> bytes = stackalloc byte[ByteCount];
|
||||||
GetBytes(bytes);
|
GetBytes(bytes);
|
||||||
|
|
||||||
foreach(byte b in bytes)
|
foreach(byte b in bytes) ascii.Append($"{b:x2}");
|
||||||
ascii.Append($"{b:x2}");
|
|
||||||
|
|
||||||
ascii.Append("\"");
|
ascii.Append("\"");
|
||||||
}
|
}
|
||||||
@@ -194,8 +191,7 @@ namespace Claunia.PropertyList
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if(obj is not UID uid)
|
if(obj is not UID uid) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
return uid.value == value;
|
return uid.value == value;
|
||||||
}
|
}
|
||||||
@@ -209,5 +205,4 @@ namespace Claunia.PropertyList
|
|||||||
/// <summary>Gets a <see cref="ulong" /> which represents this <see cref="UID" />.</summary>
|
/// <summary>Gets a <see cref="ulong" /> which represents this <see cref="UID" />.</summary>
|
||||||
/// <returns>A <see cref="ulong" /> which represents this <see cref="UID" />.</returns>
|
/// <returns>A <see cref="ulong" /> which represents this <see cref="UID" />.</returns>
|
||||||
public ulong ToUInt64() => value;
|
public ulong ToUInt64() => value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,53 +1,83 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows you to override the default value class initialization for the values found
|
||||||
|
/// in the parsed plists by registering your own preprocessing implementations.
|
||||||
|
/// </summary>
|
||||||
|
public static class ValuePreprocessor
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Allows you to override the default value class initialization for the values found
|
|
||||||
/// in the parsed plists by registering your own preprocessing implementations.
|
|
||||||
/// </summary>
|
|
||||||
public static class ValuePreprocessor
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the semantic type of content the preprocessor will work on--independent
|
/// Indicates the semantic type of content the preprocessor will work on--independent
|
||||||
/// from the underlying data type (which will be string in most cases anyway).
|
/// from the underlying data type (which will be string in most cases anyway).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
BOOL, INTEGER, FLOATING_POINT,
|
BOOL,
|
||||||
UNDEFINED_NUMBER, STRING, DATA,
|
INTEGER,
|
||||||
|
FLOATING_POINT,
|
||||||
|
UNDEFINED_NUMBER,
|
||||||
|
STRING,
|
||||||
|
DATA,
|
||||||
DATE
|
DATE
|
||||||
};
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A null-implementation of a preprocessor for registered, but passive, use cases.
|
|
||||||
/// </summary>
|
|
||||||
private static T NullPreprocessor<T>(T value) => value;
|
|
||||||
|
|
||||||
private record struct TypeIdentifier(Type ValueType, System.Type DataType);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default preprocessors for all the standard cases.
|
/// Default preprocessors for all the standard cases.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Dictionary<TypeIdentifier, Delegate> _preprocessors = new()
|
private static readonly Dictionary<TypeIdentifier, Delegate> _preprocessors = new()
|
||||||
{
|
{
|
||||||
{ new TypeIdentifier(Type.BOOL, typeof(bool)), NullPreprocessor<bool> },
|
{
|
||||||
{ new TypeIdentifier(Type.BOOL, typeof(string)), NullPreprocessor<string> },
|
new TypeIdentifier(Type.BOOL, typeof(bool)), NullPreprocessor<bool>
|
||||||
{ new TypeIdentifier(Type.INTEGER, typeof(string)), NullPreprocessor<string> },
|
},
|
||||||
{ new TypeIdentifier(Type.INTEGER, typeof(byte[])), NullPreprocessor<byte[]> },
|
{
|
||||||
{ new TypeIdentifier(Type.FLOATING_POINT, typeof(string)), NullPreprocessor<string> },
|
new TypeIdentifier(Type.BOOL, typeof(string)), NullPreprocessor<string>
|
||||||
{ new TypeIdentifier(Type.FLOATING_POINT, typeof(byte[])), NullPreprocessor<byte[]> },
|
},
|
||||||
{ new TypeIdentifier(Type.UNDEFINED_NUMBER, typeof(string)), NullPreprocessor<string> },
|
{
|
||||||
{ new TypeIdentifier(Type.STRING, typeof(string)), NullPreprocessor<string> },
|
new TypeIdentifier(Type.INTEGER, typeof(string)), NullPreprocessor<string>
|
||||||
{ new TypeIdentifier(Type.STRING, typeof(byte[])), NullPreprocessor<byte[]> },
|
},
|
||||||
{ new TypeIdentifier(Type.DATA, typeof(string)), NullPreprocessor<string> },
|
{
|
||||||
{ new TypeIdentifier(Type.DATA, typeof(byte[])), NullPreprocessor<byte[]> },
|
new TypeIdentifier(Type.INTEGER, typeof(byte[])), NullPreprocessor<byte[]>
|
||||||
{ new TypeIdentifier(Type.DATE, typeof(string)), NullPreprocessor<string> },
|
},
|
||||||
{ new TypeIdentifier(Type.DATE, typeof(double)), NullPreprocessor<double> },
|
{
|
||||||
{ new TypeIdentifier(Type.DATE, typeof(byte[])), NullPreprocessor<byte[]> },
|
new TypeIdentifier(Type.FLOATING_POINT, typeof(string)), NullPreprocessor<string>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.FLOATING_POINT, typeof(byte[])), NullPreprocessor<byte[]>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.UNDEFINED_NUMBER, typeof(string)), NullPreprocessor<string>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.STRING, typeof(string)), NullPreprocessor<string>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.STRING, typeof(byte[])), NullPreprocessor<byte[]>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.DATA, typeof(string)), NullPreprocessor<string>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.DATA, typeof(byte[])), NullPreprocessor<byte[]>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.DATE, typeof(string)), NullPreprocessor<string>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.DATE, typeof(double)), NullPreprocessor<double>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new TypeIdentifier(Type.DATE, typeof(byte[])), NullPreprocessor<byte[]>
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A null-implementation of a preprocessor for registered, but passive, use cases.
|
||||||
|
/// </summary>
|
||||||
|
private static T NullPreprocessor<T>(T value) => value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a default preprocessor.
|
/// Get a default preprocessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -57,7 +87,7 @@ namespace Claunia.PropertyList
|
|||||||
/// Set up a custom preprocessor.
|
/// Set up a custom preprocessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Set<T>(Func<T, T> preprocessor, Type type) =>
|
public static void Set<T>(Func<T, T> preprocessor, Type type) =>
|
||||||
_preprocessors[new(type, typeof(T))] = preprocessor;
|
_preprocessors[new TypeIdentifier(type, typeof(T))] = preprocessor;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,24 +95,29 @@ namespace Claunia.PropertyList
|
|||||||
/// to prevent argument errors.
|
/// to prevent argument errors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
||||||
public static void Unset<T>(Type type) =>
|
public static void Unset<T>(Type type) => _preprocessors[GetValidTypeIdentifier<T>(type)] = NullPreprocessor<T>;
|
||||||
_preprocessors[GetValidTypeIdentifier<T>(type)] = NullPreprocessor<T>;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Completely unregister a specific preprocessor--remove it instead of
|
/// Completely unregister a specific preprocessor--remove it instead of
|
||||||
/// replacing it with a null-implementation.
|
/// replacing it with a null-implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was registered.</exception>
|
/// <exception cref="ArgumentException">
|
||||||
public static void Remove<T>(Type type) =>
|
/// If no appropriate preprocessor--not even a default null-implementation--was
|
||||||
_preprocessors.Remove(GetValidTypeIdentifier<T>(type));
|
/// registered.
|
||||||
|
/// </exception>
|
||||||
|
public static void Remove<T>(Type type) => _preprocessors.Remove(GetValidTypeIdentifier<T>(type));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Preprocess the supplied data using the appropriate registered implementation.
|
/// Preprocess the supplied data using the appropriate registered implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was registered.</exception>
|
/// <exception cref="ArgumentException">
|
||||||
|
/// If no appropriate preprocessor--not even a default null-implementation--was
|
||||||
|
/// registered.
|
||||||
|
/// </exception>
|
||||||
public static T Preprocess<T>(T value, Type type) => TryGetPreprocessor(type, out Func<T, T> preprocess)
|
public static T Preprocess<T>(T value, Type type) => TryGetPreprocessor(type, out Func<T, T> preprocess)
|
||||||
? preprocess(value)
|
? preprocess(value)
|
||||||
: throw new ArgumentException($"Failed to find a preprocessor for value '{value}'.");
|
: throw new
|
||||||
|
ArgumentException($"Failed to find a preprocessor for value '{value}'.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the appropriate registered implementation--or null--and casts it back to
|
/// Gets the appropriate registered implementation--or null--and casts it back to
|
||||||
@@ -97,7 +132,7 @@ namespace Claunia.PropertyList
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess = default;
|
preprocess = default(Func<T, T>);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -111,11 +146,10 @@ namespace Claunia.PropertyList
|
|||||||
var identifier = new TypeIdentifier(type, typeof(T));
|
var identifier = new TypeIdentifier(type, typeof(T));
|
||||||
|
|
||||||
if(!_preprocessors.ContainsKey(identifier))
|
if(!_preprocessors.ContainsKey(identifier))
|
||||||
{
|
throw new ArgumentException("Failed to find a valid preprocessor type identifier.");
|
||||||
throw new ArgumentException($"Failed to find a valid preprocessor type identifier.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private record struct TypeIdentifier(Type ValueType, System.Type DataType);
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,13 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
|
|
||||||
|
/// <summary>Parses XML property lists.</summary>
|
||||||
|
/// @author Daniel Dreibrodt
|
||||||
|
/// @author Natalia Portillo
|
||||||
|
public static class XmlPropertyListParser
|
||||||
{
|
{
|
||||||
/// <summary>Parses XML property lists.</summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public static class XmlPropertyListParser
|
|
||||||
{
|
|
||||||
/// <summary>Parses a XML property list file.</summary>
|
/// <summary>Parses a XML property list file.</summary>
|
||||||
/// <param name="f">The XML property list file.</param>
|
/// <param name="f">The XML property list file.</param>
|
||||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
@@ -48,8 +48,12 @@ namespace Claunia.PropertyList
|
|||||||
};
|
};
|
||||||
|
|
||||||
using(Stream stream = f.OpenRead())
|
using(Stream stream = f.OpenRead())
|
||||||
|
{
|
||||||
using(var reader = XmlReader.Create(stream, settings))
|
using(var reader = XmlReader.Create(stream, settings))
|
||||||
|
{
|
||||||
doc.Load(reader);
|
doc.Load(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ParseDocument(doc);
|
return ParseDocument(doc);
|
||||||
}
|
}
|
||||||
@@ -71,11 +75,15 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
var doc = new XmlDocument();
|
var doc = new XmlDocument();
|
||||||
|
|
||||||
var settings = new XmlReaderSettings();
|
var settings = new XmlReaderSettings
|
||||||
settings.DtdProcessing = DtdProcessing.Ignore;
|
{
|
||||||
|
DtdProcessing = DtdProcessing.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
using(var reader = XmlReader.Create(str, settings))
|
using(var reader = XmlReader.Create(str, settings))
|
||||||
|
{
|
||||||
doc.Load(reader);
|
doc.Load(reader);
|
||||||
|
}
|
||||||
|
|
||||||
return ParseDocument(doc);
|
return ParseDocument(doc);
|
||||||
}
|
}
|
||||||
@@ -87,8 +95,10 @@ namespace Claunia.PropertyList
|
|||||||
{
|
{
|
||||||
var doc = new XmlDocument();
|
var doc = new XmlDocument();
|
||||||
|
|
||||||
var settings = new XmlReaderSettings();
|
var settings = new XmlReaderSettings
|
||||||
settings.DtdProcessing = DtdProcessing.Ignore;
|
{
|
||||||
|
DtdProcessing = DtdProcessing.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
doc.LoadXml(value);
|
doc.LoadXml(value);
|
||||||
|
|
||||||
@@ -100,17 +110,14 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="doc">The XML document.</param>
|
/// <param name="doc">The XML document.</param>
|
||||||
static NSObject ParseDocument(XmlDocument doc)
|
static NSObject ParseDocument(XmlDocument doc)
|
||||||
{
|
{
|
||||||
XmlNode docType = doc.ChildNodes.OfType<XmlNode>().
|
XmlNode docType = doc.ChildNodes.OfType<XmlNode>().SingleOrDefault(n => n.NodeType == XmlNodeType.DocumentType);
|
||||||
SingleOrDefault(n => n.NodeType == XmlNodeType.DocumentType);
|
|
||||||
|
|
||||||
if(docType == null)
|
if(docType == null)
|
||||||
{
|
{
|
||||||
if(doc.DocumentElement != null &&
|
if(doc.DocumentElement != null && !doc.DocumentElement.Name.Equals("plist"))
|
||||||
!doc.DocumentElement.Name.Equals("plist"))
|
|
||||||
throw new XmlException("The given XML document is not a property list.");
|
throw new XmlException("The given XML document is not a property list.");
|
||||||
}
|
}
|
||||||
else if(!docType.Name.Equals("plist"))
|
else if(!docType.Name.Equals("plist")) throw new XmlException("The given XML document is not a property list.");
|
||||||
throw new XmlException("The given XML document is not a property list.");
|
|
||||||
|
|
||||||
XmlNode rootNode;
|
XmlNode rootNode;
|
||||||
|
|
||||||
@@ -121,7 +128,8 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
rootNode = rootNodes.Count switch
|
rootNode = rootNodes.Count switch
|
||||||
{
|
{
|
||||||
0 => throw new PropertyListFormatException("The given XML property list has no root element!"),
|
0 => throw new
|
||||||
|
PropertyListFormatException("The given XML property list has no root element!"),
|
||||||
1 => rootNodes[0],
|
1 => rootNodes[0],
|
||||||
_ => throw new
|
_ => throw new
|
||||||
PropertyListFormatException("The given XML property list has more than one root element!")
|
PropertyListFormatException("The given XML property list has more than one root element!")
|
||||||
@@ -143,9 +151,12 @@ namespace Claunia.PropertyList
|
|||||||
switch(n.Name)
|
switch(n.Name)
|
||||||
{
|
{
|
||||||
// Special case for UID values
|
// Special case for UID values
|
||||||
case "dict" when n.ChildNodes.Count == 2 && n.ChildNodes[0].Name == "key" &&
|
case "dict" when n.ChildNodes.Count == 2 &&
|
||||||
n.ChildNodes[0].InnerText == "CF$UID" && n.ChildNodes[1].Name == "integer" &&
|
n.ChildNodes[0].Name == "key" &&
|
||||||
uint.TryParse(n.ChildNodes[1].InnerText, out uint uidValue): return new UID(uidValue);
|
n.ChildNodes[0].InnerText == "CF$UID" &&
|
||||||
|
n.ChildNodes[1].Name == "integer" &&
|
||||||
|
uint.TryParse(n.ChildNodes[1].InnerText, out uint uidValue):
|
||||||
|
return new UID(uidValue);
|
||||||
case "dict":
|
case "dict":
|
||||||
{
|
{
|
||||||
var dict = new NSDictionary();
|
var dict = new NSDictionary();
|
||||||
@@ -168,18 +179,32 @@ namespace Claunia.PropertyList
|
|||||||
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
||||||
var array = new NSArray(children.Count);
|
var array = new NSArray(children.Count);
|
||||||
|
|
||||||
for(int i = 0; i < children.Count; i++)
|
for(int i = 0; i < children.Count; i++) array.Add(ParseObject(children[i]));
|
||||||
array.Add(ParseObject(children[i]));
|
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
case "true": return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
case "true":
|
||||||
case "false": return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
case "integer": return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.INTEGER), NSNumber.INTEGER);
|
case "false":
|
||||||
case "real": return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.FLOATING_POINT), NSNumber.REAL);
|
return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
case "string": return new NSString(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.STRING));
|
case "integer":
|
||||||
case "data": return new NSData(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.DATA));
|
return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n),
|
||||||
default: return n.Name.Equals("date") ? new NSDate(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.DATE)) : null;
|
ValuePreprocessor.Type.INTEGER),
|
||||||
|
NSNumber.INTEGER);
|
||||||
|
case "real":
|
||||||
|
return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n),
|
||||||
|
ValuePreprocessor.Type.FLOATING_POINT),
|
||||||
|
NSNumber.REAL);
|
||||||
|
case "string":
|
||||||
|
return new NSString(ValuePreprocessor.Preprocess(GetNodeTextContents(n),
|
||||||
|
ValuePreprocessor.Type.STRING));
|
||||||
|
case "data":
|
||||||
|
return new NSData(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.DATA));
|
||||||
|
default:
|
||||||
|
return n.Name.Equals("date")
|
||||||
|
? new NSDate(ValuePreprocessor.Preprocess(GetNodeTextContents(n),
|
||||||
|
ValuePreprocessor.Type.DATE))
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,11 +213,10 @@ namespace Claunia.PropertyList
|
|||||||
/// <param name="list">The list of nodes to search.</param>
|
/// <param name="list">The list of nodes to search.</param>
|
||||||
static List<XmlNode> FilterElementNodes(XmlNodeList list)
|
static List<XmlNode> FilterElementNodes(XmlNodeList list)
|
||||||
{
|
{
|
||||||
List<XmlNode> result = new();
|
List<XmlNode> result = [];
|
||||||
|
|
||||||
foreach(XmlNode child in list)
|
foreach(XmlNode child in list)
|
||||||
if(child.NodeType == XmlNodeType.Element)
|
if(child.NodeType == XmlNodeType.Element) result.Add(child);
|
||||||
result.Add(child);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -212,22 +236,22 @@ namespace Claunia.PropertyList
|
|||||||
return content ?? "";
|
return content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!n.HasChildNodes)
|
if(!n.HasChildNodes) return "";
|
||||||
return "";
|
|
||||||
|
|
||||||
XmlNodeList children = n.ChildNodes;
|
XmlNodeList children = n.ChildNodes;
|
||||||
|
|
||||||
foreach(XmlNode child in children)
|
foreach(XmlNode child in children)
|
||||||
|
|
||||||
//Skip any non-text nodes, like comments or entities
|
//Skip any non-text nodes, like comments or entities
|
||||||
|
{
|
||||||
if(child.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
if(child.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
||||||
{
|
{
|
||||||
string content = child.Value; //This concatenates any adjacent text/cdata/entity nodes
|
string content = child.Value; //This concatenates any adjacent text/cdata/entity nodes
|
||||||
|
|
||||||
return content ?? "";
|
return content ?? "";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
|
||||||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" PrivateAssets="all" />
|
<PackageReference Include="Nerdbank.GitVersioning" Version="3.7.115" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net462'">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net462'">
|
||||||
<PackageReference Include="System.Memory" Version="4.6.0" />
|
<PackageReference Include="System.Memory" Version="4.6.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<string>Lot&s of &persand&s and other escapable "'<>€ characters</string>
|
<string>Lot&s of &persand&s and other escapable "'<>€ characters</string>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>emojiString</key>
|
<key>emojiString</key>
|
||||||
<string>Test Test, 😰❔👍👎🔥</string>
|
<string>Test Test, 😰❔👍👎🔥</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<array>
|
<array>
|
||||||
<dict/>
|
<dict/>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Device Name</key>
|
<key>Device Name</key>
|
||||||
<string>Kid’s iPhone</string>
|
<string>Kid’s iPhone</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,5 +1 @@
|
|||||||
// !$*UTF8*$!
|
// !$*UTF8*$!{path = "JÔÖú@2x.jpg";"Key QÔÖª@2x \u4321" = "QÔÖú@2x 啕.jpg";}
|
||||||
{
|
|
||||||
path = "JÔÖú@2x.jpg";
|
|
||||||
"Key QÔÖª@2x \u4321" = "QÔÖú@2x 啕.jpg";
|
|
||||||
}
|
|
||||||
@@ -1,5 +1 @@
|
|||||||
{
|
{"key&\102"="value&\U0042==";key2 = "strangestring\\\"";key3 = "strangestring\\";}
|
||||||
"key&\102"="value&\U0042==";
|
|
||||||
key2 = "strangestring\\\"";
|
|
||||||
key3 = "strangestring\\";
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,2 @@
|
|||||||
{
|
{ keyA = valueA; "key&\102" = "value&\U0042"; date =
|
||||||
keyA = valueA;
|
<*D2011-11-28 09:21:30 +0000>; data = <00000004 10410820 82>; array = ( <*BY>, <*BN>, <*I87>, <*R3.14159> );}
|
||||||
"key&\102" = "value&\U0042";
|
|
||||||
date = <*D2011-11-28 09:21:30 +0000>;
|
|
||||||
data = <00000004 10410820 82>;
|
|
||||||
array = (
|
|
||||||
<*BY>,
|
|
||||||
<*BN>,
|
|
||||||
<*I87>,
|
|
||||||
<*R3.14159>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,2 @@
|
|||||||
{
|
{ keyA = valueA; "key&\102" = "value&\U0042"; date = "2011-11-28T09:21:30Z"; data =
|
||||||
keyA = valueA;
|
<00000004 10410820 82>; array = ( YES, NO, 87, 3.14159 );}
|
||||||
"key&\102" = "value&\U0042";
|
|
||||||
date = "2011-11-28T09:21:30Z";
|
|
||||||
data = <00000004 10410820 82>;
|
|
||||||
array = (
|
|
||||||
YES,
|
|
||||||
NO,
|
|
||||||
87,
|
|
||||||
3.14159
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>keyA</key>
|
<key>keyA</key>
|
||||||
<string>valueA</string>
|
<string>valueA</string>
|
||||||
<key>key&B</key>
|
<key>key&B</key>
|
||||||
@@ -17,5 +17,5 @@
|
|||||||
<integer>87</integer>
|
<integer>87</integer>
|
||||||
<real>3.14159</real>
|
<real>3.14159</real>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>number</key>
|
<key>number</key>
|
||||||
<integer>-1234</integer>
|
<integer>-1234</integer>
|
||||||
<key>number2</key>
|
<key>number2</key>
|
||||||
@@ -14,5 +14,5 @@
|
|||||||
<real>2.352535353543534e+19</real>
|
<real>2.352535353543534e+19</real>
|
||||||
<key>number6</key>
|
<key>number6</key>
|
||||||
<real>-999992312312312.2</real>
|
<real>-999992312312312.2</real>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Reference in New Issue
Block a user