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,22 +2,22 @@
|
|||||||
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]
|
byte[] data;
|
||||||
public class BinaryPropertyListParserBenchmarks
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void Setup() => data = File.ReadAllBytes("plist.bin");
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public NSObject ReadLargePropertylistTest()
|
||||||
{
|
{
|
||||||
byte[] data;
|
NSObject nsObject = PropertyListParser.Parse(data);
|
||||||
|
|
||||||
[GlobalSetup]
|
return nsObject;
|
||||||
public void Setup() => data = File.ReadAllBytes("plist.bin");
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public NSObject ReadLargePropertylistTest()
|
|
||||||
{
|
|
||||||
NSObject nsObject = PropertyListParser.Parse(data);
|
|
||||||
|
|
||||||
return nsObject;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
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]
|
NSObject data;
|
||||||
public class BinaryPropertyListWriterBenchmarks
|
|
||||||
{
|
|
||||||
NSObject data;
|
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void Setup() => data = PropertyListParser.Parse("plist.bin");
|
public void Setup() => data = PropertyListParser.Parse("plist.bin");
|
||||||
|
|
||||||
[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<BinaryPropertyListWriterBenchmarks>();
|
||||||
BenchmarkRunner.Run<BinaryPropertyListParserBenchmarks>();
|
|
||||||
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
|
{
|
||||||
}, 7), InlineData(new byte[]
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07
|
||||||
{
|
},
|
||||||
0x00, 0x0e, 0x47, 0x7b
|
7)]
|
||||||
}, 0x00000000000e477b)]
|
[InlineData(new byte[]
|
||||||
public void ParseUnsignedIntTest(byte[] binaryValue, int expectedValue) =>
|
{
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseUnsignedInt(binaryValue));
|
0x00, 0x0e, 0x47, 0x7b
|
||||||
|
},
|
||||||
|
0x00000000000e477b)]
|
||||||
|
public void ParseUnsignedIntTest(byte[] binaryValue, int expectedValue) =>
|
||||||
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseUnsignedInt(binaryValue));
|
||||||
|
|
||||||
[Theory, InlineData(new byte[]
|
[Theory]
|
||||||
{
|
[InlineData(new byte[]
|
||||||
0x57
|
{
|
||||||
}, 0x57), InlineData(new byte[]
|
0x57
|
||||||
{
|
},
|
||||||
0x12, 0x34
|
0x57)]
|
||||||
}, 0x1234), InlineData(new byte[]
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x12, 0x34, 0x56
|
0x12, 0x34
|
||||||
}, 0x123456), InlineData(new byte[]
|
},
|
||||||
{
|
0x1234)]
|
||||||
0x40, 0x2d, 0xf8, 0x4d
|
[InlineData(new byte[]
|
||||||
}, 0x402df84d), InlineData(new byte[]
|
{
|
||||||
{
|
0x12, 0x34, 0x56
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a
|
},
|
||||||
}, 0x123456789a), InlineData(new byte[]
|
0x123456)]
|
||||||
{
|
[InlineData(new byte[]
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc
|
{
|
||||||
}, 0x123456789abc), InlineData(new byte[]
|
0x40, 0x2d, 0xf8, 0x4d
|
||||||
{
|
},
|
||||||
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde
|
0x402df84d)]
|
||||||
}, 0x123456789abcde), InlineData(new byte[]
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
0x12, 0x34, 0x56, 0x78, 0x9a
|
||||||
}, 0x41b483982a000000), InlineData(new byte[]
|
},
|
||||||
{
|
0x123456789a)]
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
[InlineData(new byte[]
|
||||||
}, unchecked((long)0xfffffffffffffc19)), InlineData(new byte[]
|
{
|
||||||
{
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
},
|
||||||
}, unchecked((long)0xfffffffffffffc19))]
|
0x123456789abc)]
|
||||||
public void ParseLongTest(byte[] binaryValue, long expectedValue) =>
|
[InlineData(new byte[]
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseLong(binaryValue));
|
{
|
||||||
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde
|
||||||
|
},
|
||||||
|
0x123456789abcde)]
|
||||||
|
[InlineData(new byte[]
|
||||||
|
{
|
||||||
|
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
||||||
|
},
|
||||||
|
0x41b483982a000000)]
|
||||||
|
[InlineData(new byte[]
|
||||||
|
{
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
||||||
|
},
|
||||||
|
unchecked((long)0xfffffffffffffc19))]
|
||||||
|
[InlineData(new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x19
|
||||||
|
},
|
||||||
|
unchecked((long)0xfffffffffffffc19))]
|
||||||
|
public void ParseLongTest(byte[] binaryValue, long expectedValue) =>
|
||||||
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseLong(binaryValue));
|
||||||
|
|
||||||
[Theory, InlineData(new byte[]
|
[Theory]
|
||||||
{
|
[InlineData(new byte[]
|
||||||
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
{
|
||||||
}, 344168490), InlineData(new byte[]
|
0x41, 0xb4, 0x83, 0x98, 0x2a, 0x00, 0x00, 0x00
|
||||||
{
|
},
|
||||||
0x40, 0x09, 0x21, 0xf9, 0xf0, 0x1b, 0x86, 0x6e
|
344168490)]
|
||||||
}, 3.14159), InlineData(new byte[]
|
[InlineData(new byte[]
|
||||||
{
|
{
|
||||||
0x40, 0x2d, 0xf8, 0x4d
|
0x40, 0x09, 0x21, 0xf9, 0xf0, 0x1b, 0x86, 0x6e
|
||||||
}, 2.71828007698059)]
|
},
|
||||||
public void ParseDoubleTest(byte[] binaryValue, double expectedValue) =>
|
3.14159)]
|
||||||
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseDouble(binaryValue), 14);
|
[InlineData(new byte[]
|
||||||
}
|
{
|
||||||
|
0x40, 0x2d, 0xf8, 0x4d
|
||||||
|
},
|
||||||
|
2.71828007698059)]
|
||||||
|
public void ParseDoubleTest(byte[] binaryValue, double expectedValue) =>
|
||||||
|
Assert.Equal(expectedValue, BinaryPropertyListParser.ParseDouble(binaryValue), 14);
|
||||||
}
|
}
|
||||||
@@ -2,85 +2,87 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class BinaryPropertyListWriterTests
|
||||||
{
|
{
|
||||||
public class BinaryPropertyListWriterTests
|
[Fact]
|
||||||
|
public void Roundtrip2Test()
|
||||||
{
|
{
|
||||||
[Fact]
|
byte[] data = File.ReadAllBytes("test-files/plist2.bin");
|
||||||
public void Roundtrip2Test()
|
NSObject root = PropertyListParser.Parse(data);
|
||||||
|
|
||||||
|
using var actualOutput = new MemoryStream();
|
||||||
|
|
||||||
|
using Stream expectedOutput = File.OpenRead("test-files/plist2.bin");
|
||||||
|
|
||||||
|
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
||||||
|
|
||||||
|
var writer = new BinaryPropertyListWriter(validatingStream)
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes("test-files/plist2.bin");
|
ReuseObjectIds = false
|
||||||
NSObject root = PropertyListParser.Parse(data);
|
};
|
||||||
|
|
||||||
using var actualOutput = new MemoryStream();
|
writer.Write(root);
|
||||||
|
}
|
||||||
|
|
||||||
using Stream expectedOutput = File.OpenRead("test-files/plist2.bin");
|
[Fact]
|
||||||
|
public void Roundtrip3Test()
|
||||||
|
{
|
||||||
|
byte[] data = File.ReadAllBytes("test-files/plist3.bin");
|
||||||
|
NSObject root = PropertyListParser.Parse(data);
|
||||||
|
|
||||||
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
using var actualOutput = new MemoryStream();
|
||||||
|
|
||||||
var writer = new BinaryPropertyListWriter(validatingStream)
|
using Stream expectedOutput = File.OpenRead("test-files/plist3.bin");
|
||||||
{
|
|
||||||
ReuseObjectIds = false
|
|
||||||
};
|
|
||||||
|
|
||||||
writer.Write(root);
|
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
var writer = new BinaryPropertyListWriter(validatingStream)
|
||||||
public void Roundtrip3Test()
|
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes("test-files/plist3.bin");
|
ReuseObjectIds = false
|
||||||
NSObject root = PropertyListParser.Parse(data);
|
};
|
||||||
|
|
||||||
using var actualOutput = new MemoryStream();
|
writer.Write(root);
|
||||||
|
}
|
||||||
|
|
||||||
using Stream expectedOutput = File.OpenRead("test-files/plist3.bin");
|
[Fact]
|
||||||
|
public void Roundtrip4Test()
|
||||||
|
{
|
||||||
|
byte[] data = File.ReadAllBytes("test-files/plist4.bin");
|
||||||
|
NSObject root = PropertyListParser.Parse(data);
|
||||||
|
|
||||||
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
using var actualOutput = new MemoryStream();
|
||||||
|
|
||||||
var writer = new BinaryPropertyListWriter(validatingStream);
|
using Stream expectedOutput = File.OpenRead("test-files/plist4.bin");
|
||||||
writer.ReuseObjectIds = false;
|
|
||||||
writer.Write(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
||||||
public void Roundtrip4Test()
|
|
||||||
|
var writer = new BinaryPropertyListWriter(validatingStream)
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes("test-files/plist4.bin");
|
ReuseObjectIds = false
|
||||||
NSObject root = PropertyListParser.Parse(data);
|
};
|
||||||
|
|
||||||
using var actualOutput = new MemoryStream();
|
writer.Write(root);
|
||||||
|
}
|
||||||
|
|
||||||
using Stream expectedOutput = File.OpenRead("test-files/plist4.bin");
|
[Fact]
|
||||||
|
public void RoundtripTest()
|
||||||
|
{
|
||||||
|
byte[] data = File.ReadAllBytes("test-files/plist.bin");
|
||||||
|
NSObject root = PropertyListParser.Parse(data);
|
||||||
|
|
||||||
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
using var actualOutput = new MemoryStream();
|
||||||
|
|
||||||
var writer = new BinaryPropertyListWriter(validatingStream)
|
using Stream expectedOutput = File.OpenRead("test-files/plist.bin");
|
||||||
{
|
|
||||||
ReuseObjectIds = false
|
|
||||||
};
|
|
||||||
|
|
||||||
writer.Write(root);
|
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
var writer = new BinaryPropertyListWriter(validatingStream)
|
||||||
public void RoundtripTest()
|
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes("test-files/plist.bin");
|
ReuseObjectIds = false
|
||||||
NSObject root = PropertyListParser.Parse(data);
|
};
|
||||||
|
|
||||||
using var actualOutput = new MemoryStream();
|
writer.Write(root);
|
||||||
|
|
||||||
using Stream expectedOutput = File.OpenRead("test-files/plist.bin");
|
|
||||||
|
|
||||||
using var validatingStream = new ValidatingStream(actualOutput, expectedOutput);
|
|
||||||
|
|
||||||
var writer = new BinaryPropertyListWriter(validatingStream)
|
|
||||||
{
|
|
||||||
ReuseObjectIds = false
|
|
||||||
};
|
|
||||||
|
|
||||||
writer.Write(root);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,166 +27,165 @@ 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>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public static void RoundtripDataTest()
|
||||||
{
|
{
|
||||||
/// <summary>
|
string expected = File.ReadAllText(@"test-files/RoundtripBinary.plist");
|
||||||
/// Makes sure that binary data is line-wrapped correctly when being serialized, in a scenario where the binary
|
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripBinary.plist"));
|
||||||
/// data is not indented (no leading whitespace).
|
string actual = value.ToXmlPropertyList();
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public static void RoundtripDataTest()
|
|
||||||
{
|
|
||||||
string expected = File.ReadAllText(@"test-files/RoundtripBinary.plist");
|
|
||||||
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripBinary.plist"));
|
|
||||||
string actual = value.ToXmlPropertyList();
|
|
||||||
|
|
||||||
Assert.Equal(expected, actual, ignoreLineEndingDifferences: true);
|
Assert.Equal(expected, actual, ignoreLineEndingDifferences: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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 indented.
|
/// data is indented.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void RoundtripDataTest2()
|
public static void RoundtripDataTest2()
|
||||||
{
|
{
|
||||||
string expected = File.ReadAllText(@"test-files/RoundtripBinaryIndentation.plist");
|
string expected = File.ReadAllText(@"test-files/RoundtripBinaryIndentation.plist");
|
||||||
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripBinaryIndentation.plist"));
|
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripBinaryIndentation.plist"));
|
||||||
string actual = value.ToXmlPropertyList();
|
string actual = value.ToXmlPropertyList();
|
||||||
|
|
||||||
Assert.Equal(expected, actual, ignoreLineEndingDifferences: true);
|
Assert.Equal(expected, actual, ignoreLineEndingDifferences: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void RoundtripRealTest()
|
public static void RoundtripRealTest()
|
||||||
{
|
{
|
||||||
string expected = File.ReadAllText(@"test-files/RoundtripReal.plist");
|
string expected = File.ReadAllText(@"test-files/RoundtripReal.plist");
|
||||||
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripReal.plist"));
|
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/RoundtripReal.plist"));
|
||||||
string actual = value.ToXmlPropertyList();
|
string actual = value.ToXmlPropertyList();
|
||||||
|
|
||||||
Assert.Equal(expected, actual, false, true);
|
Assert.Equal(expected, actual, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void RoundtripTest()
|
public static void RoundtripTest()
|
||||||
{
|
{
|
||||||
string expected = File.ReadAllText(@"test-files/Roundtrip.plist");
|
string expected = File.ReadAllText(@"test-files/Roundtrip.plist");
|
||||||
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/Roundtrip.plist"));
|
NSObject value = XmlPropertyListParser.Parse(new FileInfo(@"test-files/Roundtrip.plist"));
|
||||||
string actual = value.ToXmlPropertyList();
|
string actual = value.ToXmlPropertyList();
|
||||||
|
|
||||||
Assert.Equal(expected, actual, false, true);
|
Assert.Equal(expected, actual, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue16()
|
public static void TestIssue16()
|
||||||
{
|
{
|
||||||
float x = ((NSNumber)PropertyListParser.Parse(new FileInfo("test-files/issue16.plist"))).floatValue();
|
float x = ((NSNumber)PropertyListParser.Parse(new FileInfo("test-files/issue16.plist"))).floatValue();
|
||||||
Assert.True(x == (float)2.71828);
|
Assert.True(x == (float)2.71828);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue18()
|
public static void TestIssue18()
|
||||||
{
|
{
|
||||||
var x = new NSNumber(-999);
|
var x = new NSNumber(-999);
|
||||||
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testIssue18.plist"));
|
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testIssue18.plist"));
|
||||||
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testIssue18.plist"));
|
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testIssue18.plist"));
|
||||||
Assert.True(x.Equals(y));
|
Assert.True(x.Equals(y));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue21()
|
public static void TestIssue21()
|
||||||
{
|
{
|
||||||
string x = ((NSString)PropertyListParser.Parse(new FileInfo("test-files/issue21.plist"))).ToString();
|
string x = ((NSString)PropertyListParser.Parse(new FileInfo("test-files/issue21.plist"))).ToString();
|
||||||
Assert.Equal("Lot&s of &persand&s and other escapable \"\'<>€ characters", x);
|
Assert.Equal("Lot&s of &persand&s and other escapable \"\'<>€ characters", x);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue22()
|
public static void TestIssue22()
|
||||||
{
|
{
|
||||||
var x1 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue22-emoji.plist"));
|
var x1 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue22-emoji.plist"));
|
||||||
var x2 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue22-emoji-xml.plist"));
|
var x2 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue22-emoji-xml.plist"));
|
||||||
PropertyListParser.SaveAsBinary(x1, new FileInfo("test-files/out-testIssue22.plist"));
|
PropertyListParser.SaveAsBinary(x1, new FileInfo("test-files/out-testIssue22.plist"));
|
||||||
var y1 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/out-testIssue22.plist"));
|
var y1 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/out-testIssue22.plist"));
|
||||||
PropertyListParser.SaveAsXml(x2, new FileInfo("test-files/out-testIssue22-xml.plist"));
|
PropertyListParser.SaveAsXml(x2, new FileInfo("test-files/out-testIssue22-xml.plist"));
|
||||||
var y2 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/out-testIssue22-xml.plist"));
|
var y2 = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/out-testIssue22-xml.plist"));
|
||||||
Assert.True(x1.Equals(x2));
|
Assert.True(x1.Equals(x2));
|
||||||
Assert.True(x1.Equals(y1));
|
Assert.True(x1.Equals(y1));
|
||||||
Assert.True(x1.Equals(y2));
|
Assert.True(x1.Equals(y2));
|
||||||
Assert.True(x2.Equals(y1));
|
Assert.True(x2.Equals(y1));
|
||||||
Assert.True(x2.Equals(y2));
|
Assert.True(x2.Equals(y2));
|
||||||
|
|
||||||
string emojiString = "Test Test, \uD83D\uDE30\u2754\uD83D\uDC4D\uD83D\uDC4E\uD83D\uDD25";
|
string emojiString = "Test Test, \uD83D\uDE30\u2754\uD83D\uDC4D\uD83D\uDC4E\uD83D\uDD25";
|
||||||
|
|
||||||
Assert.Equal(emojiString, x1.ObjectForKey("emojiString").ToString());
|
Assert.Equal(emojiString, x1.ObjectForKey("emojiString").ToString());
|
||||||
Assert.Equal(emojiString, x2.ObjectForKey("emojiString").ToString());
|
Assert.Equal(emojiString, x2.ObjectForKey("emojiString").ToString());
|
||||||
Assert.Equal(emojiString, y1.ObjectForKey("emojiString").ToString());
|
Assert.Equal(emojiString, y1.ObjectForKey("emojiString").ToString());
|
||||||
Assert.Equal(emojiString, y2.ObjectForKey("emojiString").ToString());
|
Assert.Equal(emojiString, y2.ObjectForKey("emojiString").ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[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]
|
||||||
public static void TestIssue38()
|
public static void TestIssue38()
|
||||||
{
|
{
|
||||||
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue33.pbxproj"));
|
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue33.pbxproj"));
|
||||||
|
|
||||||
NSObject fileRef =
|
NSObject fileRef =
|
||||||
((NSDictionary)((NSDictionary)dict.Get("objects")).Get("65541A9C16D13B8C00A968D5")).Get("fileRef");
|
((NSDictionary)((NSDictionary)dict.Get("objects")).Get("65541A9C16D13B8C00A968D5")).Get("fileRef");
|
||||||
|
|
||||||
Assert.True(fileRef.Equals(new NSString("65541A9B16D13B8C00A968D5")));
|
Assert.True(fileRef.Equals(new NSString("65541A9B16D13B8C00A968D5")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue4()
|
public static void TestIssue4()
|
||||||
{
|
{
|
||||||
var d = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue4.plist"));
|
var d = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue4.plist"));
|
||||||
Assert.Equal("Kid\u2019s iPhone", ((NSString)d.ObjectForKey("Device Name")).ToString());
|
Assert.Equal("Kid\u2019s iPhone", ((NSString)d.ObjectForKey("Device Name")).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue49()
|
public static void TestIssue49()
|
||||||
{
|
{
|
||||||
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue49.plist"));
|
var dict = (NSDictionary)PropertyListParser.Parse(new FileInfo("test-files/issue49.plist"));
|
||||||
Assert.Empty(dict);
|
Assert.Empty(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestIssue7()
|
public static void TestIssue7()
|
||||||
{
|
{
|
||||||
// also a test for issue 12
|
// also a test for issue 12
|
||||||
// the issue4 test has a UTF-16-BE string in its binary representation
|
// the issue4 test has a UTF-16-BE string in its binary representation
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/issue4.plist"));
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/issue4.plist"));
|
||||||
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testIssue7.plist"));
|
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testIssue7.plist"));
|
||||||
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testIssue7.plist"));
|
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testIssue7.plist"));
|
||||||
Assert.True(x.Equals(y));
|
Assert.True(x.Equals(y));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void TestRealInResourceRule()
|
public static void TestRealInResourceRule()
|
||||||
{
|
{
|
||||||
var dict = (NSDictionary)XmlPropertyListParser.Parse(new FileInfo("test-files/ResourceRules.plist"));
|
var dict = (NSDictionary)XmlPropertyListParser.Parse(new FileInfo("test-files/ResourceRules.plist"));
|
||||||
Assert.Single(dict);
|
Assert.Single(dict);
|
||||||
Assert.True(dict.ContainsKey("weight"));
|
Assert.True(dict.ContainsKey("weight"));
|
||||||
|
|
||||||
object weight = dict["weight"].ToObject();
|
object weight = dict["weight"].ToObject();
|
||||||
Assert.IsType<double>(weight);
|
Assert.IsType<double>(weight);
|
||||||
Assert.Equal(10d, (double)weight);
|
Assert.Equal(10d, (double)weight);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,90 +2,89 @@
|
|||||||
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>
|
||||||
|
[Fact]
|
||||||
|
public void AddAndContainsObjectTest()
|
||||||
{
|
{
|
||||||
/// <summary>Tests the addition of a .NET object to the NSArray</summary>
|
var array = new NSArray
|
||||||
[Fact]
|
|
||||||
public void AddAndContainsObjectTest()
|
|
||||||
{
|
{
|
||||||
var array = new NSArray
|
1
|
||||||
{
|
};
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.True(array.Contains(1));
|
Assert.True(array.Contains(1));
|
||||||
Assert.False(array.Contains(2));
|
Assert.False(array.Contains(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tests the <see cref="NSArray.GetEnumerator" /> method.</summary>
|
/// <summary>Tests the <see cref="NSArray.GetEnumerator" /> method.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EnumeratorTest()
|
public void EnumeratorTest()
|
||||||
|
{
|
||||||
|
var array = new NSArray
|
||||||
{
|
{
|
||||||
var array = new NSArray
|
0,
|
||||||
{
|
1
|
||||||
0,
|
};
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
using IEnumerator<NSObject> enumerator = array.GetEnumerator();
|
using IEnumerator<NSObject> enumerator = array.GetEnumerator();
|
||||||
|
|
||||||
Assert.Null(enumerator.Current);
|
Assert.Null(enumerator.Current);
|
||||||
|
|
||||||
Assert.True(enumerator.MoveNext());
|
Assert.True(enumerator.MoveNext());
|
||||||
Assert.Equal(new NSNumber(0), enumerator.Current);
|
Assert.Equal(new NSNumber(0), enumerator.Current);
|
||||||
|
|
||||||
Assert.True(enumerator.MoveNext());
|
Assert.True(enumerator.MoveNext());
|
||||||
Assert.Equal(new NSNumber(1), enumerator.Current);
|
Assert.Equal(new NSNumber(1), enumerator.Current);
|
||||||
|
|
||||||
Assert.False(enumerator.MoveNext());
|
Assert.False(enumerator.MoveNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tests the <see cref="NSArray.IndexOf(object)" /> method for .NET objects.</summary>
|
/// <summary>Tests the <see cref="NSArray.IndexOf(object)" /> method for .NET objects.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IndexOfTest()
|
public void IndexOfTest()
|
||||||
|
{
|
||||||
|
var array = new NSArray
|
||||||
{
|
{
|
||||||
var array = new NSArray
|
1,
|
||||||
{
|
"test"
|
||||||
1,
|
};
|
||||||
"test"
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.Equal(0, array.IndexOf(1));
|
Assert.Equal(0, array.IndexOf(1));
|
||||||
Assert.Equal(1, array.IndexOf("test"));
|
Assert.Equal(1, array.IndexOf("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tests the <see cref="NSArray.Insert(int, object)" /> method for a .NET object.</summary>
|
/// <summary>Tests the <see cref="NSArray.Insert(int, object)" /> method for a .NET object.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InsertTest()
|
public void InsertTest()
|
||||||
|
{
|
||||||
|
var array = new NSArray
|
||||||
{
|
{
|
||||||
var array = new NSArray
|
0,
|
||||||
{
|
1,
|
||||||
0,
|
2
|
||||||
1,
|
};
|
||||||
2
|
|
||||||
};
|
|
||||||
|
|
||||||
array.Insert(1, "test");
|
array.Insert(1, "test");
|
||||||
|
|
||||||
Assert.Equal(4, array.Count);
|
Assert.Equal(4, array.Count);
|
||||||
Assert.Equal("test", array[1].ToObject());
|
Assert.Equal("test", array[1].ToObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Tests the <see cref="NSArray.Remove(object)" /> method for a .NET object.</summary>
|
/// <summary>Tests the <see cref="NSArray.Remove(object)" /> method for a .NET object.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RemoveTest()
|
public void RemoveTest()
|
||||||
|
{
|
||||||
|
var array = new NSArray
|
||||||
{
|
{
|
||||||
var array = new NSArray
|
0
|
||||||
{
|
};
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.False(array.Remove((object)1));
|
Assert.False(array.Remove((object)1));
|
||||||
Assert.True(array.Remove((object)0));
|
Assert.True(array.Remove((object)0));
|
||||||
|
|
||||||
Assert.Empty(array);
|
Assert.Empty(array);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,26 +2,25 @@
|
|||||||
using Claunia.PropertyList;
|
using Claunia.PropertyList;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace plistcil.test
|
namespace plistcil.test;
|
||||||
|
|
||||||
|
public class NSDateTests
|
||||||
{
|
{
|
||||||
public class NSDateTests
|
[Fact]
|
||||||
|
public static void ConstructorTest()
|
||||||
{
|
{
|
||||||
[Fact]
|
var actual = new NSDate("2000-01-01T00:00:00Z");
|
||||||
public static void ConstructorTest()
|
var expected = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
{
|
Assert.Equal(expected, actual.Date.ToUniversalTime());
|
||||||
var actual = new NSDate("2000-01-01T00:00:00Z");
|
}
|
||||||
var expected = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
||||||
Assert.Equal(expected, actual.Date.ToUniversalTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void MakeDateStringTest()
|
public static void MakeDateStringTest()
|
||||||
{
|
{
|
||||||
var date = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
var date = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
string expected = "2000-01-01T00:00:00Z";
|
string expected = "2000-01-01T00:00:00Z";
|
||||||
string actual = NSDate.MakeDateString(date);
|
string actual = NSDate.MakeDateString(date);
|
||||||
|
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,521 +3,513 @@ 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
|
||||||
|
// 0
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
// INTEGER values
|
new byte[]
|
||||||
// 0
|
|
||||||
new object[]
|
|
||||||
{
|
{
|
||||||
new byte[]
|
0x00
|
||||||
{
|
|
||||||
0x00
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, false, 0, 0.0
|
|
||||||
},
|
},
|
||||||
|
NSNumber.INTEGER, false, 0, 0.0
|
||||||
|
},
|
||||||
|
|
||||||
// 1-byte value < sbyte.maxValue
|
// 1-byte value < sbyte.maxValue
|
||||||
new object[]
|
new object[]
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x10
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, 16, 16.0
|
|
||||||
},
|
|
||||||
|
|
||||||
// 1-byte value > sbyte.MaxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0xFF
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, byte.MaxValue, (double)byte.MaxValue
|
|
||||||
},
|
|
||||||
|
|
||||||
// 2-byte value < short.maxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x10, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, 4096, 4096.0
|
|
||||||
},
|
|
||||||
|
|
||||||
// 2-byte value > short.maxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0xFF, 0xFF
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, ushort.MaxValue, (double)ushort.MaxValue
|
|
||||||
},
|
|
||||||
|
|
||||||
// 4-byte value < int.maxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x10, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, 0x10000000, 1.0 * 0x10000000
|
|
||||||
},
|
|
||||||
|
|
||||||
// 4-bit value > int.MaxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, uint.MaxValue, (double)uint.MaxValue
|
|
||||||
},
|
|
||||||
|
|
||||||
// 64-bit value < long.MaxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, 0x1000000000000000, 1.0 * 0x1000000000000000
|
|
||||||
},
|
|
||||||
|
|
||||||
// 64-bit value > long.MaxValue
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, -1, -1.0
|
|
||||||
},
|
|
||||||
|
|
||||||
// 128-bit positive value
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, unchecked((long)0xffffffffffffa000), 1.0 * unchecked((long)0xffffffffffffa000)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 128-bit negative value
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
||||||
},
|
|
||||||
NSNumber.INTEGER, true, -1, -1.0
|
|
||||||
},
|
|
||||||
|
|
||||||
// REAL values
|
|
||||||
// 4-byte value (float)
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.0
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x41, 0x20, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, true, 10, 10.0
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x3d, 0xcc, 0xcc, 0xcd
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.1
|
|
||||||
},
|
|
||||||
|
|
||||||
// 8-byte value (double)
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.0
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x40, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
},
|
|
||||||
NSNumber.REAL, true, 10, 10.0
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new byte[]
|
|
||||||
{
|
|
||||||
0x3f, 0xb9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a
|
|
||||||
},
|
|
||||||
NSNumber.REAL, false, 0, 0.1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(SpanConstructorTestData))]
|
|
||||||
public void SpanConstructorTest(byte[] data, int type, bool boolValue, long longValue, double doubleValue)
|
|
||||||
{
|
{
|
||||||
var number = new NSNumber((Span<byte>)data, type);
|
new byte[]
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
{
|
||||||
Assert.Equal(longValue, number.ToLong());
|
0x10
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
},
|
||||||
|
NSNumber.INTEGER, true, 16, 16.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// 1-byte value > sbyte.MaxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0xFF
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, byte.MaxValue, (double)byte.MaxValue
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2-byte value < short.maxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x10, 0x00
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, 4096, 4096.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2-byte value > short.maxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0xFF, 0xFF
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, ushort.MaxValue, (double)ushort.MaxValue
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4-byte value < int.maxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x10, 0x00, 0x00, 0x00
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, 0x10000000, 1.0 * 0x10000000
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4-bit value > int.MaxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, uint.MaxValue, (double)uint.MaxValue
|
||||||
|
},
|
||||||
|
|
||||||
|
// 64-bit value < long.MaxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, 0x1000000000000000, 1.0 * 0x1000000000000000
|
||||||
|
},
|
||||||
|
|
||||||
|
// 64-bit value > long.MaxValue
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, -1, -1.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// 128-bit positive value
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x00
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, unchecked((long)0xffffffffffffa000), 1.0 * unchecked((long)0xffffffffffffa000)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 128-bit negative value
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
},
|
||||||
|
NSNumber.INTEGER, true, -1, -1.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// REAL values
|
||||||
|
// 4-byte value (float)
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"\0\0\0\0"u8.ToArray(),
|
||||||
|
NSNumber.REAL, false, 0, 0.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"A \0\0"u8.ToArray(),
|
||||||
|
NSNumber.REAL, true, 10, 10.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x3d, 0xcc, 0xcc, 0xcd
|
||||||
|
},
|
||||||
|
NSNumber.REAL, false, 0, 0.1
|
||||||
|
},
|
||||||
|
|
||||||
|
// 8-byte value (double)
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"\0\0\0\0\0\0\0\0"u8.ToArray(),
|
||||||
|
NSNumber.REAL, false, 0, 0.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"@$\0\0\0\0\0\0"u8.ToArray(),
|
||||||
|
NSNumber.REAL, true, 10, 10.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new byte[]
|
||||||
|
{
|
||||||
|
0x3f, 0xb9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a
|
||||||
|
},
|
||||||
|
NSNumber.REAL, false, 0, 0.1
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void SpanConstructorInvalidValuesTest()
|
[MemberData(nameof(SpanConstructorTestData))]
|
||||||
{
|
public void SpanConstructorTest(byte[] data, int type, bool boolValue, long longValue, double doubleValue)
|
||||||
Assert.Throws<ArgumentNullException>(() => new NSNumber((Span<byte>)null, NSNumber.INTEGER));
|
{
|
||||||
Assert.Throws<ArgumentNullException>(() => new NSNumber((Span<byte>)null, NSNumber.REAL));
|
var number = new NSNumber((Span<byte>)data, type);
|
||||||
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(),
|
[Fact]
|
||||||
NSNumber.INTEGER));
|
public void SpanConstructorInvalidValuesTest()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new NSNumber((Span<byte>)null, NSNumber.INTEGER));
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new NSNumber((Span<byte>)null, NSNumber.REAL));
|
||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(), NSNumber.REAL));
|
Assert.Throws<ArgumentOutOfRangeException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(),
|
||||||
Assert.Throws<ArgumentException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(), 9));
|
NSNumber.INTEGER));
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
Assert.Throws<ArgumentException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(), NSNumber.REAL));
|
||||||
public void StringAndTypeConstructorInvalidValuesTest()
|
Assert.Throws<ArgumentException>(() => new NSNumber((Span<byte>)Array.Empty<byte>(), 9));
|
||||||
{
|
}
|
||||||
Assert.Throws<ArgumentNullException>(() => new NSNumber((string)null, NSNumber.INTEGER));
|
|
||||||
Assert.Throws<ArgumentNullException>(() => new NSNumber((string)null, NSNumber.REAL));
|
|
||||||
Assert.Throws<ArgumentException>(() => new NSNumber("0", 9));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void NSNumberConstructorTest()
|
public void StringAndTypeConstructorInvalidValuesTest()
|
||||||
{
|
{
|
||||||
var number = new NSNumber("10032936613", NSNumber.INTEGER);
|
Assert.Throws<ArgumentNullException>(() => new NSNumber((string)null, NSNumber.INTEGER));
|
||||||
Assert.Equal(NSNumber.INTEGER, number.GetNSNumberType());
|
Assert.Throws<ArgumentNullException>(() => new NSNumber((string)null, NSNumber.REAL));
|
||||||
Assert.Equal(10032936613, number.ToObject());
|
Assert.Throws<ArgumentException>(() => new NSNumber("0", 9));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public static void NSNumberWithDecimalTest()
|
public static void NSNumberConstructorTest()
|
||||||
{
|
{
|
||||||
var number = new NSNumber("1360155352.748765", NSNumber.REAL);
|
var number = new NSNumber("10032936613", NSNumber.INTEGER);
|
||||||
Assert.Equal("1360155352.748765", number.ToString());
|
Assert.Equal(NSNumber.INTEGER, number.GetNSNumberType());
|
||||||
}
|
Assert.Equal(10032936613, number.ToObject());
|
||||||
|
}
|
||||||
|
|
||||||
// The tests below make sure the numbers are being parsed correctly, and do not depend on the culture info
|
[Fact]
|
||||||
// being set. Especially, decimal point may vary between cultures and we don't want to take a dependency on that
|
public static void NSNumberWithDecimalTest()
|
||||||
// The value being used comes seen in a real property list:
|
{
|
||||||
|
var number = new NSNumber("1360155352.748765", NSNumber.REAL);
|
||||||
|
Assert.Equal("1360155352.748765", number.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tests below make sure the numbers are being parsed correctly, and do not depend on the culture info
|
||||||
|
// being set. Especially, decimal point may vary between cultures and we don't want to take a dependency on that
|
||||||
|
// The value being used comes seen in a real property list:
|
||||||
|
// <key>TimeZoneOffsetFromUTC</key>
|
||||||
|
// <real>7200.000000</real>
|
||||||
|
[Fact]
|
||||||
|
[UseCulture("en-US")]
|
||||||
|
public static void ParseNumberEnTest()
|
||||||
|
{
|
||||||
|
var number = new NSNumber("7200.000001");
|
||||||
|
Assert.True(number.isReal());
|
||||||
|
Assert.Equal(7200.000001d, number.ToDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[UseCulture("nl-BE")]
|
||||||
|
public static void ParseNumberNlTest()
|
||||||
|
{
|
||||||
|
// As seen in a real property list:
|
||||||
// <key>TimeZoneOffsetFromUTC</key>
|
// <key>TimeZoneOffsetFromUTC</key>
|
||||||
// <real>7200.000000</real>
|
// <real>7200.000000</real>
|
||||||
[Fact, UseCulture("en-US")]
|
var number = new NSNumber("7200.000001");
|
||||||
public static void ParseNumberEnTest()
|
Assert.True(number.isReal());
|
||||||
|
Assert.Equal(7200.000001d, number.ToDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[UseCulture("en-US")]
|
||||||
|
public static void ParseNumberEnTest2()
|
||||||
|
{
|
||||||
|
// As seen in a real property list:
|
||||||
|
// <key>TimeZoneOffsetFromUTC</key>
|
||||||
|
// <real>7200.000000</real>
|
||||||
|
var number = new NSNumber("7200.000000", NSNumber.REAL);
|
||||||
|
Assert.True(number.isReal());
|
||||||
|
Assert.Equal(7200d, number.ToDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[UseCulture("nl-BE")]
|
||||||
|
public static void ParseNumberNlTest2()
|
||||||
|
{
|
||||||
|
// As seen in a real property list:
|
||||||
|
// <key>TimeZoneOffsetFromUTC</key>
|
||||||
|
// <real>7200.000000</real>
|
||||||
|
var number = new NSNumber("7200.000000", NSNumber.REAL);
|
||||||
|
Assert.True(number.isReal());
|
||||||
|
Assert.Equal(7200d, number.ToDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> StringConstructorTestData() => new List<object[]>
|
||||||
|
{
|
||||||
|
// Long values, formatted as hexadecimal values
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
var number = new NSNumber("7200.000001");
|
"0x00", false, 0, 0.0
|
||||||
Assert.True(number.isReal());
|
},
|
||||||
Assert.Equal(7200.000001d, number.ToDouble());
|
new object[]
|
||||||
|
{
|
||||||
|
"0x1000", true, 0x1000, 1.0 * 0x1000
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"0x00001000", true, 0x1000, 1.0 * 0x1000
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"0x0000000000001000", true, 0x1000, 1.0 * 0x1000
|
||||||
|
},
|
||||||
|
|
||||||
|
// Long values, formatted as decimal values
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"0", false, 0, 0.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"10", true, 10, 10.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// Decimal values
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"0.0", false, 0, 0.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"0.10", false, 0, 0.1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"3.14", true, 3, 3.14
|
||||||
|
},
|
||||||
|
|
||||||
|
// Boolean values
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"yes", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"true", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Yes", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"True", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"YES", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"TRUE", true, 1, 1
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"no", false, 0, 0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"false", false, 0, 0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"No", false, 0, 0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"False", false, 0, 0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"NO", false, 0, 0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"FALSE", false, 0, 0
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[Fact, UseCulture("nl-BE")]
|
[Theory]
|
||||||
public static void ParseNumberNlTest()
|
[MemberData(nameof(StringConstructorTestData))]
|
||||||
|
public void StringConstructorTest(string value, bool boolValue, long longValue, double doubleValue)
|
||||||
|
{
|
||||||
|
var number = new NSNumber(value);
|
||||||
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StringConstructorInvalidValuesTest()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => new NSNumber(null));
|
||||||
|
Assert.Throws<ArgumentException>(() => new NSNumber("plist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Int32ConstructorTestData() => new List<object[]>
|
||||||
|
{
|
||||||
|
// Long values, formatted as hexadecimal values
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
0, false, 0, 0.0
|
||||||
// <key>TimeZoneOffsetFromUTC</key>
|
},
|
||||||
// <real>7200.000000</real>
|
new object[]
|
||||||
var number = new NSNumber("7200.000001");
|
{
|
||||||
Assert.True(number.isReal());
|
1, true, 1, 1.0
|
||||||
Assert.Equal(7200.000001d, number.ToDouble());
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
-1, true, -1, -1.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
int.MaxValue, true, int.MaxValue, int.MaxValue
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
int.MinValue, true, int.MinValue, int.MinValue
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[Fact, UseCulture("en-US")]
|
[Theory]
|
||||||
public static void ParseNumberEnTest2()
|
[MemberData(nameof(Int32ConstructorTestData))]
|
||||||
|
public void Int32ConstructorTest(int value, bool boolValue, long longValue, double doubleValue)
|
||||||
|
{
|
||||||
|
var number = new NSNumber(value);
|
||||||
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Int64ConstructorTestData() => new List<object[]>
|
||||||
|
{
|
||||||
|
// Long values, formatted as hexadecimal values
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
0, false, 0, 0.0
|
||||||
// <key>TimeZoneOffsetFromUTC</key>
|
},
|
||||||
// <real>7200.000000</real>
|
new object[]
|
||||||
var number = new NSNumber("7200.000000", NSNumber.REAL);
|
{
|
||||||
Assert.True(number.isReal());
|
1, true, 1, 1.0
|
||||||
Assert.Equal(7200d, number.ToDouble());
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
-1, true, -1, -1.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
long.MaxValue, true, long.MaxValue, long.MaxValue
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
long.MinValue, true, long.MinValue, long.MinValue
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[Fact, UseCulture("nl-BE")]
|
[Theory]
|
||||||
public static void ParseNumberNlTest2()
|
[MemberData(nameof(Int64ConstructorTestData))]
|
||||||
|
public void Int64ConstructorTest(long value, bool boolValue, long longValue, double doubleValue)
|
||||||
|
{
|
||||||
|
var number = new NSNumber(value);
|
||||||
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> DoubleConstructorTestData() => new List<object[]>
|
||||||
|
{
|
||||||
|
// Long values, formatted as hexadecimal values
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
// As seen in a real property list:
|
0.0, false, 0, 0.0
|
||||||
// <key>TimeZoneOffsetFromUTC</key>
|
},
|
||||||
// <real>7200.000000</real>
|
new object[]
|
||||||
var number = new NSNumber("7200.000000", NSNumber.REAL);
|
{
|
||||||
Assert.True(number.isReal());
|
1.0, true, 1, 1.0
|
||||||
Assert.Equal(7200d, number.ToDouble());
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
-1.0, true, -1, -1.0
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
double.Epsilon, false, 0, double.Epsilon
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
double.MaxValue, true, long.MinValue /* Overflow! */, double.MaxValue
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
double.MinValue, true, long.MinValue, double.MinValue
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static IEnumerable<object[]> StringConstructorTestData() => new List<object[]>
|
[Theory]
|
||||||
|
[MemberData(nameof(DoubleConstructorTestData))]
|
||||||
|
public void DoubleConstructorTest(double value, bool boolValue, long longValue, double doubleValue)
|
||||||
|
{
|
||||||
|
var number = new NSNumber(value);
|
||||||
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> BoolConstructorTestData() => new List<object[]>
|
||||||
|
{
|
||||||
|
// Long values, formatted as hexadecimal values
|
||||||
|
new object[]
|
||||||
{
|
{
|
||||||
// Long values, formatted as hexadecimal values
|
false, false, 0, 0.0
|
||||||
new object[]
|
},
|
||||||
{
|
new object[]
|
||||||
"0x00", false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0x1000", true, 0x1000, 1.0 * 0x1000
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0x00001000", true, 0x1000, 1.0 * 0x1000
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0x0000000000001000", true, 0x1000, 1.0 * 0x1000
|
|
||||||
},
|
|
||||||
|
|
||||||
// Long values, formatted as decimal values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0", false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"10", true, 10, 10.0
|
|
||||||
},
|
|
||||||
|
|
||||||
// Decimal values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0.0", false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"0.10", false, 0, 0.1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"3.14", true, 3, 3.14
|
|
||||||
},
|
|
||||||
|
|
||||||
// Boolean values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"yes", true, 1, 1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"true", true, 1, 1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"Yes", true, 1, 1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"True", true, 1, 1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"YES", true, 1, 1
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"TRUE", true, 1, 1
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"no", false, 0, 0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"false", false, 0, 0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"No", false, 0, 0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"False", false, 0, 0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"NO", false, 0, 0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
"FALSE", false, 0, 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(StringConstructorTestData))]
|
|
||||||
public void StringConstructorTest(string value, bool boolValue, long longValue, double doubleValue)
|
|
||||||
{
|
{
|
||||||
var number = new NSNumber(value);
|
true, true, 1, 1.0
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
|
||||||
Assert.Equal(longValue, number.ToLong());
|
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void StringConstructorInvalidValuesTest()
|
[MemberData(nameof(BoolConstructorTestData))]
|
||||||
{
|
public void BoolConstructorTest(bool value, bool boolValue, long longValue, double doubleValue)
|
||||||
Assert.Throws<ArgumentException>(() => new NSNumber(null));
|
{
|
||||||
Assert.Throws<ArgumentException>(() => new NSNumber("plist"));
|
var number = new NSNumber(value);
|
||||||
}
|
Assert.Equal(boolValue, number.ToBool());
|
||||||
|
Assert.Equal(longValue, number.ToLong());
|
||||||
|
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<object[]> Int32ConstructorTestData() => new List<object[]>
|
[Fact]
|
||||||
{
|
public void EqualTest()
|
||||||
// Long values, formatted as hexadecimal values
|
{
|
||||||
new object[]
|
var a = new NSNumber(2);
|
||||||
{
|
var b = new NSNumber(2);
|
||||||
0, false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
1, true, 1, 1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
-1, true, -1, -1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
int.MaxValue, true, int.MaxValue, int.MaxValue
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
int.MinValue, true, int.MinValue, int.MinValue
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(Int32ConstructorTestData))]
|
Assert.Equal(a.GetHashCode(), b.GetHashCode());
|
||||||
public void Int32ConstructorTest(int value, bool boolValue, long longValue, double doubleValue)
|
Assert.True(a.Equals(b));
|
||||||
{
|
Assert.True(b.Equals(a));
|
||||||
var number = new NSNumber(value);
|
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
|
||||||
Assert.Equal(longValue, number.ToLong());
|
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> Int64ConstructorTestData() => new List<object[]>
|
|
||||||
{
|
|
||||||
// Long values, formatted as hexadecimal values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0, false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
1, true, 1, 1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
-1, true, -1, -1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
long.MaxValue, true, long.MaxValue, long.MaxValue
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
long.MinValue, true, long.MinValue, long.MinValue
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(Int64ConstructorTestData))]
|
|
||||||
public void Int64ConstructorTest(long value, bool boolValue, long longValue, double doubleValue)
|
|
||||||
{
|
|
||||||
var number = new NSNumber(value);
|
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
|
||||||
Assert.Equal(longValue, number.ToLong());
|
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> DoubleConstructorTestData() => new List<object[]>
|
|
||||||
{
|
|
||||||
// Long values, formatted as hexadecimal values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
0.0, false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
1.0, true, 1, 1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
-1.0, true, -1, -1.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
double.Epsilon, false, 0, double.Epsilon
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
double.MaxValue, true, long.MinValue /* Overflow! */, double.MaxValue
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
double.MinValue, true, long.MinValue, double.MinValue
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(DoubleConstructorTestData))]
|
|
||||||
public void DoubleConstructorTest(double value, bool boolValue, long longValue, double doubleValue)
|
|
||||||
{
|
|
||||||
var number = new NSNumber(value);
|
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
|
||||||
Assert.Equal(longValue, number.ToLong());
|
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object[]> BoolConstructorTestData() => new List<object[]>
|
|
||||||
{
|
|
||||||
// Long values, formatted as hexadecimal values
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
false, false, 0, 0.0
|
|
||||||
},
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
true, true, 1, 1.0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory, MemberData(nameof(BoolConstructorTestData))]
|
|
||||||
public void BoolConstructorTest(bool value, bool boolValue, long longValue, double doubleValue)
|
|
||||||
{
|
|
||||||
var number = new NSNumber(value);
|
|
||||||
Assert.Equal(boolValue, number.ToBool());
|
|
||||||
Assert.Equal(longValue, number.ToLong());
|
|
||||||
Assert.Equal(doubleValue, number.ToDouble(), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void EqualTest()
|
|
||||||
{
|
|
||||||
var a = new NSNumber(2);
|
|
||||||
var b = new NSNumber(2);
|
|
||||||
|
|
||||||
Assert.Equal(a.GetHashCode(), b.GetHashCode());
|
|
||||||
Assert.True(a.Equals(b));
|
|
||||||
Assert.True(b.Equals(a));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,29 +1,28 @@
|
|||||||
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 END_TOKEN = "</string>";
|
||||||
|
|
||||||
|
[InlineData("abc", "abc")]
|
||||||
|
[InlineData("a>b", "a>b")]
|
||||||
|
[InlineData("a<b", "a<b")]
|
||||||
|
[InlineData("a&b", "a&b")]
|
||||||
|
[Theory]
|
||||||
|
public void Content_IsEscaped(string value, string content)
|
||||||
{
|
{
|
||||||
const string START_TOKEN = "<string>";
|
var element = new NSString(value);
|
||||||
const string END_TOKEN = "</string>";
|
string xml = element.ToXmlPropertyList();
|
||||||
|
|
||||||
[InlineData("abc", "abc")]
|
// Strip the leading and trailing data, so we just get the string element itself
|
||||||
[InlineData("a>b", "a>b")]
|
int start = xml.IndexOf(START_TOKEN) + START_TOKEN.Length;
|
||||||
[InlineData("a<b", "a<b")]
|
int end = xml.IndexOf(END_TOKEN);
|
||||||
[InlineData("a&b", "a&b")]
|
string actualContent = xml.Substring(start, end - start);
|
||||||
[Theory]
|
|
||||||
public void Content_IsEscaped(string value, string content)
|
|
||||||
{
|
|
||||||
var element = new NSString(value);
|
|
||||||
string xml = element.ToXmlPropertyList();
|
|
||||||
|
|
||||||
// Strip the leading and trailing data, so we just get the string element itself
|
Assert.Equal(content, actualContent);
|
||||||
int start = xml.IndexOf(START_TOKEN) + START_TOKEN.Length;
|
|
||||||
int end = xml.IndexOf(END_TOKEN);
|
|
||||||
string actualContent = xml.Substring(start, end - start);
|
|
||||||
|
|
||||||
Assert.Equal(content, actualContent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,315 +29,327 @@ 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) return false;
|
||||||
{
|
|
||||||
if(arrayA.Length != arrayB.Length)
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NSSet only occurs in binary property lists, so we have to test it separately.
|
* NSSet only occurs in binary property lists, so we have to test it separately.
|
||||||
* NSSets are not yet supported in reading/writing, as binary property list format v1+ is required.
|
* NSSets are not yet supported in reading/writing, as binary property list format v1+ is required.
|
||||||
*/
|
|
||||||
/*
|
|
||||||
[Fact]
|
|
||||||
public static void TestSet()
|
|
||||||
{
|
|
||||||
NSSet s = new NSSet();
|
|
||||||
s.AddObject(new NSNumber(1));
|
|
||||||
s.AddObject(new NSNumber(3));
|
|
||||||
s.AddObject(new NSNumber(2));
|
|
||||||
|
|
||||||
NSSet orderedSet = new NSSet(true);
|
|
||||||
s.AddObject(new NSNumber(1));
|
|
||||||
s.AddObject(new NSNumber(3));
|
|
||||||
s.AddObject(new NSNumber(2));
|
|
||||||
|
|
||||||
NSDictionary dict = new NSDictionary();
|
|
||||||
dict.Add("set1", s);
|
|
||||||
dict.Add("set2", orderedSet);
|
|
||||||
|
|
||||||
PropertyListParser.SaveAsBinary(dict, new FileInfo("test-files/out-testSet.plist"));
|
|
||||||
NSObject ParsedRoot = PropertyListParser.Parse(new FileInfo("test-files/out-testSet.plist"));
|
|
||||||
Assert.True(ParsedRoot.Equals(dict));
|
|
||||||
}*/
|
|
||||||
[Fact]
|
|
||||||
public static void TestASCII()
|
|
||||||
{
|
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1-ascii.plist"));
|
|
||||||
var d = (NSDictionary)x;
|
|
||||||
Assert.True(d.Count == 5);
|
|
||||||
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
|
||||||
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
|
||||||
|
|
||||||
var actualDate = (NSDate)d.ObjectForKey("date");
|
|
||||||
DateTime expectedDate = new DateTime(2011, 11, 28, 9, 21, 30, DateTimeKind.Utc).ToLocalTime();
|
|
||||||
|
|
||||||
Assert.Equal(actualDate.Date, expectedDate);
|
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
|
||||||
}));
|
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
|
||||||
Assert.True(a.Count == 4);
|
|
||||||
Assert.True(a[0].Equals(new NSString("YES")));
|
|
||||||
Assert.True(a[1].Equals(new NSString("NO")));
|
|
||||||
Assert.True(a[2].Equals(new NSString("87")));
|
|
||||||
Assert.True(a[3].Equals(new NSString("3.14159")));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void testAsciiUtf8CharactersInQuotedString()
|
|
||||||
{
|
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test-ascii-utf8.plist"));
|
|
||||||
var d = (NSDictionary)x;
|
|
||||||
Assert.Equal(2, d.Count);
|
|
||||||
Assert.Equal("JÔÖú@2x.jpg", d.ObjectForKey("path").ToString());
|
|
||||||
Assert.Equal("QÔÖú@2x 啕.jpg", d.ObjectForKey("Key QÔÖª@2x 䌡").ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestASCIIWriting()
|
|
||||||
{
|
|
||||||
var inf = new FileInfo("test-files/test1.plist");
|
|
||||||
var outf = new FileInfo("test-files/out-test1-ascii.plist");
|
|
||||||
var in2 = new FileInfo("test-files/test1-ascii.plist");
|
|
||||||
var x = (NSDictionary)PropertyListParser.Parse(inf);
|
|
||||||
PropertyListParser.SaveAsASCII(x, outf);
|
|
||||||
|
|
||||||
//Information gets lost when saving into the ASCII format (NSNumbers are converted to NSStrings)
|
|
||||||
|
|
||||||
var y = (NSDictionary)PropertyListParser.Parse(outf);
|
|
||||||
var z = (NSDictionary)PropertyListParser.Parse(in2);
|
|
||||||
Assert.True(y.Equals(z));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the binary reader/writer.
|
|
||||||
*/
|
|
||||||
[Fact]
|
|
||||||
public static void TestBinary()
|
|
||||||
{
|
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1.plist"));
|
|
||||||
|
|
||||||
// save and load as binary
|
|
||||||
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testBinary.plist"));
|
|
||||||
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testBinary.plist"));
|
|
||||||
Assert.True(x.Equals(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestGnuStepASCII()
|
|
||||||
{
|
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1-ascii-gnustep.plist"));
|
|
||||||
var d = (NSDictionary)x;
|
|
||||||
Assert.True(d.Count == 5);
|
|
||||||
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).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,
|
|
||||||
DateTimeKind.Utc).ToLocalTime()));
|
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
|
||||||
{
|
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
|
||||||
}));
|
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
|
||||||
Assert.True(a.Count == 4);
|
|
||||||
Assert.True(a[0].Equals(new NSNumber(true)));
|
|
||||||
Assert.True(a[1].Equals(new NSNumber(false)));
|
|
||||||
Assert.True(a[2].Equals(new NSNumber(87)));
|
|
||||||
Assert.True(a[3].Equals(new NSNumber(3.14159)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestGnuStepASCIIWriting()
|
|
||||||
{
|
|
||||||
var inf = new FileInfo("test-files/test1.plist");
|
|
||||||
var outf = new FileInfo("test-files/out-test1-ascii-gnustep.plist");
|
|
||||||
var x = (NSDictionary)PropertyListParser.Parse(inf);
|
|
||||||
PropertyListParser.SaveAsGnuStepASCII(x, outf);
|
|
||||||
NSObject y = PropertyListParser.Parse(outf);
|
|
||||||
Assert.True(x.Equals(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestWrap()
|
|
||||||
{
|
|
||||||
bool bl = true;
|
|
||||||
byte byt = 24;
|
|
||||||
short shrt = 12;
|
|
||||||
int i = 42;
|
|
||||||
long lng = 30000000000L;
|
|
||||||
float flt = 124.3f;
|
|
||||||
double dbl = 32.0;
|
|
||||||
var date = new DateTime();
|
|
||||||
string strg = "Hello World";
|
|
||||||
|
|
||||||
byte[] bytes =
|
|
||||||
{
|
|
||||||
0x00, 0xAF, 0xAF
|
|
||||||
};
|
|
||||||
|
|
||||||
object[] array =
|
|
||||||
{
|
|
||||||
bl, byt, shrt, i, lng, flt, dbl, date, strg, bytes
|
|
||||||
};
|
|
||||||
|
|
||||||
int[] array2 =
|
|
||||||
{
|
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3000
|
|
||||||
};
|
|
||||||
|
|
||||||
List<object> list = new(array);
|
|
||||||
|
|
||||||
Dictionary<string, object> map = new();
|
|
||||||
map.Add("int", i);
|
|
||||||
map.Add("long", lng);
|
|
||||||
map.Add("date", date);
|
|
||||||
|
|
||||||
List<Dictionary<string,object>> listOfMaps = new()
|
|
||||||
{
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ "int", i },
|
|
||||||
{ "long", lng },
|
|
||||||
{ "date", date }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var WrappedO = NSObject.Wrap((object)bl);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True(WrappedO.ToObject().Equals(bl));
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)byt);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((int)WrappedO.ToObject() == byt);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)shrt);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((int)WrappedO.ToObject() == shrt);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)i);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((int)WrappedO.ToObject() == i);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)lng);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((long)WrappedO.ToObject() == lng);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)flt);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((double)WrappedO.ToObject() == flt);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)dbl);
|
|
||||||
Assert.True(WrappedO is (NSNumber));
|
|
||||||
Assert.True((double)WrappedO.ToObject() == dbl);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap(date);
|
|
||||||
Assert.True(WrappedO is (NSDate));
|
|
||||||
Assert.True(((DateTime)WrappedO.ToObject()).Equals(date));
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap(strg);
|
|
||||||
Assert.True(WrappedO is (NSString));
|
|
||||||
Assert.Equal((string)WrappedO.ToObject(), strg);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)bytes);
|
|
||||||
Assert.True(WrappedO is (NSData));
|
|
||||||
byte[] data = (byte[])WrappedO.ToObject();
|
|
||||||
Assert.True(data.Length == bytes.Length);
|
|
||||||
|
|
||||||
for(int x = 0; x < bytes.Length; x++)
|
|
||||||
Assert.True(data[x] == bytes[x]);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)array);
|
|
||||||
Assert.True(WrappedO is (NSArray));
|
|
||||||
object[] objArray = (object[])WrappedO.ToObject();
|
|
||||||
Assert.True(objArray.Length == array.Length);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap(array2);
|
|
||||||
Assert.True(WrappedO is (NSArray));
|
|
||||||
Assert.True(((NSArray)WrappedO).Count == array2.Length);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)list);
|
|
||||||
Assert.True(WrappedO is (NSArray));
|
|
||||||
objArray = (object[])WrappedO.ToObject();
|
|
||||||
Assert.True(objArray.Length == array.Length);
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)map);
|
|
||||||
Assert.True(WrappedO is (NSDictionary));
|
|
||||||
var dict = (NSDictionary)WrappedO;
|
|
||||||
Assert.True(((NSNumber)dict.ObjectForKey("int")).ToLong() == i);
|
|
||||||
Assert.True(((NSNumber)dict.ObjectForKey("long")).ToLong() == lng);
|
|
||||||
Assert.True(((NSDate)dict.ObjectForKey("date")).Date.Equals(date));
|
|
||||||
|
|
||||||
WrappedO = NSObject.Wrap((object)listOfMaps);
|
|
||||||
Assert.True(WrappedO is (NSArray));
|
|
||||||
var arrayOfMaps = (NSArray)WrappedO;
|
|
||||||
Assert.True(arrayOfMaps.Count == 1);
|
|
||||||
var firstMap = (NSDictionary)arrayOfMaps[0];
|
|
||||||
Assert.True(((NSNumber)firstMap.ObjectForKey("int")).ToLong() == i);
|
|
||||||
Assert.True(((NSNumber)firstMap.ObjectForKey("long")).ToLong() == lng);
|
|
||||||
Assert.True(((NSDate)firstMap.ObjectForKey("date")).Date.Equals(date));
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
/*
|
|
||||||
Object unWrappedO = WrappedO.ToObject();
|
|
||||||
Map map2 = (Map)unWrappedO;
|
|
||||||
Assert.True(((int)map.get("int")) == i);
|
|
||||||
Assert.True(((long)map.get("long")) == lng);
|
|
||||||
Assert.True(((DateTime)map.get("date")).Equals(date));*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the xml reader/writer
|
|
||||||
*/
|
*/
|
||||||
[Fact]
|
/*
|
||||||
public static void TestXml()
|
[Fact]
|
||||||
{
|
public static void TestSet()
|
||||||
// Parse an example plist file
|
{
|
||||||
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1.plist"));
|
NSSet s = new NSSet();
|
||||||
|
s.AddObject(new NSNumber(1));
|
||||||
|
s.AddObject(new NSNumber(3));
|
||||||
|
s.AddObject(new NSNumber(2));
|
||||||
|
|
||||||
// check the data in it
|
NSSet orderedSet = new NSSet(true);
|
||||||
var d = (NSDictionary)x;
|
s.AddObject(new NSNumber(1));
|
||||||
Assert.True(d.Count == 5);
|
s.AddObject(new NSNumber(3));
|
||||||
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
s.AddObject(new NSNumber(2));
|
||||||
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,
|
NSDictionary dict = new NSDictionary();
|
||||||
DateTimeKind.Utc)) ||
|
dict.Add("set1", s);
|
||||||
((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011, 11, 28, 9, 21, 30,
|
dict.Add("set2", orderedSet);
|
||||||
DateTimeKind.Utc)));
|
|
||||||
|
|
||||||
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes, new byte[]
|
PropertyListParser.SaveAsBinary(dict, new FileInfo("test-files/out-testSet.plist"));
|
||||||
|
NSObject ParsedRoot = PropertyListParser.Parse(new FileInfo("test-files/out-testSet.plist"));
|
||||||
|
Assert.True(ParsedRoot.Equals(dict));
|
||||||
|
}*/
|
||||||
|
[Fact]
|
||||||
|
public static void TestASCII()
|
||||||
|
{
|
||||||
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1-ascii.plist"));
|
||||||
|
var d = (NSDictionary)x;
|
||||||
|
Assert.True(d.Count == 5);
|
||||||
|
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).ToString());
|
||||||
|
Assert.Equal("value&B", ((NSString)d.ObjectForKey("key&B")).ToString());
|
||||||
|
|
||||||
|
var actualDate = (NSDate)d.ObjectForKey("date");
|
||||||
|
DateTime expectedDate = new DateTime(2011, 11, 28, 9, 21, 30, DateTimeKind.Utc).ToLocalTime();
|
||||||
|
|
||||||
|
Assert.Equal(actualDate.Date, expectedDate);
|
||||||
|
|
||||||
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
|
[
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
|
]));
|
||||||
|
|
||||||
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
|
Assert.True(a.Count == 4);
|
||||||
|
Assert.True(a[0].Equals(new NSString("YES")));
|
||||||
|
Assert.True(a[1].Equals(new NSString("NO")));
|
||||||
|
Assert.True(a[2].Equals(new NSString("87")));
|
||||||
|
Assert.True(a[3].Equals(new NSString("3.14159")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void testAsciiUtf8CharactersInQuotedString()
|
||||||
|
{
|
||||||
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test-ascii-utf8.plist"));
|
||||||
|
var d = (NSDictionary)x;
|
||||||
|
Assert.Equal(2, d.Count);
|
||||||
|
Assert.Equal("JÔÖú@2x.jpg", d.ObjectForKey("path").ToString());
|
||||||
|
Assert.Equal("QÔÖú@2x 啕.jpg", d.ObjectForKey("Key QÔÖª@2x 䌡").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestASCIIWriting()
|
||||||
|
{
|
||||||
|
var inf = new FileInfo("test-files/test1.plist");
|
||||||
|
var outf = new FileInfo("test-files/out-test1-ascii.plist");
|
||||||
|
var in2 = new FileInfo("test-files/test1-ascii.plist");
|
||||||
|
var x = (NSDictionary)PropertyListParser.Parse(inf);
|
||||||
|
PropertyListParser.SaveAsASCII(x, outf);
|
||||||
|
|
||||||
|
//Information gets lost when saving into the ASCII format (NSNumbers are converted to NSStrings)
|
||||||
|
|
||||||
|
var y = (NSDictionary)PropertyListParser.Parse(outf);
|
||||||
|
var z = (NSDictionary)PropertyListParser.Parse(in2);
|
||||||
|
Assert.True(y.Equals(z));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the binary reader/writer.
|
||||||
|
*/
|
||||||
|
[Fact]
|
||||||
|
public static void TestBinary()
|
||||||
|
{
|
||||||
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1.plist"));
|
||||||
|
|
||||||
|
// save and load as binary
|
||||||
|
PropertyListParser.SaveAsBinary(x, new FileInfo("test-files/out-testBinary.plist"));
|
||||||
|
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testBinary.plist"));
|
||||||
|
Assert.True(x.Equals(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestGnuStepASCII()
|
||||||
|
{
|
||||||
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1-ascii-gnustep.plist"));
|
||||||
|
var d = (NSDictionary)x;
|
||||||
|
Assert.True(d.Count == 5);
|
||||||
|
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).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, DateTimeKind.Utc)
|
||||||
|
.ToLocalTime()));
|
||||||
|
|
||||||
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
|
[
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
|
]));
|
||||||
|
|
||||||
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
|
Assert.True(a.Count == 4);
|
||||||
|
Assert.True(a[0].Equals(new NSNumber(true)));
|
||||||
|
Assert.True(a[1].Equals(new NSNumber(false)));
|
||||||
|
Assert.True(a[2].Equals(new NSNumber(87)));
|
||||||
|
Assert.True(a[3].Equals(new NSNumber(3.14159)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestGnuStepASCIIWriting()
|
||||||
|
{
|
||||||
|
var inf = new FileInfo("test-files/test1.plist");
|
||||||
|
var outf = new FileInfo("test-files/out-test1-ascii-gnustep.plist");
|
||||||
|
var x = (NSDictionary)PropertyListParser.Parse(inf);
|
||||||
|
PropertyListParser.SaveAsGnuStepASCII(x, outf);
|
||||||
|
NSObject y = PropertyListParser.Parse(outf);
|
||||||
|
Assert.True(x.Equals(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestWrap()
|
||||||
|
{
|
||||||
|
bool bl = true;
|
||||||
|
byte byt = 24;
|
||||||
|
short shrt = 12;
|
||||||
|
int i = 42;
|
||||||
|
long lng = 30000000000L;
|
||||||
|
float flt = 124.3f;
|
||||||
|
double dbl = 32.0;
|
||||||
|
var date = new DateTime();
|
||||||
|
string strg = "Hello World";
|
||||||
|
|
||||||
|
byte[] bytes =
|
||||||
|
[
|
||||||
|
0x00, 0xAF, 0xAF
|
||||||
|
];
|
||||||
|
|
||||||
|
object[] array =
|
||||||
|
[
|
||||||
|
bl, byt, shrt, i, lng, flt, dbl, date, strg, bytes
|
||||||
|
];
|
||||||
|
|
||||||
|
int[] array2 =
|
||||||
|
[
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3000
|
||||||
|
];
|
||||||
|
|
||||||
|
List<object> list = new(array);
|
||||||
|
|
||||||
|
Dictionary<string, object> map = new();
|
||||||
|
map.Add("int", i);
|
||||||
|
map.Add("long", lng);
|
||||||
|
map.Add("date", date);
|
||||||
|
|
||||||
|
List<Dictionary<string, object>> listOfMaps =
|
||||||
|
[
|
||||||
|
new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
{
|
||||||
}));
|
"int", i
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"long", lng
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date", date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
var a = (NSArray)d.ObjectForKey("array");
|
var WrappedO = NSObject.Wrap((object)bl);
|
||||||
Assert.True(a.Count == 4);
|
Assert.True(WrappedO is (NSNumber));
|
||||||
Assert.True(a[0].Equals(new NSNumber(true)));
|
Assert.True(WrappedO.ToObject().Equals(bl));
|
||||||
Assert.True(a[1].Equals(new NSNumber(false)));
|
|
||||||
Assert.True(a[2].Equals(new NSNumber(87)));
|
|
||||||
Assert.True(a[3].Equals(new NSNumber(3.14159)));
|
|
||||||
|
|
||||||
// read/write it, make sure we get the same thing
|
WrappedO = NSObject.Wrap((object)byt);
|
||||||
PropertyListParser.SaveAsXml(x, new FileInfo("test-files/out-testXml.plist"));
|
Assert.True(WrappedO is (NSNumber));
|
||||||
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testXml.plist"));
|
Assert.True((int)WrappedO.ToObject() == byt);
|
||||||
Assert.True(x.Equals(y));
|
|
||||||
}
|
WrappedO = NSObject.Wrap((object)shrt);
|
||||||
|
Assert.True(WrappedO is (NSNumber));
|
||||||
|
Assert.True((int)WrappedO.ToObject() == shrt);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)i);
|
||||||
|
Assert.True(WrappedO is (NSNumber));
|
||||||
|
Assert.True((int)WrappedO.ToObject() == i);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)lng);
|
||||||
|
Assert.True(WrappedO is (NSNumber));
|
||||||
|
Assert.True((long)WrappedO.ToObject() == lng);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)flt);
|
||||||
|
Assert.True(WrappedO is (NSNumber));
|
||||||
|
Assert.True((double)WrappedO.ToObject() == flt);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)dbl);
|
||||||
|
Assert.True(WrappedO is (NSNumber));
|
||||||
|
Assert.True((double)WrappedO.ToObject() == dbl);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap(date);
|
||||||
|
Assert.True(WrappedO is (NSDate));
|
||||||
|
Assert.True(((DateTime)WrappedO.ToObject()).Equals(date));
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap(strg);
|
||||||
|
Assert.True(WrappedO is (NSString));
|
||||||
|
Assert.Equal((string)WrappedO.ToObject(), strg);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)bytes);
|
||||||
|
Assert.True(WrappedO is (NSData));
|
||||||
|
byte[] data = (byte[])WrappedO.ToObject();
|
||||||
|
Assert.True(data.Length == bytes.Length);
|
||||||
|
|
||||||
|
for(int x = 0; x < bytes.Length; x++) Assert.True(data[x] == bytes[x]);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)array);
|
||||||
|
Assert.True(WrappedO is (NSArray));
|
||||||
|
object[] objArray = (object[])WrappedO.ToObject();
|
||||||
|
Assert.True(objArray.Length == array.Length);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap(array2);
|
||||||
|
Assert.True(WrappedO is (NSArray));
|
||||||
|
Assert.True(((NSArray)WrappedO).Count == array2.Length);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)list);
|
||||||
|
Assert.True(WrappedO is (NSArray));
|
||||||
|
objArray = (object[])WrappedO.ToObject();
|
||||||
|
Assert.True(objArray.Length == array.Length);
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap((object)map);
|
||||||
|
Assert.True(WrappedO is (NSDictionary));
|
||||||
|
var dict = (NSDictionary)WrappedO;
|
||||||
|
Assert.True(((NSNumber)dict.ObjectForKey("int")).ToLong() == i);
|
||||||
|
Assert.True(((NSNumber)dict.ObjectForKey("long")).ToLong() == lng);
|
||||||
|
Assert.True(((NSDate)dict.ObjectForKey("date")).Date.Equals(date));
|
||||||
|
|
||||||
|
WrappedO = NSObject.Wrap(listOfMaps);
|
||||||
|
Assert.True(WrappedO is (NSArray));
|
||||||
|
var arrayOfMaps = (NSArray)WrappedO;
|
||||||
|
Assert.True(arrayOfMaps.Count == 1);
|
||||||
|
var firstMap = (NSDictionary)arrayOfMaps[0];
|
||||||
|
Assert.True(((NSNumber)firstMap.ObjectForKey("int")).ToLong() == i);
|
||||||
|
Assert.True(((NSNumber)firstMap.ObjectForKey("long")).ToLong() == lng);
|
||||||
|
Assert.True(((NSDate)firstMap.ObjectForKey("date")).Date.Equals(date));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*
|
||||||
|
Object unWrappedO = WrappedO.ToObject();
|
||||||
|
Map map2 = (Map)unWrappedO;
|
||||||
|
Assert.True(((int)map.get("int")) == i);
|
||||||
|
Assert.True(((long)map.get("long")) == lng);
|
||||||
|
Assert.True(((DateTime)map.get("date")).Equals(date));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the xml reader/writer
|
||||||
|
*/
|
||||||
|
[Fact]
|
||||||
|
public static void TestXml()
|
||||||
|
{
|
||||||
|
// Parse an example plist file
|
||||||
|
NSObject x = PropertyListParser.Parse(new FileInfo("test-files/test1.plist"));
|
||||||
|
|
||||||
|
// check the data in it
|
||||||
|
var d = (NSDictionary)x;
|
||||||
|
Assert.True(d.Count == 5);
|
||||||
|
Assert.Equal("valueA", ((NSString)d.ObjectForKey("keyA")).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,
|
||||||
|
DateTimeKind.Utc)) ||
|
||||||
|
((NSDate)d.ObjectForKey("date")).Date.Equals(new DateTime(2011,
|
||||||
|
11,
|
||||||
|
28,
|
||||||
|
9,
|
||||||
|
21,
|
||||||
|
30,
|
||||||
|
DateTimeKind.Utc)));
|
||||||
|
|
||||||
|
Assert.True(ArrayEquals(((NSData)d.ObjectForKey("data")).Bytes,
|
||||||
|
[
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x10, 0x41, 0x08, 0x20, 0x82
|
||||||
|
]));
|
||||||
|
|
||||||
|
var a = (NSArray)d.ObjectForKey("array");
|
||||||
|
Assert.True(a.Count == 4);
|
||||||
|
Assert.True(a[0].Equals(new NSNumber(true)));
|
||||||
|
Assert.True(a[1].Equals(new NSNumber(false)));
|
||||||
|
Assert.True(a[2].Equals(new NSNumber(87)));
|
||||||
|
Assert.True(a[3].Equals(new NSNumber(3.14159)));
|
||||||
|
|
||||||
|
// read/write it, make sure we get the same thing
|
||||||
|
PropertyListParser.SaveAsXml(x, new FileInfo("test-files/out-testXml.plist"));
|
||||||
|
NSObject y = PropertyListParser.Parse(new FileInfo("test-files/out-testXml.plist"));
|
||||||
|
Assert.True(x.Equals(y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,19 +2,18 @@
|
|||||||
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();
|
|
||||||
|
|
||||||
PropertyListParser.Parse(stream);
|
PropertyListParser.Parse(stream);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void ParseEmptyStreamTest() =>
|
|
||||||
Assert.Throws<PropertyListFormatException>(ParseEmptyStreamTestDelegate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void ParseEmptyStreamTest() =>
|
||||||
|
Assert.Throws<PropertyListFormatException>(ParseEmptyStreamTestDelegate);
|
||||||
}
|
}
|
||||||
@@ -2,131 +2,140 @@
|
|||||||
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
|
||||||
public void UidFromArrayTest(byte[] array)
|
})]
|
||||||
{
|
public void UidFromArrayTest(byte[] array)
|
||||||
var uid = new UID(array);
|
{
|
||||||
Assert.Equal(array, uid.Bytes);
|
var uid = new UID(array);
|
||||||
}
|
Assert.Equal(array, uid.Bytes);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void BinaryRoundTripTest()
|
public void BinaryRoundTripTest()
|
||||||
{
|
{
|
||||||
var original = new UID(0xabcd);
|
var original = new UID(0xabcd);
|
||||||
|
|
||||||
using var stream = new MemoryStream();
|
using var stream = new MemoryStream();
|
||||||
|
|
||||||
BinaryPropertyListWriter.Write(stream, original);
|
BinaryPropertyListWriter.Write(stream, original);
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
var roundtrip = BinaryPropertyListParser.Parse(stream) as UID;
|
var roundtrip = BinaryPropertyListParser.Parse(stream) as UID;
|
||||||
Assert.Equal(original.Bytes, roundtrip.Bytes);
|
Assert.Equal(original.Bytes, roundtrip.Bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ByteUidTest()
|
public void ByteUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xAB);
|
var uid = new UID(0xAB);
|
||||||
|
|
||||||
Assert.Equal(new byte[]
|
Assert.Equal(new byte[]
|
||||||
{
|
{
|
||||||
0xAB
|
0xAB
|
||||||
}, uid.Bytes);
|
},
|
||||||
|
uid.Bytes);
|
||||||
|
|
||||||
Assert.Equal(0xABu, uid.ToUInt64());
|
Assert.Equal(0xABu, uid.ToUInt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IntUidTest()
|
public void IntUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xABCDEF00);
|
var uid = new UID(0xABCDEF00);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LongUidTest()
|
public void LongUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xABCDEF0000EFCDAB);
|
var uid = new UID(0xABCDEF0000EFCDAB);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UIntUidTest()
|
public void UIntUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xABCDEF00u);
|
var uid = new UID(0xABCDEF00u);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ULongUidTest()
|
public void ULongUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xABCDEF0000EFCDABu);
|
var uid = new UID(0xABCDEF0000EFCDABu);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UShortUidTest()
|
public void UShortUidTest()
|
||||||
{
|
{
|
||||||
var uid = new UID(0xABCDu);
|
var uid = new UID(0xABCDu);
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void XmlRoundTripTest()
|
public void XmlRoundTripTest()
|
||||||
{
|
{
|
||||||
var original = new UID(0xabcd);
|
var original = new UID(0xabcd);
|
||||||
|
|
||||||
string plist = original.ToXmlPropertyList();
|
string plist = original.ToXmlPropertyList();
|
||||||
|
|
||||||
// UIDs don't exist in XML property lists, but they are represented as dictionaries
|
// UIDs don't exist in XML property lists, but they are represented as dictionaries
|
||||||
// for compability purposes
|
// for compability purposes
|
||||||
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,93 +9,91 @@ 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>
|
readonly Stream expectedOutput;
|
||||||
/// A <see cref="Stream" /> which writes its output to a <see cref="Stream" /> and validates that the data which
|
readonly Stream output;
|
||||||
/// is being written to the output stream matches the data in a reference stream.
|
|
||||||
/// </summary>
|
/// <summary>Initializes a new instance of the <see cref="ValidatingCompositeStream" /> class.</summary>
|
||||||
internal class ValidatingStream : Stream
|
/// <param name="output">The <see cref="Stream" /> to which to write data.</param>
|
||||||
|
/// <param name="expectedOutput">The reference stream for <paramref name="output" />.</param>
|
||||||
|
public ValidatingStream(Stream output, Stream expectedOutput)
|
||||||
{
|
{
|
||||||
readonly Stream expectedOutput;
|
this.output = output ?? throw new ArgumentNullException(nameof(output));
|
||||||
readonly Stream output;
|
this.expectedOutput = expectedOutput ?? throw new ArgumentNullException(nameof(expectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="ValidatingCompositeStream" /> class.</summary>
|
/// <inheritdoc />
|
||||||
/// <param name="output">The <see cref="Stream" /> to which to write data.</param>
|
public override bool CanRead => false;
|
||||||
/// <param name="expectedOutput">The reference stream for <paramref name="output" />.</param>
|
|
||||||
public ValidatingStream(Stream output, Stream expectedOutput)
|
|
||||||
{
|
|
||||||
this.output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
this.expectedOutput = expectedOutput ?? throw new ArgumentNullException(nameof(expectedOutput));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanRead => false;
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanSeek => false;
|
public override bool CanWrite => true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanWrite => true;
|
public override long Length => output.Length;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override long Length => output.Length;
|
public override long Position
|
||||||
|
{
|
||||||
|
get => output.Position;
|
||||||
|
set => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override long Position
|
public override void Flush() => output.Flush();
|
||||||
{
|
|
||||||
get => output.Position;
|
|
||||||
set => throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Flush() => output.Flush();
|
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
|
||||||
|
throw new NotSupportedException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<int>
|
public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
|
||||||
ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
|
|
||||||
throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
|
public override void SetLength(long value) => throw new NotImplementedException();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void SetLength(long value) => throw new NotImplementedException();
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
byte[] expected = new byte[buffer.Length];
|
||||||
|
expectedOutput.Read(expected, offset, count);
|
||||||
|
|
||||||
/// <inheritdoc />
|
byte[] bufferChunk = buffer.Skip(offset).Take(count).ToArray();
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
byte[] expectedChunk = expected.Skip(offset).Take(count).ToArray();
|
||||||
{
|
|
||||||
byte[] expected = new byte[buffer.Length];
|
|
||||||
expectedOutput.Read(expected, offset, count);
|
|
||||||
|
|
||||||
byte[] bufferChunk = buffer.Skip(offset).Take(count).ToArray();
|
// Make sure the data being writen matches the data which was written to the expected stream.
|
||||||
byte[] expectedChunk = expected.Skip(offset).Take(count).ToArray();
|
// This will detect any errors as the invalid data is being written out - as opposed to post-
|
||||||
|
// test binary validation.
|
||||||
|
Assert.Equal(expectedChunk, bufferChunk);
|
||||||
|
output.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the data being writen matches the data which was written to the expected stream.
|
/// <inheritdoc />
|
||||||
// This will detect any errors as the invalid data is being written out - as opposed to post-
|
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
// test binary validation.
|
{
|
||||||
Assert.Equal(expectedChunk, bufferChunk);
|
byte[] expected = new byte[buffer.Length];
|
||||||
output.Write(buffer, offset, count);
|
await expectedOutput.ReadAsync(expected, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
byte[] bufferChunk = buffer.Skip(offset).Take(count).ToArray();
|
||||||
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
byte[] expectedChunk = expected.Skip(offset).Take(count).ToArray();
|
||||||
{
|
|
||||||
byte[] expected = new byte[buffer.Length];
|
|
||||||
await expectedOutput.ReadAsync(expected, offset, count, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
byte[] bufferChunk = buffer.Skip(offset).Take(count).ToArray();
|
// Make sure the data being writen matches the data which was written to the expected stream.
|
||||||
byte[] expectedChunk = expected.Skip(offset).Take(count).ToArray();
|
// This will detect any errors as the invalid data is being written out - as opposed to post-
|
||||||
|
// test binary validation.
|
||||||
|
Assert.Equal(expectedChunk, bufferChunk);
|
||||||
|
|
||||||
// Make sure the data being writen matches the data which was written to the expected stream.
|
await output.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
// This will detect any errors as the invalid data is being written out - as opposed to post-
|
|
||||||
// test binary validation.
|
|
||||||
Assert.Equal(expectedChunk, bufferChunk);
|
|
||||||
|
|
||||||
await output.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,123 +3,122 @@ 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
|
||||||
|
private static readonly object _testLock = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestPassiveDefaultPreprocessorsRegistered()
|
||||||
{
|
{
|
||||||
// lock tests to make sure temporarily added / replaced preprocessors don't interfere with the other tests in this suite
|
byte[] testByteArray = [0x1, 0x2, 0x4, 0x8];
|
||||||
private static readonly object _testLock = new();
|
|
||||||
|
|
||||||
[Fact]
|
Assert.Equal(true, ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
public static void TestPassiveDefaultPreprocessorsRegistered()
|
Assert.Equal(false, ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
|
Assert.Equal("true", ValuePreprocessor.Preprocess("true", ValuePreprocessor.Type.BOOL));
|
||||||
|
|
||||||
|
Assert.Equal("42", ValuePreprocessor.Preprocess("42", ValuePreprocessor.Type.INTEGER));
|
||||||
|
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.INTEGER));
|
||||||
|
|
||||||
|
Assert.Equal("3.14159", ValuePreprocessor.Preprocess("3.14159", ValuePreprocessor.Type.FLOATING_POINT));
|
||||||
|
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.FLOATING_POINT));
|
||||||
|
|
||||||
|
Assert.Equal("2.71828", ValuePreprocessor.Preprocess("2.71828", ValuePreprocessor.Type.UNDEFINED_NUMBER));
|
||||||
|
|
||||||
|
Assert.Equal("TestString", ValuePreprocessor.Preprocess("TestString", ValuePreprocessor.Type.STRING));
|
||||||
|
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.STRING));
|
||||||
|
|
||||||
|
Assert.Equal("TestData", ValuePreprocessor.Preprocess("TestData", ValuePreprocessor.Type.DATA));
|
||||||
|
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.DATA));
|
||||||
|
|
||||||
|
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.DATE));
|
||||||
|
Assert.Equal("01.02.1903", ValuePreprocessor.Preprocess("01.02.1903", ValuePreprocessor.Type.DATE));
|
||||||
|
Assert.Equal(23.0, ValuePreprocessor.Preprocess(23.0, ValuePreprocessor.Type.DATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestRegisterPreprocessor()
|
||||||
|
{
|
||||||
|
lock(_testLock)
|
||||||
{
|
{
|
||||||
byte[] testByteArray = [0x1, 0x2, 0x4, 0x8];
|
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||||
|
string testString = "TestString";
|
||||||
|
string expected = "gnirtStseT";
|
||||||
|
|
||||||
Assert.Equal(true, ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
var testType = (ValuePreprocessor.Type)42;
|
||||||
Assert.Equal(false, ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
|
||||||
Assert.Equal("true", ValuePreprocessor.Preprocess("true", ValuePreprocessor.Type.BOOL));
|
|
||||||
|
|
||||||
Assert.Equal("42", ValuePreprocessor.Preprocess("42", ValuePreprocessor.Type.INTEGER));
|
ValuePreprocessor.Set(examplePreprocessor, testType);
|
||||||
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.INTEGER));
|
string actual = ValuePreprocessor.Preprocess(testString, testType);
|
||||||
|
|
||||||
Assert.Equal("3.14159", ValuePreprocessor.Preprocess("3.14159", ValuePreprocessor.Type.FLOATING_POINT));
|
Assert.Equal(actual, expected);
|
||||||
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.FLOATING_POINT));
|
|
||||||
|
|
||||||
Assert.Equal("2.71828", ValuePreprocessor.Preprocess("2.71828", ValuePreprocessor.Type.UNDEFINED_NUMBER));
|
ValuePreprocessor.Unset<string>(testType);
|
||||||
|
|
||||||
Assert.Equal("TestString", ValuePreprocessor.Preprocess("TestString", ValuePreprocessor.Type.STRING));
|
|
||||||
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.STRING));
|
|
||||||
|
|
||||||
Assert.Equal("TestData", ValuePreprocessor.Preprocess("TestData", ValuePreprocessor.Type.DATA));
|
|
||||||
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.DATA));
|
|
||||||
|
|
||||||
Assert.Equal(testByteArray, ValuePreprocessor.Preprocess(testByteArray, ValuePreprocessor.Type.DATE));
|
|
||||||
Assert.Equal("01.02.1903", ValuePreprocessor.Preprocess("01.02.1903", ValuePreprocessor.Type.DATE));
|
|
||||||
Assert.Equal(23.0, ValuePreprocessor.Preprocess(23.0, ValuePreprocessor.Type.DATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestRegisterPreprocessor()
|
|
||||||
{
|
|
||||||
lock(_testLock)
|
|
||||||
{
|
|
||||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
|
||||||
string testString = "TestString";
|
|
||||||
string expected = "gnirtStseT";
|
|
||||||
|
|
||||||
var testType = (ValuePreprocessor.Type)42;
|
|
||||||
|
|
||||||
ValuePreprocessor.Set(examplePreprocessor, testType);
|
|
||||||
string actual = ValuePreprocessor.Preprocess(testString, testType);
|
|
||||||
|
|
||||||
Assert.Equal(actual, expected);
|
|
||||||
|
|
||||||
ValuePreprocessor.Unset<string>(testType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestRegisteredPreprocessorSelection1()
|
|
||||||
{
|
|
||||||
lock(_testLock)
|
|
||||||
{
|
|
||||||
Func<short, short> examplePreprocessor = value => (short)(value - 1);
|
|
||||||
short testShort = 42;
|
|
||||||
string testString = "TestString";
|
|
||||||
|
|
||||||
var testType = (ValuePreprocessor.Type)42;
|
|
||||||
|
|
||||||
// correct value type, differing data type
|
|
||||||
ValuePreprocessor.Set(examplePreprocessor, testType);
|
|
||||||
ValuePreprocessor.Set(ValuePreprocessor.GetDefault<string>(), testType);
|
|
||||||
|
|
||||||
string actual1 = ValuePreprocessor.Preprocess(testString, testType);
|
|
||||||
short actual2 = ValuePreprocessor.Preprocess(testShort, testType);
|
|
||||||
|
|
||||||
// assert unchanged, since the selected preprocessor != tested preprocessor
|
|
||||||
Assert.Equal(actual1, testString);
|
|
||||||
Assert.NotEqual(actual2, testShort);
|
|
||||||
|
|
||||||
ValuePreprocessor.Remove<short>(testType);
|
|
||||||
ValuePreprocessor.Remove<string>(testType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestRegisteredPreprocessorSelection2()
|
|
||||||
{
|
|
||||||
lock(_testLock)
|
|
||||||
{
|
|
||||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
|
||||||
byte[] testByteArray = [0x42,];
|
|
||||||
string testString = "TestString";
|
|
||||||
|
|
||||||
var testType = (ValuePreprocessor.Type)42;
|
|
||||||
|
|
||||||
// correct value type, differing data type
|
|
||||||
ValuePreprocessor.Set(examplePreprocessor, testType);
|
|
||||||
ValuePreprocessor.Set(ValuePreprocessor.GetDefault<byte[]>(), testType);
|
|
||||||
|
|
||||||
string actual1 = ValuePreprocessor.Preprocess(testString, testType);
|
|
||||||
byte[] actual2 = ValuePreprocessor.Preprocess(testByteArray, testType);
|
|
||||||
|
|
||||||
Assert.NotEqual(actual1, testString);
|
|
||||||
|
|
||||||
// assert unchanged, since the selected preprocessor != tested preprocessor
|
|
||||||
Assert.Equal(actual2, testByteArray);
|
|
||||||
|
|
||||||
ValuePreprocessor.Unset<string>(testType);
|
|
||||||
ValuePreprocessor.Remove<byte[]>(testType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void TestUnregisteredPreprocessorThrows()
|
|
||||||
{
|
|
||||||
int[] testArray = [1, 2, 4, 8];
|
|
||||||
|
|
||||||
// there's no registered preprocessor for byte array arguments for STRING
|
|
||||||
Assert.Throws<ArgumentException>(() => ValuePreprocessor.Preprocess(testArray, ValuePreprocessor.Type.STRING));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestRegisteredPreprocessorSelection1()
|
||||||
|
{
|
||||||
|
lock(_testLock)
|
||||||
|
{
|
||||||
|
Func<short, short> examplePreprocessor = value => (short)(value - 1);
|
||||||
|
short testShort = 42;
|
||||||
|
string testString = "TestString";
|
||||||
|
|
||||||
|
var testType = (ValuePreprocessor.Type)42;
|
||||||
|
|
||||||
|
// correct value type, differing data type
|
||||||
|
ValuePreprocessor.Set(examplePreprocessor, testType);
|
||||||
|
ValuePreprocessor.Set(ValuePreprocessor.GetDefault<string>(), testType);
|
||||||
|
|
||||||
|
string actual1 = ValuePreprocessor.Preprocess(testString, testType);
|
||||||
|
short actual2 = ValuePreprocessor.Preprocess(testShort, testType);
|
||||||
|
|
||||||
|
// assert unchanged, since the selected preprocessor != tested preprocessor
|
||||||
|
Assert.Equal(actual1, testString);
|
||||||
|
Assert.NotEqual(actual2, testShort);
|
||||||
|
|
||||||
|
ValuePreprocessor.Remove<short>(testType);
|
||||||
|
ValuePreprocessor.Remove<string>(testType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestRegisteredPreprocessorSelection2()
|
||||||
|
{
|
||||||
|
lock(_testLock)
|
||||||
|
{
|
||||||
|
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||||
|
byte[] testByteArray = [0x42];
|
||||||
|
string testString = "TestString";
|
||||||
|
|
||||||
|
var testType = (ValuePreprocessor.Type)42;
|
||||||
|
|
||||||
|
// correct value type, differing data type
|
||||||
|
ValuePreprocessor.Set(examplePreprocessor, testType);
|
||||||
|
ValuePreprocessor.Set(ValuePreprocessor.GetDefault<byte[]>(), testType);
|
||||||
|
|
||||||
|
string actual1 = ValuePreprocessor.Preprocess(testString, testType);
|
||||||
|
byte[] actual2 = ValuePreprocessor.Preprocess(testByteArray, testType);
|
||||||
|
|
||||||
|
Assert.NotEqual(actual1, testString);
|
||||||
|
|
||||||
|
// assert unchanged, since the selected preprocessor != tested preprocessor
|
||||||
|
Assert.Equal(actual2, testByteArray);
|
||||||
|
|
||||||
|
ValuePreprocessor.Unset<string>(testType);
|
||||||
|
ValuePreprocessor.Remove<byte[]>(testType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void TestUnregisteredPreprocessorThrows()
|
||||||
|
{
|
||||||
|
int[] testArray = [1, 2, 4, 8];
|
||||||
|
|
||||||
|
// there's no registered preprocessor for byte array arguments for 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,80 +1,80 @@
|
|||||||
<?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>
|
||||||
<data>
|
<data>
|
||||||
n57qDP4tZfLD1rCS43W0B4LQjzE=
|
n57qDP4tZfLD1rCS43W0B4LQjzE=
|
||||||
</data>
|
</data>
|
||||||
<key>icon.png</key>
|
<key>icon.png</key>
|
||||||
<data>
|
<data>
|
||||||
EUOeOW/HpmiAZeEGzJm8j3hE6vo=
|
EUOeOW/HpmiAZeEGzJm8j3hE6vo=
|
||||||
</data>
|
</data>
|
||||||
</dict>
|
</dict>
|
||||||
<key>files2</key>
|
<key>files2</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>PkgInfo</key>
|
<key>PkgInfo</key>
|
||||||
<data>
|
<data>
|
||||||
n57qDP4tZfLD1rCS43W0B4LQjzE=
|
n57qDP4tZfLD1rCS43W0B4LQjzE=
|
||||||
</data>
|
</data>
|
||||||
<key>icon.png</key>
|
<key>icon.png</key>
|
||||||
<data>
|
<data>
|
||||||
EUOeOW/HpmiAZeEGzJm8j3hE6vo=
|
EUOeOW/HpmiAZeEGzJm8j3hE6vo=
|
||||||
</data>
|
</data>
|
||||||
</dict>
|
</dict>
|
||||||
<key>rules</key>
|
<key>rules</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>.*</key>
|
<key>.*</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>Info.plist</key>
|
<key>Info.plist</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>omit</key>
|
<key>omit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<real>10</real>
|
<real>10</real>
|
||||||
</dict>
|
</dict>
|
||||||
<key>ResourceRules.plist</key>
|
<key>ResourceRules.plist</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>omit</key>
|
<key>omit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<real>100</real>
|
<real>100</real>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>rules2</key>
|
<key>rules2</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>.*</key>
|
<key>.*</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>Info.plist</key>
|
<key>Info.plist</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>omit</key>
|
<key>omit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<real>10</real>
|
<real>10</real>
|
||||||
</dict>
|
</dict>
|
||||||
<key>ResourceRules.plist</key>
|
<key>ResourceRules.plist</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>omit</key>
|
<key>omit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<real>100</real>
|
<real>100</real>
|
||||||
</dict>
|
</dict>
|
||||||
<key>^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/</key>
|
<key>^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>nested</key>
|
<key>nested</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<real>0.0</real>
|
<real>0.0</real>
|
||||||
</dict>
|
</dict>
|
||||||
<key>^[^/]+$</key>
|
<key>^[^/]+$</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>top</key>
|
<key>top</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>weight</key>
|
<key>weight</key>
|
||||||
<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>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,30 @@
|
|||||||
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>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
class AddObjectEqualityComparer : EqualityComparer<NSObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
public override bool Equals(NSObject x, NSObject y)
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
class AddObjectEqualityComparer : EqualityComparer<NSObject>
|
|
||||||
{
|
{
|
||||||
public override bool Equals(NSObject x, NSObject y)
|
if(x is not NSString a || y is not NSString b) return ReferenceEquals(x, y);
|
||||||
{
|
|
||||||
if(x is not NSString a ||
|
|
||||||
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,39 +1,43 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Claunia.PropertyList
|
namespace Claunia.PropertyList;
|
||||||
{
|
|
||||||
public partial class BinaryPropertyListWriter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The equality comparer which is used when retrieving objects in the
|
|
||||||
/// <see cref="BinaryPropertyListWriter.idMap" />. The logic is slightly different from
|
|
||||||
/// <see cref="AddObjectEqualityComparer" />, results in two equivalent objects (UIDs mainly) being added to the
|
|
||||||
/// <see cref="BinaryPropertyListWriter.idMap" />. Whenever the ID for one of those equivalent objects is requested,
|
|
||||||
/// the first ID is always returned. This means that there are "orphan" objects in binary property lists - duplicate
|
|
||||||
/// objects which are never referenced -; this logic exists purely to maintain binary compatibility with Apple's
|
|
||||||
/// format.
|
|
||||||
/// </summary>
|
|
||||||
class GetObjectEqualityComparer : EqualityComparer<NSObject>
|
|
||||||
{
|
|
||||||
public override bool Equals(NSObject x, NSObject y) => x switch
|
|
||||||
{
|
|
||||||
// By default, use reference equality. Even if there are two objects - say a NSString - with the same
|
|
||||||
// value, do not consider them equal unless they are the same instance of NSString.
|
|
||||||
// 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".
|
|
||||||
UID => x.Equals(y),
|
|
||||||
NSNumber number when IsSerializationPrimitive(number) => number.Equals(y),
|
|
||||||
NSString nsString when IsSerializationPrimitive(nsString) => nsString.Equals(y),
|
|
||||||
_ => ReferenceEquals(x, y)
|
|
||||||
};
|
|
||||||
|
|
||||||
public override int GetHashCode(NSObject obj) => obj switch
|
public partial class BinaryPropertyListWriter
|
||||||
{
|
{
|
||||||
UID u => u.GetHashCode(),
|
/// <summary>
|
||||||
NSNumber n when IsSerializationPrimitive(n) => n.ToObject().GetHashCode(),
|
/// The equality comparer which is used when retrieving objects in the
|
||||||
NSString s when IsSerializationPrimitive(s) => s.Content.GetHashCode(),
|
/// <see cref="BinaryPropertyListWriter.idMap" />. The logic is slightly different from
|
||||||
_ => obj.GetHashCode()
|
/// <see cref="AddObjectEqualityComparer" />, results in two equivalent objects (UIDs mainly) being added to the
|
||||||
};
|
/// <see cref="BinaryPropertyListWriter.idMap" />. Whenever the ID for one of those equivalent objects is requested,
|
||||||
}
|
/// the first ID is always returned. This means that there are "orphan" objects in binary property lists - duplicate
|
||||||
|
/// objects which are never referenced -; this logic exists purely to maintain binary compatibility with Apple's
|
||||||
|
/// format.
|
||||||
|
/// </summary>
|
||||||
|
class GetObjectEqualityComparer : EqualityComparer<NSObject>
|
||||||
|
{
|
||||||
|
public override bool Equals(NSObject x, NSObject y) => x switch
|
||||||
|
{
|
||||||
|
// By default, use reference equality. Even if there are two objects - say a NSString - with the same
|
||||||
|
// value, do not consider them equal unless they are the same instance of NSString.
|
||||||
|
// 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".
|
||||||
|
UID => x.Equals(y),
|
||||||
|
NSNumber number when IsSerializationPrimitive(number)
|
||||||
|
=> number.Equals(y),
|
||||||
|
NSString nsString when
|
||||||
|
IsSerializationPrimitive(nsString) => nsString
|
||||||
|
.Equals(y),
|
||||||
|
_ => ReferenceEquals(x, y)
|
||||||
|
};
|
||||||
|
|
||||||
|
public override int GetHashCode(NSObject obj) => obj switch
|
||||||
|
{
|
||||||
|
UID u => u.GetHashCode(),
|
||||||
|
NSNumber n when IsSerializationPrimitive(n) => n.ToObject()
|
||||||
|
.GetHashCode(),
|
||||||
|
NSString s when IsSerializationPrimitive(s) => s.Content
|
||||||
|
.GetHashCode(),
|
||||||
|
_ => obj.GetHashCode()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,396 +27,386 @@ 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>
|
/// <summary>Binary property list version 0.0</summary>
|
||||||
/// <para>A BinaryPropertyListWriter is a helper class for writing out binary property list files.</para>
|
public const int VERSION_00 = 0;
|
||||||
/// <para>
|
/// <summary>Binary property list version 1.0</summary>
|
||||||
/// It contains an output stream and various structures for keeping track of which NSObjects have already been
|
public const int VERSION_10 = 10;
|
||||||
/// serialized, and where they were put in the file.
|
/// <summary>Binary property list version 1.5</summary>
|
||||||
/// </para>
|
public const int VERSION_15 = 15;
|
||||||
/// </summary>
|
/// <summary>Binary property list version 2.0</summary>
|
||||||
/// @author Keith Randall
|
public const int VERSION_20 = 20;
|
||||||
/// @author Natalia Portillo
|
|
||||||
public partial class BinaryPropertyListWriter
|
// map from object to its ID
|
||||||
|
protected readonly Dictionary<NSObject, int> idDict = new(new AddObjectEqualityComparer());
|
||||||
|
protected readonly Dictionary<NSObject, int> idDict2 = new(new GetObjectEqualityComparer());
|
||||||
|
|
||||||
|
// raw output stream to result file
|
||||||
|
readonly Stream outStream;
|
||||||
|
|
||||||
|
readonly int version = VERSION_00;
|
||||||
|
|
||||||
|
// # of bytes written so far
|
||||||
|
long count;
|
||||||
|
protected int currentId;
|
||||||
|
int idSizeInBytes;
|
||||||
|
|
||||||
|
/// <summary>Creates a new binary property list writer</summary>
|
||||||
|
/// <param name="outStr">The output stream into which the binary property list will be written</param>
|
||||||
|
/// <exception cref="IOException">If an error occured while writing to the stream</exception>
|
||||||
|
public BinaryPropertyListWriter(Stream outStr) => outStream = outStr;
|
||||||
|
|
||||||
|
public BinaryPropertyListWriter(Stream outStr, int version)
|
||||||
{
|
{
|
||||||
/// <summary>Binary property list version 0.0</summary>
|
this.version = version;
|
||||||
public const int VERSION_00 = 0;
|
outStream = outStr;
|
||||||
/// <summary>Binary property list version 1.0</summary>
|
|
||||||
public const int VERSION_10 = 10;
|
|
||||||
/// <summary>Binary property list version 1.5</summary>
|
|
||||||
public const int VERSION_15 = 15;
|
|
||||||
/// <summary>Binary property list version 2.0</summary>
|
|
||||||
public const int VERSION_20 = 20;
|
|
||||||
|
|
||||||
// map from object to its ID
|
|
||||||
protected readonly Dictionary<NSObject, int> idDict = new(new AddObjectEqualityComparer());
|
|
||||||
protected readonly Dictionary<NSObject, int> idDict2 = new(new GetObjectEqualityComparer());
|
|
||||||
|
|
||||||
// raw output stream to result file
|
|
||||||
readonly Stream outStream;
|
|
||||||
|
|
||||||
readonly int version = VERSION_00;
|
|
||||||
|
|
||||||
// # of bytes written so far
|
|
||||||
long count;
|
|
||||||
protected int currentId;
|
|
||||||
int idSizeInBytes;
|
|
||||||
|
|
||||||
/// <summary>Creates a new binary property list writer</summary>
|
|
||||||
/// <param name="outStr">The output stream into which the binary property list will be written</param>
|
|
||||||
/// <exception cref="IOException">If an error occured while writing to the stream</exception>
|
|
||||||
public BinaryPropertyListWriter(Stream outStr) => outStream = outStr;
|
|
||||||
|
|
||||||
public BinaryPropertyListWriter(Stream outStr, int version)
|
|
||||||
{
|
|
||||||
this.version = version;
|
|
||||||
outStream = outStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BinaryPropertyListWriter(Stream outStr, int version,
|
|
||||||
IEqualityComparer<NSObject> addObjectEqualityComparer,
|
|
||||||
IEqualityComparer<NSObject> getObjectEqualityComparer)
|
|
||||||
{
|
|
||||||
this.version = version;
|
|
||||||
outStream = outStr;
|
|
||||||
idDict = new Dictionary<NSObject, int>(addObjectEqualityComparer);
|
|
||||||
idDict2 = new Dictionary<NSObject, int>(getObjectEqualityComparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether two equivalent objects should be serialized once in the binary
|
|
||||||
/// property list file, or whether the value should be stored multiple times in the binary property list file. The
|
|
||||||
/// default is <see langword="false" /> .
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// In most scenarios, you want this to be <see langword="true" />, as it reduces the size of the binary proeprty
|
|
||||||
/// list file. However, by default, the Apple tools do not seem to implement this optimization, so set this value to
|
|
||||||
/// <see langword="false" /> if you want to maintain binary compatibility with the Apple tools.
|
|
||||||
/// </remarks>
|
|
||||||
public bool ReuseObjectIds { get; set; }
|
|
||||||
|
|
||||||
/// <summary>Finds out the minimum binary property list format version that can be used to save the given NSObject tree.</summary>
|
|
||||||
/// <returns>Version code</returns>
|
|
||||||
/// <param name="root">Object root.</param>
|
|
||||||
static int GetMinimumRequiredVersion(NSObject root)
|
|
||||||
{
|
|
||||||
int minVersion = VERSION_00;
|
|
||||||
|
|
||||||
switch(root)
|
|
||||||
{
|
|
||||||
case null:
|
|
||||||
minVersion = VERSION_10;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NSDictionary dict:
|
|
||||||
{
|
|
||||||
foreach(NSObject o in dict.GetDictionary().Values)
|
|
||||||
{
|
|
||||||
int v = GetMinimumRequiredVersion(o);
|
|
||||||
|
|
||||||
if(v > minVersion)
|
|
||||||
minVersion = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NSArray array:
|
|
||||||
{
|
|
||||||
foreach(NSObject o in array)
|
|
||||||
{
|
|
||||||
int v = GetMinimumRequiredVersion(o);
|
|
||||||
|
|
||||||
if(v > minVersion)
|
|
||||||
minVersion = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NSSet set:
|
|
||||||
{
|
|
||||||
//Sets are only allowed in property lists v1+
|
|
||||||
minVersion = VERSION_10;
|
|
||||||
|
|
||||||
foreach(NSObject o in set.AllObjects())
|
|
||||||
{
|
|
||||||
int v = GetMinimumRequiredVersion(o);
|
|
||||||
|
|
||||||
if(v > minVersion)
|
|
||||||
minVersion = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return minVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Writes a binary plist file with the given object as the root.</summary>
|
|
||||||
/// <param name="file">the file to write to</param>
|
|
||||||
/// <param name="root">the source of the data to write to the file</param>
|
|
||||||
/// <exception cref="IOException"></exception>
|
|
||||||
public static void Write(FileInfo file, NSObject root)
|
|
||||||
{
|
|
||||||
using FileStream fous = file.OpenWrite();
|
|
||||||
|
|
||||||
Write(fous, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Writes a binary plist serialization of the given object as the root.</summary>
|
|
||||||
/// <param name="outStream">the stream to write to</param>
|
|
||||||
/// <param name="root">the source of the data to write to the stream</param>
|
|
||||||
/// <exception cref="IOException"></exception>
|
|
||||||
public static void Write(Stream outStream, NSObject root)
|
|
||||||
{
|
|
||||||
int minVersion = GetMinimumRequiredVersion(root);
|
|
||||||
|
|
||||||
if(minVersion > VERSION_00)
|
|
||||||
{
|
|
||||||
string versionString = minVersion == VERSION_10
|
|
||||||
? "v1.0"
|
|
||||||
: minVersion == VERSION_15
|
|
||||||
? "v1.5"
|
|
||||||
: minVersion == VERSION_20
|
|
||||||
? "v2.0"
|
|
||||||
: "v0.0";
|
|
||||||
|
|
||||||
throw new IOException("The given property list structure cannot be saved. " +
|
|
||||||
"The required version of the binary format (" + versionString +
|
|
||||||
") is not yet supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var w = new BinaryPropertyListWriter(outStream, minVersion);
|
|
||||||
w.Write(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Writes a binary plist serialization of the given object as the root into a byte array.</summary>
|
|
||||||
/// <returns>The byte array containing the serialized property list</returns>
|
|
||||||
/// <param name="root">The root object of the property list</param>
|
|
||||||
/// <exception cref="IOException"></exception>
|
|
||||||
public static byte[] WriteToArray(NSObject root)
|
|
||||||
{
|
|
||||||
var bout = new MemoryStream();
|
|
||||||
Write(bout, root);
|
|
||||||
|
|
||||||
return bout.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(NSObject root)
|
|
||||||
{
|
|
||||||
// magic bytes
|
|
||||||
Write(new[]
|
|
||||||
{
|
|
||||||
(byte)'b', (byte)'p', (byte)'l', (byte)'i', (byte)'s', (byte)'t'
|
|
||||||
});
|
|
||||||
|
|
||||||
//version
|
|
||||||
switch(version)
|
|
||||||
{
|
|
||||||
case VERSION_00:
|
|
||||||
{
|
|
||||||
Write(new[]
|
|
||||||
{
|
|
||||||
(byte)'0', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VERSION_10:
|
|
||||||
{
|
|
||||||
Write(new[]
|
|
||||||
{
|
|
||||||
(byte)'1', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VERSION_15:
|
|
||||||
{
|
|
||||||
Write(new[]
|
|
||||||
{
|
|
||||||
(byte)'1', (byte)'5'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VERSION_20:
|
|
||||||
{
|
|
||||||
Write(new[]
|
|
||||||
{
|
|
||||||
(byte)'2', (byte)'0'
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign IDs to all the objects.
|
|
||||||
root.AssignIDs(this);
|
|
||||||
|
|
||||||
idSizeInBytes = ComputeIdSizeInBytes(idDict.Count);
|
|
||||||
|
|
||||||
// offsets of each object, indexed by ID
|
|
||||||
long[] offsets = new long[idDict.Count];
|
|
||||||
|
|
||||||
// write each object, save offset
|
|
||||||
foreach(KeyValuePair<NSObject, int> pair in idDict)
|
|
||||||
{
|
|
||||||
NSObject obj = pair.Key;
|
|
||||||
int id = pair.Value;
|
|
||||||
offsets[id] = count;
|
|
||||||
|
|
||||||
if(obj == null)
|
|
||||||
Write(0x00);
|
|
||||||
else
|
|
||||||
obj.ToBinary(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// write offset table
|
|
||||||
long offsetTableOffset = count;
|
|
||||||
int offsetSizeInBytes = ComputeOffsetSizeInBytes(count);
|
|
||||||
|
|
||||||
foreach(long offset in offsets)
|
|
||||||
WriteBytes(offset, offsetSizeInBytes);
|
|
||||||
|
|
||||||
if(version != VERSION_15)
|
|
||||||
{
|
|
||||||
// write trailer
|
|
||||||
// 6 null bytes
|
|
||||||
Write(new byte[6]);
|
|
||||||
|
|
||||||
// size of an offset
|
|
||||||
Write(offsetSizeInBytes);
|
|
||||||
|
|
||||||
// size of a ref
|
|
||||||
Write(idSizeInBytes);
|
|
||||||
|
|
||||||
// number of objects
|
|
||||||
WriteLong(idDict.Count);
|
|
||||||
|
|
||||||
// top object
|
|
||||||
int rootID = idDict[root];
|
|
||||||
WriteLong(rootID);
|
|
||||||
|
|
||||||
// offset table offset
|
|
||||||
WriteLong(offsetTableOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
outStream.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal virtual void AssignID(NSObject obj)
|
|
||||||
{
|
|
||||||
if(ReuseObjectIds)
|
|
||||||
{
|
|
||||||
if(!idDict.ContainsKey(obj))
|
|
||||||
idDict.Add(obj, currentId++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!idDict2.ContainsKey(obj))
|
|
||||||
idDict2.Add(obj, currentId);
|
|
||||||
|
|
||||||
if(!idDict.ContainsKey(obj))
|
|
||||||
idDict.Add(obj, currentId++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int GetID(NSObject obj) => ReuseObjectIds ? idDict[obj] : idDict2[obj];
|
|
||||||
|
|
||||||
static int ComputeIdSizeInBytes(int numberOfIds)
|
|
||||||
{
|
|
||||||
if(numberOfIds < 256)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return numberOfIds < 65536 ? 2 : 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ComputeOffsetSizeInBytes(long maxOffset) => maxOffset switch
|
|
||||||
{
|
|
||||||
< 256 => 1,
|
|
||||||
< 65536 => 2,
|
|
||||||
< 4294967296L => 4,
|
|
||||||
_ => 8
|
|
||||||
};
|
|
||||||
|
|
||||||
internal void WriteIntHeader(int kind, int value)
|
|
||||||
{
|
|
||||||
switch(value)
|
|
||||||
{
|
|
||||||
case < 0: throw new ArgumentException("value must be greater than or equal to 0", "value");
|
|
||||||
case < 15:
|
|
||||||
Write((kind << 4) + value);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case < 256:
|
|
||||||
Write((kind << 4) + 15);
|
|
||||||
Write(0x10);
|
|
||||||
WriteBytes(value, 1);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case < 65536:
|
|
||||||
Write((kind << 4) + 15);
|
|
||||||
Write(0x11);
|
|
||||||
WriteBytes(value, 2);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Write((kind << 4) + 15);
|
|
||||||
Write(0x12);
|
|
||||||
WriteBytes(value, 4);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Write(int b)
|
|
||||||
{
|
|
||||||
outStream.WriteByte((byte)b);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Write(byte[] bytes)
|
|
||||||
{
|
|
||||||
outStream.Write(bytes, 0, bytes.Length);
|
|
||||||
count += bytes.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Write(Span<byte> bytes)
|
|
||||||
{
|
|
||||||
#if NATIVE_SPAN
|
|
||||||
outStream.Write(bytes);
|
|
||||||
count += bytes.Length;
|
|
||||||
#else
|
|
||||||
Write(bytes.ToArray());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void WriteBytes(long value, int bytes)
|
|
||||||
{
|
|
||||||
// write low-order bytes big-endian style
|
|
||||||
for(int i = bytes - 1; i >= 0; i--)
|
|
||||||
Write((int)(value >> (8 * i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void WriteID(int id) => WriteBytes(id, idSizeInBytes);
|
|
||||||
|
|
||||||
internal void WriteLong(long value) => WriteBytes(value, 8);
|
|
||||||
|
|
||||||
internal void WriteDouble(double value) => WriteLong(BitConverter.DoubleToInt64Bits(value));
|
|
||||||
|
|
||||||
internal static bool IsSerializationPrimitive(NSString obj)
|
|
||||||
{
|
|
||||||
string content = obj.Content;
|
|
||||||
|
|
||||||
// This is a list of "special" values which are only added once to a binary property
|
|
||||||
// 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
|
|
||||||
"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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BinaryPropertyListWriter(Stream outStr, int version, IEqualityComparer<NSObject> addObjectEqualityComparer,
|
||||||
|
IEqualityComparer<NSObject> getObjectEqualityComparer)
|
||||||
|
{
|
||||||
|
this.version = version;
|
||||||
|
outStream = outStr;
|
||||||
|
idDict = new Dictionary<NSObject, int>(addObjectEqualityComparer);
|
||||||
|
idDict2 = new Dictionary<NSObject, int>(getObjectEqualityComparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether two equivalent objects should be serialized once in the binary
|
||||||
|
/// property list file, or whether the value should be stored multiple times in the binary property list file. The
|
||||||
|
/// default is <see langword="false" /> .
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In most scenarios, you want this to be <see langword="true" />, as it reduces the size of the binary proeprty
|
||||||
|
/// list file. However, by default, the Apple tools do not seem to implement this optimization, so set this value to
|
||||||
|
/// <see langword="false" /> if you want to maintain binary compatibility with the Apple tools.
|
||||||
|
/// </remarks>
|
||||||
|
public bool ReuseObjectIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Finds out the minimum binary property list format version that can be used to save the given NSObject tree.</summary>
|
||||||
|
/// <returns>Version code</returns>
|
||||||
|
/// <param name="root">Object root.</param>
|
||||||
|
static int GetMinimumRequiredVersion(NSObject root)
|
||||||
|
{
|
||||||
|
int minVersion = VERSION_00;
|
||||||
|
|
||||||
|
switch(root)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
minVersion = VERSION_10;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NSDictionary dict:
|
||||||
|
{
|
||||||
|
foreach(NSObject o in dict.GetDictionary().Values)
|
||||||
|
{
|
||||||
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
|
if(v > minVersion) minVersion = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NSArray array:
|
||||||
|
{
|
||||||
|
foreach(NSObject o in array)
|
||||||
|
{
|
||||||
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
|
if(v > minVersion) minVersion = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NSSet set:
|
||||||
|
{
|
||||||
|
//Sets are only allowed in property lists v1+
|
||||||
|
minVersion = VERSION_10;
|
||||||
|
|
||||||
|
foreach(NSObject o in set.AllObjects())
|
||||||
|
{
|
||||||
|
int v = GetMinimumRequiredVersion(o);
|
||||||
|
|
||||||
|
if(v > minVersion) minVersion = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a binary plist file with the given object as the root.</summary>
|
||||||
|
/// <param name="file">the file to write to</param>
|
||||||
|
/// <param name="root">the source of the data to write to the file</param>
|
||||||
|
/// <exception cref="IOException"></exception>
|
||||||
|
public static void Write(FileInfo file, NSObject root)
|
||||||
|
{
|
||||||
|
using FileStream fous = file.OpenWrite();
|
||||||
|
|
||||||
|
Write(fous, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a binary plist serialization of the given object as the root.</summary>
|
||||||
|
/// <param name="outStream">the stream to write to</param>
|
||||||
|
/// <param name="root">the source of the data to write to the stream</param>
|
||||||
|
/// <exception cref="IOException"></exception>
|
||||||
|
public static void Write(Stream outStream, NSObject root)
|
||||||
|
{
|
||||||
|
int minVersion = GetMinimumRequiredVersion(root);
|
||||||
|
|
||||||
|
if(minVersion > VERSION_00)
|
||||||
|
{
|
||||||
|
string versionString = minVersion == VERSION_10
|
||||||
|
? "v1.0"
|
||||||
|
: minVersion == VERSION_15
|
||||||
|
? "v1.5"
|
||||||
|
: minVersion == VERSION_20
|
||||||
|
? "v2.0"
|
||||||
|
: "v0.0";
|
||||||
|
|
||||||
|
throw new IOException("The given property list structure cannot be saved. " +
|
||||||
|
"The required version of the binary format (" +
|
||||||
|
versionString +
|
||||||
|
") is not yet supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var w = new BinaryPropertyListWriter(outStream, minVersion);
|
||||||
|
w.Write(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes a binary plist serialization of the given object as the root into a byte array.</summary>
|
||||||
|
/// <returns>The byte array containing the serialized property list</returns>
|
||||||
|
/// <param name="root">The root object of the property list</param>
|
||||||
|
/// <exception cref="IOException"></exception>
|
||||||
|
public static byte[] WriteToArray(NSObject root)
|
||||||
|
{
|
||||||
|
var bout = new MemoryStream();
|
||||||
|
Write(bout, root);
|
||||||
|
|
||||||
|
return bout.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(NSObject root)
|
||||||
|
{
|
||||||
|
// magic bytes
|
||||||
|
Write("bplist"u8.ToArray());
|
||||||
|
|
||||||
|
//version
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case VERSION_00:
|
||||||
|
{
|
||||||
|
Write("00"u8.ToArray());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERSION_10:
|
||||||
|
{
|
||||||
|
Write("10"u8.ToArray());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERSION_15:
|
||||||
|
{
|
||||||
|
Write("15"u8.ToArray());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VERSION_20:
|
||||||
|
{
|
||||||
|
Write("20"u8.ToArray());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign IDs to all the objects.
|
||||||
|
root.AssignIDs(this);
|
||||||
|
|
||||||
|
idSizeInBytes = ComputeIdSizeInBytes(idDict.Count);
|
||||||
|
|
||||||
|
// offsets of each object, indexed by ID
|
||||||
|
long[] offsets = new long[idDict.Count];
|
||||||
|
|
||||||
|
// write each object, save offset
|
||||||
|
foreach(KeyValuePair<NSObject, int> pair in idDict)
|
||||||
|
{
|
||||||
|
NSObject obj = pair.Key;
|
||||||
|
int id = pair.Value;
|
||||||
|
offsets[id] = count;
|
||||||
|
|
||||||
|
if(obj == null)
|
||||||
|
Write(0x00);
|
||||||
|
else
|
||||||
|
obj.ToBinary(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write offset table
|
||||||
|
long offsetTableOffset = count;
|
||||||
|
int offsetSizeInBytes = ComputeOffsetSizeInBytes(count);
|
||||||
|
|
||||||
|
foreach(long offset in offsets) WriteBytes(offset, offsetSizeInBytes);
|
||||||
|
|
||||||
|
if(version != VERSION_15)
|
||||||
|
{
|
||||||
|
// write trailer
|
||||||
|
// 6 null bytes
|
||||||
|
Write(new byte[6]);
|
||||||
|
|
||||||
|
// size of an offset
|
||||||
|
Write(offsetSizeInBytes);
|
||||||
|
|
||||||
|
// size of a ref
|
||||||
|
Write(idSizeInBytes);
|
||||||
|
|
||||||
|
// number of objects
|
||||||
|
WriteLong(idDict.Count);
|
||||||
|
|
||||||
|
// top object
|
||||||
|
int rootID = idDict[root];
|
||||||
|
WriteLong(rootID);
|
||||||
|
|
||||||
|
// offset table offset
|
||||||
|
WriteLong(offsetTableOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
outStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal virtual void AssignID(NSObject obj)
|
||||||
|
{
|
||||||
|
if(ReuseObjectIds)
|
||||||
|
{
|
||||||
|
if(!idDict.ContainsKey(obj)) idDict.Add(obj, currentId++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!idDict2.ContainsKey(obj)) idDict2.Add(obj, currentId);
|
||||||
|
|
||||||
|
if(!idDict.ContainsKey(obj)) idDict.Add(obj, currentId++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetID(NSObject obj) => ReuseObjectIds ? idDict[obj] : idDict2[obj];
|
||||||
|
|
||||||
|
static int ComputeIdSizeInBytes(int numberOfIds)
|
||||||
|
{
|
||||||
|
if(numberOfIds < 256) return 1;
|
||||||
|
|
||||||
|
return numberOfIds < 65536 ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ComputeOffsetSizeInBytes(long maxOffset) => maxOffset switch
|
||||||
|
{
|
||||||
|
< 256 => 1,
|
||||||
|
< 65536 => 2,
|
||||||
|
< 4294967296L => 4,
|
||||||
|
_ => 8
|
||||||
|
};
|
||||||
|
|
||||||
|
internal void WriteIntHeader(int kind, int value)
|
||||||
|
{
|
||||||
|
switch(value)
|
||||||
|
{
|
||||||
|
case < 0:
|
||||||
|
throw new ArgumentException("value must be greater than or equal to 0", "value");
|
||||||
|
case < 15:
|
||||||
|
Write((kind << 4) + value);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case < 256:
|
||||||
|
Write((kind << 4) + 15);
|
||||||
|
Write(0x10);
|
||||||
|
WriteBytes(value, 1);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case < 65536:
|
||||||
|
Write((kind << 4) + 15);
|
||||||
|
Write(0x11);
|
||||||
|
WriteBytes(value, 2);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Write((kind << 4) + 15);
|
||||||
|
Write(0x12);
|
||||||
|
WriteBytes(value, 4);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Write(int b)
|
||||||
|
{
|
||||||
|
outStream.WriteByte((byte)b);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Write(byte[] bytes)
|
||||||
|
{
|
||||||
|
outStream.Write(bytes, 0, bytes.Length);
|
||||||
|
count += bytes.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Write(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
#if NATIVE_SPAN
|
||||||
|
outStream.Write(bytes);
|
||||||
|
count += bytes.Length;
|
||||||
|
#else
|
||||||
|
Write(bytes.ToArray());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void WriteBytes(long value, int bytes)
|
||||||
|
{
|
||||||
|
// write low-order bytes big-endian style
|
||||||
|
for(int i = bytes - 1; i >= 0; i--) Write((int)(value >> 8 * i));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void WriteID(int id) => WriteBytes(id, idSizeInBytes);
|
||||||
|
|
||||||
|
internal void WriteLong(long value) => WriteBytes(value, 8);
|
||||||
|
|
||||||
|
internal void WriteDouble(double value) => WriteLong(BitConverter.DoubleToInt64Bits(value));
|
||||||
|
|
||||||
|
internal static bool IsSerializationPrimitive(NSString obj)
|
||||||
|
{
|
||||||
|
string content = obj.Content;
|
||||||
|
|
||||||
|
// This is a list of "special" values which are only added once to a binary property
|
||||||
|
// 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 "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();
|
||||||
}
|
}
|
||||||
@@ -22,59 +22,58 @@
|
|||||||
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 />
|
||||||
|
public NSObject this[int index]
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
get => array[index];
|
||||||
public NSObject this[int index]
|
|
||||||
{
|
|
||||||
get => array[index];
|
|
||||||
|
|
||||||
set => array[index] = value;
|
set => array[index] = value;
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsReadOnly => false;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Add(NSObject item) => array.Add(item);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Clear() => array.Clear();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Contains(NSObject item) => array.Contains(item);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void CopyTo(NSObject[] array, int arrayIndex) => this.array.CopyTo(array, arrayIndex);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerator<NSObject> GetEnumerator() => array.GetEnumerator();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public int IndexOf(NSObject item) => array.IndexOf(item);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Insert(int index, NSObject item) => array.Insert(index, item);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Remove(NSObject item) => array.Remove(item);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void RemoveAt(int index) => array.RemoveAt(index);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => array.GetEnumerator();
|
|
||||||
|
|
||||||
public void Add(object item) => Add(Wrap(item));
|
|
||||||
|
|
||||||
public bool Contains(object item) => Contains(Wrap(item));
|
|
||||||
|
|
||||||
public int IndexOf(object item) => array.IndexOf(Wrap(item));
|
|
||||||
|
|
||||||
public void Insert(int index, object item) => Insert(index, Wrap(item));
|
|
||||||
|
|
||||||
public bool Remove(object item) => Remove(Wrap(item));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Add(NSObject item) => array.Add(item);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Clear() => array.Clear();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Contains(NSObject item) => array.Contains(item);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void CopyTo(NSObject[] array, int arrayIndex) => this.array.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<NSObject> GetEnumerator() => array.GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int IndexOf(NSObject item) => array.IndexOf(item);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Insert(int index, NSObject item) => array.Insert(index, item);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Remove(NSObject item) => array.Remove(item);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void RemoveAt(int index) => array.RemoveAt(index);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => array.GetEnumerator();
|
||||||
|
|
||||||
|
public void Add(object item) => Add(Wrap(item));
|
||||||
|
|
||||||
|
public bool Contains(object item) => Contains(Wrap(item));
|
||||||
|
|
||||||
|
public int IndexOf(object item) => array.IndexOf(Wrap(item));
|
||||||
|
|
||||||
|
public void Insert(int index, object item) => Insert(index, Wrap(item));
|
||||||
|
|
||||||
|
public bool Remove(object item) => Remove(Wrap(item));
|
||||||
}
|
}
|
||||||
@@ -27,336 +27,317 @@ 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>
|
readonly List<NSObject> array;
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
/// <summary>Creates an empty array of the given length.</summary>
|
||||||
public partial class NSArray : NSObject
|
/// <param name="length">The number of elements this array will be able to hold.</param>
|
||||||
|
public NSArray(int length) => array = new List<NSObject>(length);
|
||||||
|
|
||||||
|
/// <summary>Creates a array from an existing one</summary>
|
||||||
|
/// <param name="a">The array which should be wrapped by the NSArray.</param>
|
||||||
|
public NSArray(params NSObject[] a) => array = [..a];
|
||||||
|
|
||||||
|
/// <summary>Returns the size of the array.</summary>
|
||||||
|
/// <value>The number of elements that this array can store.</value>
|
||||||
|
public int Count => array.Count;
|
||||||
|
|
||||||
|
/// <summary>Returns the object stored at the given index.</summary>
|
||||||
|
/// <returns>The object at the given index.</returns>
|
||||||
|
/// <param name="i">The index of the object.</param>
|
||||||
|
[Obsolete]
|
||||||
|
public NSObject ObjectAtIndex(int i) => array[i];
|
||||||
|
|
||||||
|
/// <summary>Remove the i-th element from the array. The array will be resized.</summary>
|
||||||
|
/// <param name="i">The index of the object</param>
|
||||||
|
[Obsolete]
|
||||||
|
public void Remove(int i) => array.RemoveAt(i);
|
||||||
|
|
||||||
|
/// <summary>Stores an object at the specified index. If there was another object stored at that index it will be replaced.</summary>
|
||||||
|
/// <param name="key">The index where to store the object.</param>
|
||||||
|
/// <param name="value">The object.</param>
|
||||||
|
[Obsolete]
|
||||||
|
public void SetValue(int key, object value)
|
||||||
{
|
{
|
||||||
readonly List<NSObject> array;
|
if(value == null) throw new ArgumentNullException("value", "Cannot add null values to an NSArray!");
|
||||||
|
|
||||||
/// <summary>Creates an empty array of the given length.</summary>
|
array[key] = Wrap(value);
|
||||||
/// <param name="length">The number of elements this array will be able to hold.</param>
|
}
|
||||||
public NSArray(int length) => array = new List<NSObject>(length);
|
|
||||||
|
|
||||||
/// <summary>Creates a array from an existing one</summary>
|
/// <summary>
|
||||||
/// <param name="a">The array which should be wrapped by the NSArray.</param>
|
/// Returns the array of NSObjects represented by this NSArray. Any changes to the values of this array will also
|
||||||
public NSArray(params NSObject[] a) => array = new List<NSObject>(a);
|
/// affect the NSArray.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The actual array represented by this NSArray.</returns>
|
||||||
|
[Obsolete]
|
||||||
|
public NSObject[] GetArray() => array.ToArray();
|
||||||
|
|
||||||
/// <summary>Returns the size of the array.</summary>
|
/// <summary>Checks whether an object is present in the array or whether it is equal to any of the objects in the array.</summary>
|
||||||
/// <value>The number of elements that this array can store.</value>
|
/// <returns><c>true</c>, when the object could be found. <c>false</c> otherwise.</returns>
|
||||||
public int Count => array.Count;
|
/// <param name="obj">The object to look for.</param>
|
||||||
|
[Obsolete]
|
||||||
|
public bool ContainsObject(object obj)
|
||||||
|
{
|
||||||
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
/// <summary>Returns the object stored at the given index.</summary>
|
foreach(NSObject elem in array)
|
||||||
/// <returns>The object at the given index.</returns>
|
if(elem.Equals(nso)) return true;
|
||||||
/// <param name="i">The index of the object.</param>
|
|
||||||
[Obsolete]
|
|
||||||
public NSObject ObjectAtIndex(int i) => array[i];
|
|
||||||
|
|
||||||
/// <summary>Remove the i-th element from the array. The array will be resized.</summary>
|
return false;
|
||||||
/// <param name="i">The index of the object</param>
|
}
|
||||||
[Obsolete]
|
|
||||||
public void Remove(int i) => array.RemoveAt(i);
|
|
||||||
|
|
||||||
/// <summary>Stores an object at the specified index. If there was another object stored at that index it will be replaced.</summary>
|
/// <summary>
|
||||||
/// <param name="key">The index where to store the object.</param>
|
/// Searches for an object in the array. If it is found its index will be returned. This method also returns an
|
||||||
/// <param name="value">The object.</param>
|
/// index if the object is not the same as the one stored in the array but has equal contents.
|
||||||
[Obsolete]
|
/// </summary>
|
||||||
public void SetValue(int key, object value)
|
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
||||||
|
/// <param name="obj">The object to look for.</param>
|
||||||
|
[Obsolete]
|
||||||
|
public int IndexOfObject(object obj)
|
||||||
|
{
|
||||||
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Count; i++)
|
||||||
|
if(array[i].Equals(nso)) return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for an object in the array. If it is found its index will be returned. This method only returns the
|
||||||
|
/// index of an object that is <b>identical</b> to the given one. Thus objects that might contain the same value as the
|
||||||
|
/// given one will not be considered.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
||||||
|
/// <param name="obj">The object to look for.</param>
|
||||||
|
[Obsolete]
|
||||||
|
public int IndexOfIdenticalObject(object obj)
|
||||||
|
{
|
||||||
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Count; i++)
|
||||||
|
if(array[i] == nso) return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the last object contained in this array.</summary>
|
||||||
|
/// <returns>The value of the highest index in the array.</returns>
|
||||||
|
public NSObject LastObject() => array[array.Count - 1];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a new array containing only the values stored at the given indices. The values are sorted by their
|
||||||
|
/// index.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new array containing the objects stored at the given indices.</returns>
|
||||||
|
/// <param name="indexes">The indices of the objects.</param>
|
||||||
|
public NSObject[] ObjectsAtIndexes(params int[] indexes)
|
||||||
|
{
|
||||||
|
var result = new NSObject[indexes.Length];
|
||||||
|
Array.Sort(indexes);
|
||||||
|
|
||||||
|
for(int i = 0; i < indexes.Length; i++) result[i] = array[indexes[i]];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="System.Object" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if(obj is NSArray nsArray) return ArrayEquals(nsArray, this);
|
||||||
|
|
||||||
|
NSObject nso = Wrap(obj);
|
||||||
|
|
||||||
|
if(nso is NSArray nsoArray) return ArrayEquals(nsoArray, this);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSArray" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 7;
|
||||||
|
hash = 89 * hash + array.GetHashCode();
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("<array>");
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
|
||||||
|
foreach(NSObject o in array)
|
||||||
{
|
{
|
||||||
if(value == null)
|
o.ToXml(xml, level + 1);
|
||||||
throw new ArgumentNullException("value", "Cannot add null values to an NSArray!");
|
|
||||||
|
|
||||||
array[key] = Wrap(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the array of NSObjects represented by this NSArray. Any changes to the values of this array will also
|
|
||||||
/// affect the NSArray.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The actual array represented by this NSArray.</returns>
|
|
||||||
[Obsolete]
|
|
||||||
public NSObject[] GetArray() => array.ToArray();
|
|
||||||
|
|
||||||
/// <summary>Checks whether an object is present in the array or whether it is equal to any of the objects in the array.</summary>
|
|
||||||
/// <returns><c>true</c>, when the object could be found. <c>false</c> otherwise.</returns>
|
|
||||||
/// <param name="obj">The object to look for.</param>
|
|
||||||
[Obsolete]
|
|
||||||
public bool ContainsObject(object obj)
|
|
||||||
{
|
|
||||||
NSObject nso = Wrap(obj);
|
|
||||||
|
|
||||||
foreach(NSObject elem in array)
|
|
||||||
if(elem.Equals(nso))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Searches for an object in the array. If it is found its index will be returned. This method also returns an
|
|
||||||
/// index if the object is not the same as the one stored in the array but has equal contents.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
|
||||||
/// <param name="obj">The object to look for.</param>
|
|
||||||
[Obsolete]
|
|
||||||
public int IndexOfObject(object obj)
|
|
||||||
{
|
|
||||||
NSObject nso = Wrap(obj);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
|
||||||
if(array[i].Equals(nso))
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Searches for an object in the array. If it is found its index will be returned. This method only returns the
|
|
||||||
/// index of an object that is <b>identical</b> to the given one. Thus objects that might contain the same value as the
|
|
||||||
/// given one will not be considered.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The index of the object, if it was found. -1 otherwise.</returns>
|
|
||||||
/// <param name="obj">The object to look for.</param>
|
|
||||||
[Obsolete]
|
|
||||||
public int IndexOfIdenticalObject(object obj)
|
|
||||||
{
|
|
||||||
NSObject nso = Wrap(obj);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
|
||||||
if(array[i] == nso)
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns the last object contained in this array.</summary>
|
|
||||||
/// <returns>The value of the highest index in the array.</returns>
|
|
||||||
public NSObject LastObject() => array[array.Count - 1];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a new array containing only the values stored at the given indices. The values are sorted by their
|
|
||||||
/// index.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The new array containing the objects stored at the given indices.</returns>
|
|
||||||
/// <param name="indexes">The indices of the objects.</param>
|
|
||||||
public NSObject[] ObjectsAtIndexes(params int[] indexes)
|
|
||||||
{
|
|
||||||
NSObject[] result = new NSObject[indexes.Length];
|
|
||||||
Array.Sort(indexes);
|
|
||||||
|
|
||||||
for(int i = 0; i < indexes.Length; i++)
|
|
||||||
result[i] = array[indexes[i]];
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="System.Object" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if(obj is NSArray nsArray)
|
|
||||||
return ArrayEquals(nsArray, this);
|
|
||||||
|
|
||||||
NSObject nso = Wrap(obj);
|
|
||||||
|
|
||||||
if(nso is NSArray nsoArray)
|
|
||||||
return ArrayEquals(nsoArray, this);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSArray" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
int hash = 7;
|
|
||||||
hash = (89 * hash) + array.GetHashCode();
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<array>");
|
|
||||||
xml.Append(NEWLINE);
|
xml.Append(NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
foreach(NSObject o in array)
|
Indent(xml, level);
|
||||||
|
xml.Append("</array>");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void AssignIDs(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
base.AssignIDs(outPlist);
|
||||||
|
|
||||||
|
foreach(NSObject obj in array) obj.AssignIDs(outPlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
outPlist.WriteIntHeader(0xA, array.Count);
|
||||||
|
|
||||||
|
foreach(NSObject obj in array) outPlist.WriteID(outPlist.GetID(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Generates a valid ASCII property list which has this NSArray as its root object.</para>
|
||||||
|
/// <para>
|
||||||
|
/// The generated property list complies with the format as described in
|
||||||
|
/// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
||||||
|
/// Property List Programming Guide - Old-Style ASCII Property Lists.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>ASCII representation of this object.</returns>
|
||||||
|
public string ToASCIIPropertyList()
|
||||||
|
{
|
||||||
|
var ascii = new StringBuilder();
|
||||||
|
ToASCII(ascii, 0);
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
|
||||||
|
return ascii.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Generates a valid ASCII property list in GnuStep format which has this NSArray as its root object.</para>
|
||||||
|
/// <para>
|
||||||
|
/// The generated property list complies with the format as described in
|
||||||
|
/// http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html GnuStep -
|
||||||
|
/// NSPropertyListSerialization class documentation.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>GnuStep ASCII representation of this object.</returns>
|
||||||
|
public string ToGnuStepASCIIPropertyList()
|
||||||
|
{
|
||||||
|
var ascii = new StringBuilder();
|
||||||
|
ToASCIIGnuStep(ascii, 0);
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
|
||||||
|
return ascii.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
|
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Count; i++)
|
||||||
|
{
|
||||||
|
if((array[i] is NSDictionary || array[i] is NSArray || array[i] is NSData) &&
|
||||||
|
indexOfLastNewLine != ascii.Length)
|
||||||
{
|
{
|
||||||
o.ToXml(xml, level + 1);
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("</array>");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void AssignIDs(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
base.AssignIDs(outPlist);
|
|
||||||
|
|
||||||
foreach(NSObject obj in array)
|
|
||||||
obj.AssignIDs(outPlist);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
outPlist.WriteIntHeader(0xA, array.Count);
|
|
||||||
|
|
||||||
foreach(NSObject obj in array)
|
|
||||||
outPlist.WriteID(outPlist.GetID(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Generates a valid ASCII property list which has this NSArray as its root object.</para>
|
|
||||||
/// <para>
|
|
||||||
/// The generated property list complies with the format as described in
|
|
||||||
/// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
|
||||||
/// Property List Programming Guide - Old-Style ASCII Property Lists.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>ASCII representation of this object.</returns>
|
|
||||||
public string ToASCIIPropertyList()
|
|
||||||
{
|
|
||||||
var ascii = new StringBuilder();
|
|
||||||
ToASCII(ascii, 0);
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
|
|
||||||
return ascii.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para>Generates a valid ASCII property list in GnuStep format which has this NSArray as its root object.</para>
|
|
||||||
/// <para>
|
|
||||||
/// The generated property list complies with the format as described in
|
|
||||||
/// http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html GnuStep -
|
|
||||||
/// NSPropertyListSerialization class documentation.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>GnuStep ASCII representation of this object.</returns>
|
|
||||||
public string ToGnuStepASCIIPropertyList()
|
|
||||||
{
|
|
||||||
var ascii = new StringBuilder();
|
|
||||||
ToASCIIGnuStep(ascii, 0);
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
|
|
||||||
return ascii.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
|
||||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
|
||||||
{
|
|
||||||
if((array[i] is NSDictionary || array[i] is NSArray || array[i] is NSData) &&
|
|
||||||
indexOfLastNewLine != ascii.Length)
|
|
||||||
{
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
array[i].ToASCII(ascii, level + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(i != 0)
|
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCII(ascii, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i != array.Count - 1)
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
array[i].ToASCII(ascii, level + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(i != 0) ascii.Append(" ");
|
||||||
|
|
||||||
|
array[i].ToASCII(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
if(i != array.Count - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
|
|
||||||
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
|
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
|
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Count; i++)
|
||||||
{
|
{
|
||||||
Indent(ascii, level);
|
Type objClass = array[i].GetType();
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
|
||||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
if((array[i] is NSDictionary || array[i] is NSArray || array[i] is NSData) &&
|
||||||
|
indexOfLastNewLine != ascii.Length)
|
||||||
{
|
{
|
||||||
Type objClass = array[i].GetType();
|
|
||||||
|
|
||||||
if((array[i] is NSDictionary || array[i] is NSArray || array[i] is NSData) &&
|
|
||||||
indexOfLastNewLine != ascii.Length)
|
|
||||||
{
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
array[i].ToASCIIGnuStep(ascii, level + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(i != 0)
|
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCIIGnuStep(ascii, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i != array.Count - 1)
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
ascii.Append(NEWLINE);
|
||||||
indexOfLastNewLine = ascii.Length;
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
array[i].ToASCIIGnuStep(ascii, level + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(i != 0) ascii.Append(" ");
|
||||||
|
|
||||||
|
array[i].ToASCIIGnuStep(ascii, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
if(i != array.Count - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
|
|
||||||
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
|
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
}
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSArray" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSArray nsArray)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(array.Count != nsArray.array.Count)
|
/// <summary>
|
||||||
return false;
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSArray" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSArray nsArray) return false;
|
||||||
|
|
||||||
for(int i = 0; i < array.Count; i++)
|
if(array.Count != nsArray.array.Count) return false;
|
||||||
if(!array[i].Equals(nsArray[i]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
for(int i = 0; i < array.Count; i++)
|
||||||
}
|
if(!array[i].Equals(nsArray[i])) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,162 +27,161 @@ 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>
|
// In the XML property list format, the base-64 encoded data is split across multiple lines.
|
||||||
/// @author Daniel Dreibrodt
|
// Each line contains 68 characters.
|
||||||
/// @author Natalia Portillo
|
const int DataLineLength = 68;
|
||||||
public class NSData : NSObject
|
|
||||||
|
/// <summary>Creates the NSData object from the binary representation of it.</summary>
|
||||||
|
/// <param name="bytes">The raw data contained in the NSData object.</param>
|
||||||
|
public NSData(byte[] bytes) => Bytes = bytes;
|
||||||
|
|
||||||
|
/// <summary>Creates a NSData object from its textual representation, which is a Base64 encoded amount of bytes.</summary>
|
||||||
|
/// <param name="base64">The Base64 encoded contents of the NSData object.</param>
|
||||||
|
/// <exception cref="FormatException">When the given string is not a proper Base64 formatted string.</exception>
|
||||||
|
public NSData(string base64) => Bytes = Convert.FromBase64String(base64);
|
||||||
|
|
||||||
|
/// <summary>Creates a NSData object from a file. Using the files contents as the contents of this NSData object.</summary>
|
||||||
|
/// <param name="file">The file containing the data.</param>
|
||||||
|
/// <exception cref="FileNotFoundException">If the file could not be found.</exception>
|
||||||
|
/// <exception cref="IOException">If the file could not be read.</exception>
|
||||||
|
public NSData(FileInfo file)
|
||||||
{
|
{
|
||||||
// In the XML property list format, the base-64 encoded data is split across multiple lines.
|
Bytes = new byte[(int)file.Length];
|
||||||
// Each line contains 68 characters.
|
|
||||||
const int DataLineLength = 68;
|
|
||||||
|
|
||||||
/// <summary>Creates the NSData object from the binary representation of it.</summary>
|
using FileStream raf = file.OpenRead();
|
||||||
/// <param name="bytes">The raw data contained in the NSData object.</param>
|
|
||||||
public NSData(byte[] bytes) => Bytes = bytes;
|
|
||||||
|
|
||||||
/// <summary>Creates a NSData object from its textual representation, which is a Base64 encoded amount of bytes.</summary>
|
|
||||||
/// <param name="base64">The Base64 encoded contents of the NSData object.</param>
|
|
||||||
/// <exception cref="FormatException">When the given string is not a proper Base64 formatted string.</exception>
|
|
||||||
public NSData(string base64) => Bytes = Convert.FromBase64String(base64);
|
|
||||||
|
|
||||||
/// <summary>Creates a NSData object from a file. Using the files contents as the contents of this NSData object.</summary>
|
|
||||||
/// <param name="file">The file containing the data.</param>
|
|
||||||
/// <exception cref="FileNotFoundException">If the file could not be found.</exception>
|
|
||||||
/// <exception cref="IOException">If the file could not be read.</exception>
|
|
||||||
public NSData(FileInfo file)
|
|
||||||
{
|
|
||||||
Bytes = new byte[(int)file.Length];
|
|
||||||
|
|
||||||
using FileStream raf = file.OpenRead();
|
|
||||||
|
|
||||||
#if NET7_0_OR_GREATER
|
#if NET7_0_OR_GREATER
|
||||||
raf.ReadExactly(Bytes, 0, (int)file.Length);
|
raf.ReadExactly(Bytes, 0, (int)file.Length);
|
||||||
#else
|
#else
|
||||||
raf.Read(Bytes, 0, (int)file.Length);
|
raf.Read(Bytes, 0, (int)file.Length);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>The bytes contained in this NSData object.</summary>
|
|
||||||
/// <value>The data as bytes</value>
|
|
||||||
public byte[] Bytes { get; }
|
|
||||||
|
|
||||||
/// <summary>Gets the amount of data stored in this object.</summary>
|
|
||||||
/// <value>The number of bytes contained in this object.</value>
|
|
||||||
public int Length => Bytes.Length;
|
|
||||||
|
|
||||||
/// <summary>Loads the bytes from this NSData object into a byte buffer.</summary>
|
|
||||||
/// <param name="buf">The byte buffer which will contain the data</param>
|
|
||||||
/// <param name="length">The amount of data to copy</param>
|
|
||||||
public void GetBytes(MemoryStream buf, int length) => buf.Write(Bytes, 0, Math.Min(Bytes.Length, length));
|
|
||||||
|
|
||||||
/// <summary>Loads the bytes from this NSData object into a byte buffer.</summary>
|
|
||||||
/// <param name="buf">The byte buffer which will contain the data</param>
|
|
||||||
/// <param name="rangeStart">The start index.</param>
|
|
||||||
/// <param name="rangeStop">The stop index.</param>
|
|
||||||
public void GetBytes(MemoryStream buf, int rangeStart, int rangeStop) =>
|
|
||||||
buf.Write(Bytes, rangeStart, Math.Min(Bytes.Length, rangeStop));
|
|
||||||
|
|
||||||
/// <summary>Gets the Base64 encoded data contained in this NSData object.</summary>
|
|
||||||
/// <returns>The Base64 encoded data as a <c>string</c>.</returns>
|
|
||||||
public string GetBase64EncodedData() => Convert.ToBase64String(Bytes);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="System.Object" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(object obj) =>
|
|
||||||
obj.GetType().Equals(GetType()) && ArrayEquals(((NSData)obj).Bytes, Bytes);
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSData" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
int hash = 5;
|
|
||||||
hash = (67 * hash) + Bytes.GetHashCode();
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<data>");
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
string base64 = GetBase64EncodedData();
|
|
||||||
|
|
||||||
foreach(string line in base64.Split('\n'))
|
|
||||||
for(int offset = 0; offset < base64.Length; offset += DataLineLength)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append(line.Substring(offset, Math.Min(DataLineLength, line.Length - offset)));
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("</data>");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
outPlist.WriteIntHeader(0x4, Bytes.Length);
|
|
||||||
outPlist.Write(Bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append(ASCIIPropertyListParser.DATA_BEGIN_TOKEN);
|
|
||||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
for(int i = 0; i < Bytes.Length; i++)
|
|
||||||
{
|
|
||||||
int b = Bytes[i] & 0xFF;
|
|
||||||
ascii.Append($"{b:x2}");
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH)
|
|
||||||
{
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
}
|
|
||||||
else if((i + 1) % 2 == 0 &&
|
|
||||||
i != Bytes.Length - 1)
|
|
||||||
ascii.Append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.DATA_END_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) => ToASCII(ascii, level);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSData" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj) => obj is NSData data && ArrayEquals(Bytes, data.Bytes);
|
|
||||||
|
|
||||||
public static explicit operator byte[](NSData value) => value.Bytes;
|
|
||||||
|
|
||||||
public static explicit operator NSData(byte[] value) => new(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>The bytes contained in this NSData object.</summary>
|
||||||
|
/// <value>The data as bytes</value>
|
||||||
|
public byte[] Bytes { get; }
|
||||||
|
|
||||||
|
/// <summary>Gets the amount of data stored in this object.</summary>
|
||||||
|
/// <value>The number of bytes contained in this object.</value>
|
||||||
|
public int Length => Bytes.Length;
|
||||||
|
|
||||||
|
/// <summary>Loads the bytes from this NSData object into a byte buffer.</summary>
|
||||||
|
/// <param name="buf">The byte buffer which will contain the data</param>
|
||||||
|
/// <param name="length">The amount of data to copy</param>
|
||||||
|
public void GetBytes(MemoryStream buf, int length) => buf.Write(Bytes, 0, Math.Min(Bytes.Length, length));
|
||||||
|
|
||||||
|
/// <summary>Loads the bytes from this NSData object into a byte buffer.</summary>
|
||||||
|
/// <param name="buf">The byte buffer which will contain the data</param>
|
||||||
|
/// <param name="rangeStart">The start index.</param>
|
||||||
|
/// <param name="rangeStop">The stop index.</param>
|
||||||
|
public void GetBytes(MemoryStream buf, int rangeStart, int rangeStop) =>
|
||||||
|
buf.Write(Bytes, rangeStart, Math.Min(Bytes.Length, rangeStop));
|
||||||
|
|
||||||
|
/// <summary>Gets the Base64 encoded data contained in this NSData object.</summary>
|
||||||
|
/// <returns>The Base64 encoded data as a <c>string</c>.</returns>
|
||||||
|
public string GetBase64EncodedData() => Convert.ToBase64String(Bytes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="System.Object" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(object obj) =>
|
||||||
|
obj.GetType().Equals(GetType()) && ArrayEquals(((NSData)obj).Bytes, Bytes);
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSData" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 5;
|
||||||
|
hash = 67 * hash + Bytes.GetHashCode();
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("<data>");
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
string base64 = GetBase64EncodedData();
|
||||||
|
|
||||||
|
foreach(string line in base64.Split('\n'))
|
||||||
|
{
|
||||||
|
for(int offset = 0; offset < base64.Length; offset += DataLineLength)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append(line.Substring(offset, Math.Min(DataLineLength, line.Length - offset)));
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("</data>");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
outPlist.WriteIntHeader(0x4, Bytes.Length);
|
||||||
|
outPlist.Write(Bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append(ASCIIPropertyListParser.DATA_BEGIN_TOKEN);
|
||||||
|
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
for(int i = 0; i < Bytes.Length; i++)
|
||||||
|
{
|
||||||
|
int b = Bytes[i] & 0xFF;
|
||||||
|
ascii.Append($"{b:x2}");
|
||||||
|
|
||||||
|
if(ascii.Length - indexOfLastNewLine > ASCII_LINE_LENGTH)
|
||||||
|
{
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
}
|
||||||
|
else if((i + 1) % 2 == 0 && i != Bytes.Length - 1) ascii.Append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
ascii.Append(ASCIIPropertyListParser.DATA_END_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) => ToASCII(ascii, level);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSData" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj) => obj is NSData data && ArrayEquals(Bytes, data.Bytes);
|
||||||
|
|
||||||
|
public static explicit operator byte[](NSData value) => value.Bytes;
|
||||||
|
|
||||||
|
public static explicit operator NSData(byte[] value) => new(value);
|
||||||
}
|
}
|
||||||
@@ -27,151 +27,149 @@ 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>
|
static readonly DateTime EPOCH = new(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
// The datetime ends with 'Z', which indicates UTC time. To make sure .NET
|
||||||
public class NSDate : NSObject
|
// understands the 'Z' character as a timezone, specify the 'K' format string.
|
||||||
|
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[] sdfAll =
|
||||||
|
[
|
||||||
|
sdfDefault, sdfGnuStep
|
||||||
|
];
|
||||||
|
|
||||||
|
static readonly CultureInfo provider = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
/// <summary>Creates a date from its binary representation.</summary>
|
||||||
|
/// <param name="bytes">bytes The date bytes</param>
|
||||||
|
public NSDate(ReadOnlySpan<byte> bytes) =>
|
||||||
|
|
||||||
|
//dates are 8 byte big-endian double, seconds since the epoch
|
||||||
|
Date = EPOCH.AddSeconds(BinaryPropertyListParser.ParseDouble(bytes));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a date from its textual representation. That representation has the following pattern:
|
||||||
|
/// <code>yyyy-MM-dd'T'HH:mm:ss'Z'</code>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textRepresentation">The textual representation of the date (ISO 8601 format)</param>
|
||||||
|
/// <exception cref="FormatException">When the date could not be parsed, i.e. it does not match the expected pattern.</exception>
|
||||||
|
public NSDate(string textRepresentation) => Date = ParseDateString(textRepresentation);
|
||||||
|
|
||||||
|
/// <summary>Creates a NSDate from a .NET DateTime</summary>
|
||||||
|
/// <param name="d">The date</param>
|
||||||
|
public NSDate(DateTime d) => Date = d;
|
||||||
|
|
||||||
|
/// <summary>Gets the date.</summary>
|
||||||
|
/// <returns>The date.</returns>
|
||||||
|
public DateTime Date { get; }
|
||||||
|
|
||||||
|
/// <summary>Parses the XML date string and creates a .NET DateTime object from it.</summary>
|
||||||
|
/// <returns>The parsed Date</returns>
|
||||||
|
/// <param name="textRepresentation">The date string as found in the XML property list</param>
|
||||||
|
/// <exception cref="FormatException">Given string cannot be parsed</exception>
|
||||||
|
static DateTime ParseDateString(string textRepresentation) =>
|
||||||
|
DateTime.ParseExact(textRepresentation, sdfAll, provider, DateTimeStyles.None);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a String representation of a .NET DateTime object. The string is formatted according to the
|
||||||
|
/// specification for XML property list dates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The date which should be represented.</param>
|
||||||
|
/// <returns>The string representation of the date.</returns>
|
||||||
|
public static string MakeDateString(DateTime date) => date.ToUniversalTime().ToString(sdfDefault, provider);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a String representation of a .NET DateTime object. The string is formatted according to the
|
||||||
|
/// specification for GnuStep ASCII property list dates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The date which should be represented.</param>
|
||||||
|
/// <returns>The string representation of the date.</returns>
|
||||||
|
static string MakeDateStringGnuStep(DateTime date) => date.ToString(sdfGnuStep, provider);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="System.Object" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(object obj) => obj.GetType().Equals(GetType()) && Date.Equals(((NSDate)obj).Date);
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSDate" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode() => Date.GetHashCode();
|
||||||
|
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
{
|
{
|
||||||
static readonly DateTime EPOCH = new(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
Indent(xml, level);
|
||||||
|
xml.Append("<date>");
|
||||||
// The datetime ends with 'Z', which indicates UTC time. To make sure .NET
|
xml.Append(MakeDateString(Date));
|
||||||
// understands the 'Z' character as a timezone, specify the 'K' format string.
|
xml.Append("</date>");
|
||||||
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[] sdfAll =
|
|
||||||
{
|
|
||||||
sdfDefault, sdfGnuStep
|
|
||||||
};
|
|
||||||
|
|
||||||
static readonly CultureInfo provider = CultureInfo.InvariantCulture;
|
|
||||||
|
|
||||||
/// <summary>Creates a date from its binary representation.</summary>
|
|
||||||
/// <param name="bytes">bytes The date bytes</param>
|
|
||||||
public NSDate(ReadOnlySpan<byte> bytes) =>
|
|
||||||
|
|
||||||
//dates are 8 byte big-endian double, seconds since the epoch
|
|
||||||
Date = EPOCH.AddSeconds(BinaryPropertyListParser.ParseDouble(bytes));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses a date from its textual representation. That representation has the following pattern:
|
|
||||||
/// <code>yyyy-MM-dd'T'HH:mm:ss'Z'</code>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textRepresentation">The textual representation of the date (ISO 8601 format)</param>
|
|
||||||
/// <exception cref="FormatException">When the date could not be parsed, i.e. it does not match the expected pattern.</exception>
|
|
||||||
public NSDate(string textRepresentation) => Date = ParseDateString(textRepresentation);
|
|
||||||
|
|
||||||
/// <summary>Creates a NSDate from a .NET DateTime</summary>
|
|
||||||
/// <param name="d">The date</param>
|
|
||||||
public NSDate(DateTime d) => Date = d;
|
|
||||||
|
|
||||||
/// <summary>Gets the date.</summary>
|
|
||||||
/// <returns>The date.</returns>
|
|
||||||
public DateTime Date { get; }
|
|
||||||
|
|
||||||
/// <summary>Parses the XML date string and creates a .NET DateTime object from it.</summary>
|
|
||||||
/// <returns>The parsed Date</returns>
|
|
||||||
/// <param name="textRepresentation">The date string as found in the XML property list</param>
|
|
||||||
/// <exception cref="FormatException">Given string cannot be parsed</exception>
|
|
||||||
static DateTime ParseDateString(string textRepresentation) =>
|
|
||||||
DateTime.ParseExact(textRepresentation, sdfAll, provider, DateTimeStyles.None);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a String representation of a .NET DateTime object. The string is formatted according to the
|
|
||||||
/// specification for XML property list dates.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="date">The date which should be represented.</param>
|
|
||||||
/// <returns>The string representation of the date.</returns>
|
|
||||||
public static string MakeDateString(DateTime date) => date.ToUniversalTime().ToString(sdfDefault, provider);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a String representation of a .NET DateTime object. The string is formatted according to the
|
|
||||||
/// specification for GnuStep ASCII property list dates.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="date">The date which should be represented.</param>
|
|
||||||
/// <returns>The string representation of the date.</returns>
|
|
||||||
static string MakeDateStringGnuStep(DateTime date) => date.ToString(sdfGnuStep, provider);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="System.Object" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(object obj) => obj.GetType().Equals(GetType()) && Date.Equals(((NSDate)obj).Date);
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSDate" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode() => Date.GetHashCode();
|
|
||||||
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<date>");
|
|
||||||
xml.Append(MakeDateString(Date));
|
|
||||||
xml.Append("</date>");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x33);
|
|
||||||
outPlist.WriteDouble((Date - EPOCH).TotalSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Generates a string representation of the date.</summary>
|
|
||||||
/// <returns>A string representation of the date.</returns>
|
|
||||||
public override string ToString() => Date.ToString();
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append("\"");
|
|
||||||
ascii.Append(MakeDateString(Date));
|
|
||||||
ascii.Append("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append("<*D");
|
|
||||||
ascii.Append(MakeDateStringGnuStep(Date));
|
|
||||||
ascii.Append(">");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSDate" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSDate date)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int equality = DateTime.Compare(Date, date.Date);
|
|
||||||
|
|
||||||
return equality == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator DateTime(NSDate value) => value.Date;
|
|
||||||
|
|
||||||
public static explicit operator NSDate(DateTime value) => new(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x33);
|
||||||
|
outPlist.WriteDouble((Date - EPOCH).TotalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Generates a string representation of the date.</summary>
|
||||||
|
/// <returns>A string representation of the date.</returns>
|
||||||
|
public override string ToString() => Date.ToString();
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append("\"");
|
||||||
|
ascii.Append(MakeDateString(Date));
|
||||||
|
ascii.Append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append("<*D");
|
||||||
|
ascii.Append(MakeDateStringGnuStep(Date));
|
||||||
|
ascii.Append(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSDate" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSDate date) return false;
|
||||||
|
|
||||||
|
int equality = DateTime.Compare(Date, date.Date);
|
||||||
|
|
||||||
|
return equality == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator DateTime(NSDate value) => value.Date;
|
||||||
|
|
||||||
|
public static explicit operator NSDate(DateTime value) => new(value);
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -27,197 +27,183 @@ 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>
|
/// <summary>
|
||||||
/// @author Daniel Dreibrodt
|
/// Indicates that the number's value is an integer. The number is stored as a .NET <see cref="long" />. Its
|
||||||
/// @author Natalia Portillo
|
/// original value could have been char, short, int, long or even long long.
|
||||||
public class NSNumber : NSObject, IComparable
|
/// </summary>
|
||||||
|
public const int INTEGER = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the number's value is a real number. The number is stored as a .NET <see cref="double" />. Its
|
||||||
|
/// original value could have been float or double.
|
||||||
|
/// </summary>
|
||||||
|
public const int REAL = 1;
|
||||||
|
|
||||||
|
/// <summary>Indicates that the number's value is bool.</summary>
|
||||||
|
public const int BOOLEAN = 2;
|
||||||
|
readonly bool boolValue;
|
||||||
|
readonly double doubleValue;
|
||||||
|
|
||||||
|
readonly long longValue;
|
||||||
|
|
||||||
|
//Holds the current type of this number
|
||||||
|
readonly int type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses integers and real numbers from their binary representation.
|
||||||
|
/// <i>Note: real numbers are not yet supported.</i>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes">The binary representation</param>
|
||||||
|
/// <param name="type">The type of number</param>
|
||||||
|
/// <seealso cref="INTEGER" />
|
||||||
|
/// <seealso cref="REAL" />
|
||||||
|
public NSNumber(ReadOnlySpan<byte> bytes, int type)
|
||||||
{
|
{
|
||||||
/// <summary>
|
switch(type)
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
public const int INTEGER = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that the number's value is a real number. The number is stored as a .NET <see cref="double" />. Its
|
|
||||||
/// original value could have been float or double.
|
|
||||||
/// </summary>
|
|
||||||
public const int REAL = 1;
|
|
||||||
|
|
||||||
/// <summary>Indicates that the number's value is bool.</summary>
|
|
||||||
public const int BOOLEAN = 2;
|
|
||||||
readonly bool boolValue;
|
|
||||||
readonly double doubleValue;
|
|
||||||
|
|
||||||
readonly long longValue;
|
|
||||||
|
|
||||||
//Holds the current type of this number
|
|
||||||
readonly int type;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses integers and real numbers from their binary representation.
|
|
||||||
/// <i>Note: real numbers are not yet supported.</i>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The binary representation</param>
|
|
||||||
/// <param name="type">The type of number</param>
|
|
||||||
/// <seealso cref="INTEGER" />
|
|
||||||
/// <seealso cref="REAL" />
|
|
||||||
public NSNumber(ReadOnlySpan<byte> bytes, int type)
|
|
||||||
{
|
{
|
||||||
switch(type)
|
case INTEGER:
|
||||||
{
|
doubleValue = longValue = BinaryPropertyListParser.ParseLong(bytes);
|
||||||
case INTEGER:
|
|
||||||
doubleValue = longValue = BinaryPropertyListParser.ParseLong(bytes);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REAL:
|
case REAL:
|
||||||
doubleValue = BinaryPropertyListParser.ParseDouble(bytes);
|
doubleValue = BinaryPropertyListParser.ParseDouble(bytes);
|
||||||
longValue = (long)Math.Round(doubleValue);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: throw new ArgumentException("Type argument is not valid.", nameof(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NSNumber(string text, int type)
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case INTEGER:
|
|
||||||
{
|
|
||||||
doubleValue = longValue = long.Parse(text, CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REAL:
|
|
||||||
{
|
|
||||||
doubleValue = double.Parse(text, CultureInfo.InvariantCulture);
|
|
||||||
longValue = (long)Math.Round(doubleValue);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Type argument is not valid.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates a number from its textual representation.</summary>
|
|
||||||
/// <param name="text">The textual representation of the number.</param>
|
|
||||||
/// <seealso cref="bool.Parse(string)" />
|
|
||||||
/// <seealso cref="long.Parse(string)" />
|
|
||||||
/// <seealso cref="double.Parse(string, IFormatProvider)" />
|
|
||||||
public NSNumber(string text)
|
|
||||||
{
|
|
||||||
if(text == null)
|
|
||||||
throw new ArgumentException("The given string is null and cannot be parsed as number.");
|
|
||||||
|
|
||||||
if(text.StartsWith("0x") &&
|
|
||||||
long.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture,
|
|
||||||
out long l))
|
|
||||||
{
|
|
||||||
doubleValue = longValue = l;
|
|
||||||
type = INTEGER;
|
|
||||||
}
|
|
||||||
else if(long.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out l))
|
|
||||||
{
|
|
||||||
doubleValue = longValue = l;
|
|
||||||
type = INTEGER;
|
|
||||||
}
|
|
||||||
else if(double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out double d))
|
|
||||||
{
|
|
||||||
doubleValue = d;
|
|
||||||
longValue = (long)Math.Round(doubleValue);
|
longValue = (long)Math.Round(doubleValue);
|
||||||
type = REAL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool isTrue = string.Equals(text, "true", StringComparison.CurrentCultureIgnoreCase) ||
|
|
||||||
string.Equals(text, "yes", StringComparison.CurrentCultureIgnoreCase);
|
|
||||||
|
|
||||||
bool isFalse = string.Equals(text, "false", StringComparison.CurrentCultureIgnoreCase) ||
|
break;
|
||||||
string.Equals(text, "no", StringComparison.CurrentCultureIgnoreCase);
|
|
||||||
|
|
||||||
if(isTrue || isFalse)
|
default:
|
||||||
{
|
throw new ArgumentException("Type argument is not valid.", nameof(type));
|
||||||
type = BOOLEAN;
|
|
||||||
boolValue = isTrue;
|
|
||||||
doubleValue = longValue = boolValue ? 1 : 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new
|
|
||||||
ArgumentException("The given string neither represents a double, an int nor a bool value.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates an integer number.</summary>
|
this.type = type;
|
||||||
/// <param name="i">The integer value.</param>
|
}
|
||||||
public NSNumber(int i)
|
|
||||||
|
public NSNumber(string text, int type)
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
{
|
{
|
||||||
doubleValue = longValue = i;
|
case INTEGER:
|
||||||
type = INTEGER;
|
{
|
||||||
|
doubleValue = longValue = long.Parse(text, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REAL:
|
||||||
|
{
|
||||||
|
doubleValue = double.Parse(text, CultureInfo.InvariantCulture);
|
||||||
|
longValue = (long)Math.Round(doubleValue);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Type argument is not valid.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates an integer number.</summary>
|
this.type = type;
|
||||||
/// <param name="l">The long integer value.</param>
|
}
|
||||||
public NSNumber(long l)
|
|
||||||
|
/// <summary>Creates a number from its textual representation.</summary>
|
||||||
|
/// <param name="text">The textual representation of the number.</param>
|
||||||
|
/// <seealso cref="bool.Parse(string)" />
|
||||||
|
/// <seealso cref="long.Parse(string)" />
|
||||||
|
/// <seealso cref="double.Parse(string, IFormatProvider)" />
|
||||||
|
public NSNumber(string text)
|
||||||
|
{
|
||||||
|
if(text == null) throw new ArgumentException("The given string is null and cannot be parsed as number.");
|
||||||
|
|
||||||
|
if(text.StartsWith("0x") &&
|
||||||
|
long.TryParse(text.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long l))
|
||||||
{
|
{
|
||||||
doubleValue = longValue = l;
|
doubleValue = longValue = l;
|
||||||
type = INTEGER;
|
type = INTEGER;
|
||||||
}
|
}
|
||||||
|
else if(long.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out l))
|
||||||
/// <summary>Creates a real number.</summary>
|
|
||||||
/// <param name="d">The real value.</param>
|
|
||||||
public NSNumber(double d)
|
|
||||||
{
|
{
|
||||||
longValue = (long)(doubleValue = d);
|
doubleValue = longValue = l;
|
||||||
type = REAL;
|
type = INTEGER;
|
||||||
}
|
}
|
||||||
|
else if(double.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out double d))
|
||||||
/// <summary>Creates a bool number.</summary>
|
|
||||||
/// <param name="b">The bool value.</param>
|
|
||||||
public NSNumber(bool b)
|
|
||||||
{
|
{
|
||||||
boolValue = b;
|
doubleValue = d;
|
||||||
doubleValue = longValue = b ? 1 : 0;
|
longValue = (long)Math.Round(doubleValue);
|
||||||
type = BOOLEAN;
|
type = REAL;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <summary>Compares the current <see cref="Claunia.PropertyList.NSNumber" /> to the specified object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// 0 if the numbers are equal, 1 if the current <see cref="Claunia.PropertyList.NSNumber" /> is greater than the
|
|
||||||
/// argument and -1 if it is less, or the argument is not a number.
|
|
||||||
/// </returns>
|
|
||||||
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSNumber" />.</param>
|
|
||||||
public int CompareTo(object o)
|
|
||||||
{
|
{
|
||||||
double x = ToDouble();
|
bool isTrue = string.Equals(text, "true", StringComparison.CurrentCultureIgnoreCase) ||
|
||||||
double y;
|
string.Equals(text, "yes", StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
if(o is NSNumber num)
|
bool isFalse = string.Equals(text, "false", StringComparison.CurrentCultureIgnoreCase) ||
|
||||||
|
string.Equals(text, "no", StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
|
if(isTrue || isFalse)
|
||||||
{
|
{
|
||||||
y = num.ToDouble();
|
type = BOOLEAN;
|
||||||
|
boolValue = isTrue;
|
||||||
return x < y
|
doubleValue = longValue = boolValue ? 1 : 0;
|
||||||
? -1
|
|
||||||
: x == y
|
|
||||||
? 0
|
|
||||||
: 1;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
throw new ArgumentException("The given string neither represents a double, an int nor a bool value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!IsNumber(o))
|
/// <summary>Creates an integer number.</summary>
|
||||||
return -1;
|
/// <param name="i">The integer value.</param>
|
||||||
|
public NSNumber(int i)
|
||||||
|
{
|
||||||
|
doubleValue = longValue = i;
|
||||||
|
type = INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
y = GetDoubleFromObject(o);
|
/// <summary>Creates an integer number.</summary>
|
||||||
|
/// <param name="l">The long integer value.</param>
|
||||||
|
public NSNumber(long l)
|
||||||
|
{
|
||||||
|
doubleValue = longValue = l;
|
||||||
|
type = INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a real number.</summary>
|
||||||
|
/// <param name="d">The real value.</param>
|
||||||
|
public NSNumber(double d)
|
||||||
|
{
|
||||||
|
longValue = (long)(doubleValue = d);
|
||||||
|
type = REAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a bool number.</summary>
|
||||||
|
/// <param name="b">The bool value.</param>
|
||||||
|
public NSNumber(bool b)
|
||||||
|
{
|
||||||
|
boolValue = b;
|
||||||
|
doubleValue = longValue = b ? 1 : 0;
|
||||||
|
type = BOOLEAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Compares the current <see cref="Claunia.PropertyList.NSNumber" /> to the specified object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// 0 if the numbers are equal, 1 if the current <see cref="Claunia.PropertyList.NSNumber" /> is greater than the
|
||||||
|
/// argument and -1 if it is less, or the argument is not a number.
|
||||||
|
/// </returns>
|
||||||
|
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSNumber" />.</param>
|
||||||
|
public int CompareTo(object o)
|
||||||
|
{
|
||||||
|
double x = ToDouble();
|
||||||
|
double y;
|
||||||
|
|
||||||
|
if(o is NSNumber num)
|
||||||
|
{
|
||||||
|
y = num.ToDouble();
|
||||||
|
|
||||||
return x < y
|
return x < y
|
||||||
? -1
|
? -1
|
||||||
@@ -226,320 +212,329 @@ namespace Claunia.PropertyList
|
|||||||
: 1;
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Gets the type of this number's value.</summary>
|
if(!IsNumber(o)) return -1;
|
||||||
/// <returns>The type flag.</returns>
|
|
||||||
/// <seealso cref="BOOLEAN" />
|
|
||||||
/// <seealso cref="INTEGER" />
|
|
||||||
/// <seealso cref="REAL" />
|
|
||||||
public int GetNSNumberType() => type;
|
|
||||||
|
|
||||||
/// <summary>Checks whether the value of this NSNumber is a bool.</summary>
|
y = GetDoubleFromObject(o);
|
||||||
/// <returns>Whether the number's value is a bool.</returns>
|
|
||||||
public bool isBoolean() => type == BOOLEAN;
|
|
||||||
|
|
||||||
/// <summary>Checks whether the value of this NSNumber is an integer.</summary>
|
return x < y
|
||||||
/// <returns>Whether the number's value is an integer.</returns>
|
? -1
|
||||||
public bool isInteger() => type == INTEGER;
|
: x == y
|
||||||
|
? 0
|
||||||
/// <summary>Checks whether the value of this NSNumber is a real number.</summary>
|
: 1;
|
||||||
/// <returns>Whether the number's value is a real number.</returns>
|
|
||||||
public bool isReal() => type == REAL;
|
|
||||||
|
|
||||||
/// <summary>The number's bool value.</summary>
|
|
||||||
/// <returns><c>true</c> if the value is true or non-zero, <c>false</c> otherwise.</returns>
|
|
||||||
public bool ToBool()
|
|
||||||
{
|
|
||||||
if(type == BOOLEAN)
|
|
||||||
return boolValue;
|
|
||||||
|
|
||||||
return longValue != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>The number's long value.</summary>
|
|
||||||
/// <returns>The value of the number as long</returns>
|
|
||||||
public long ToLong() => longValue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number's int value.
|
|
||||||
/// <i>
|
|
||||||
/// Note: Even though the number's type might be INTEGER it can be larger than a Java int. Use intValue() only if
|
|
||||||
/// you are certain that it contains a number from the int range. Otherwise the value might be inaccurate.
|
|
||||||
/// </i>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The value of the number as int.</returns>
|
|
||||||
public int ToInt() => (int)longValue;
|
|
||||||
|
|
||||||
/// <summary>The number's double value.</summary>
|
|
||||||
/// <returns>The value of the number as double.</returns>
|
|
||||||
public double ToDouble() => doubleValue;
|
|
||||||
|
|
||||||
/// <summary>The number's float value. WARNING: Possible loss of precision if the value is outside the float range.</summary>
|
|
||||||
/// <returns>The value of the number as float.</returns>
|
|
||||||
public float floatValue() => (float)doubleValue;
|
|
||||||
|
|
||||||
/// <summary>Checks whether the other object is a NSNumber of the same value.</summary>
|
|
||||||
/// <param name="obj">The object to compare to.</param>
|
|
||||||
/// <returns>Whether the objects are equal in terms of numeric value and type.</returns>
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSNumber number)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return type == number.type && longValue == number.longValue && doubleValue == number.doubleValue &&
|
|
||||||
boolValue == number.boolValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSNumber" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
int hash = type;
|
|
||||||
hash = (37 * hash) + (int)(longValue ^ ((uint)longValue >> 32));
|
|
||||||
|
|
||||||
hash = (37 * hash) + (int)(BitConverter.DoubleToInt64Bits(doubleValue) ^
|
|
||||||
(uint)(BitConverter.DoubleToInt64Bits(doubleValue) >> 32));
|
|
||||||
|
|
||||||
hash = (37 * hash) + (ToBool() ? 1 : 0);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="System.String" /> that represents the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A <see cref="System.String" /> that represents the current <see cref="Claunia.PropertyList.NSNumber" />.</returns>
|
|
||||||
public override string ToString() => type switch
|
|
||||||
{
|
|
||||||
INTEGER => ToLong().ToString(),
|
|
||||||
REAL => ToDouble().ToString("R", CultureInfo.InvariantCulture),
|
|
||||||
BOOLEAN => ToBool().ToString(),
|
|
||||||
_ => base.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case INTEGER:
|
|
||||||
{
|
|
||||||
xml.Append("<integer>");
|
|
||||||
xml.Append(ToLong());
|
|
||||||
xml.Append("</integer>");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REAL:
|
|
||||||
{
|
|
||||||
xml.Append("<real>");
|
|
||||||
|
|
||||||
if(doubleValue == 0)
|
|
||||||
xml.Append("0.0");
|
|
||||||
else
|
|
||||||
xml.Append(ToDouble().ToString("R", CultureInfo.InvariantCulture));
|
|
||||||
|
|
||||||
xml.Append("</real>");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BOOLEAN:
|
|
||||||
{
|
|
||||||
xml.Append(ToBool() ? "<true/>" : "<false/>");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
switch(GetNSNumberType())
|
|
||||||
{
|
|
||||||
case INTEGER:
|
|
||||||
{
|
|
||||||
if(ToLong() < 0)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x13);
|
|
||||||
outPlist.WriteBytes(ToLong(), 8);
|
|
||||||
}
|
|
||||||
else if(ToLong() <= 0xff)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x10);
|
|
||||||
outPlist.WriteBytes(ToLong(), 1);
|
|
||||||
}
|
|
||||||
else if(ToLong() <= 0xffff)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x11);
|
|
||||||
outPlist.WriteBytes(ToLong(), 2);
|
|
||||||
}
|
|
||||||
else if(ToLong() <= 0xffffffffL)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x12);
|
|
||||||
outPlist.WriteBytes(ToLong(), 4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outPlist.Write(0x13);
|
|
||||||
outPlist.WriteBytes(ToLong(), 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REAL:
|
|
||||||
{
|
|
||||||
outPlist.Write(0x23);
|
|
||||||
outPlist.WriteDouble(ToDouble());
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BOOLEAN:
|
|
||||||
{
|
|
||||||
outPlist.Write(ToBool() ? 0x09 : 0x08);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
|
|
||||||
if(type == BOOLEAN)
|
|
||||||
ascii.Append(boolValue ? "YES" : "NO");
|
|
||||||
else
|
|
||||||
ascii.Append(ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case INTEGER:
|
|
||||||
{
|
|
||||||
ascii.Append("<*I");
|
|
||||||
ascii.Append(ToString());
|
|
||||||
ascii.Append(">");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case REAL:
|
|
||||||
{
|
|
||||||
ascii.Append("<*R");
|
|
||||||
ascii.Append(ToString());
|
|
||||||
ascii.Append(">");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BOOLEAN:
|
|
||||||
{
|
|
||||||
ascii.Append(boolValue ? "<*BY>" : "<*BN>");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Determines if an object is a number. Substitutes .NET's Number class comparison</summary>
|
|
||||||
/// <returns><c>true</c> if it is a number.</returns>
|
|
||||||
/// <param name="o">Object.</param>
|
|
||||||
static bool IsNumber(object o) =>
|
|
||||||
o is sbyte or byte or short or ushort or int or uint or long or ulong or float or double or decimal;
|
|
||||||
|
|
||||||
static double GetDoubleFromObject(object o) => o switch
|
|
||||||
{
|
|
||||||
sbyte @sbyte => @sbyte,
|
|
||||||
byte b => b,
|
|
||||||
short s => s,
|
|
||||||
ushort @ushort => @ushort,
|
|
||||||
int i => i,
|
|
||||||
uint u => u,
|
|
||||||
long l => l,
|
|
||||||
ulong @ulong => @ulong,
|
|
||||||
float f => f,
|
|
||||||
double d => d,
|
|
||||||
decimal @decimal => (double)@decimal,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSNumber" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSNumber number)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(number.GetNSNumberType() != type)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
INTEGER => longValue == number.ToLong(),
|
|
||||||
REAL => doubleValue == number.ToDouble(),
|
|
||||||
BOOLEAN => boolValue == number.ToBool(),
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator ulong(NSNumber value) => (ulong)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator long(NSNumber value) => value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator uint(NSNumber value) => (uint)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator int(NSNumber value) => (int)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator ushort(NSNumber value) => (ushort)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator short(NSNumber value) => (short)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator byte(NSNumber value) => (byte)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator sbyte(NSNumber value) => (sbyte)value.longValue;
|
|
||||||
|
|
||||||
public static explicit operator double(NSNumber value) => value.doubleValue;
|
|
||||||
|
|
||||||
public static explicit operator float(NSNumber value) => (float)value.doubleValue;
|
|
||||||
|
|
||||||
public static explicit operator bool(NSNumber value) => value.boolValue;
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(ulong value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(long value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(uint value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(int value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(ushort value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(short value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(byte value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(sbyte value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(double value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(float value) => new(value);
|
|
||||||
|
|
||||||
public static explicit operator NSNumber(bool value) => new(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the type of this number's value.</summary>
|
||||||
|
/// <returns>The type flag.</returns>
|
||||||
|
/// <seealso cref="BOOLEAN" />
|
||||||
|
/// <seealso cref="INTEGER" />
|
||||||
|
/// <seealso cref="REAL" />
|
||||||
|
public int GetNSNumberType() => type;
|
||||||
|
|
||||||
|
/// <summary>Checks whether the value of this NSNumber is a bool.</summary>
|
||||||
|
/// <returns>Whether the number's value is a bool.</returns>
|
||||||
|
public bool isBoolean() => type == BOOLEAN;
|
||||||
|
|
||||||
|
/// <summary>Checks whether the value of this NSNumber is an integer.</summary>
|
||||||
|
/// <returns>Whether the number's value is an integer.</returns>
|
||||||
|
public bool isInteger() => type == INTEGER;
|
||||||
|
|
||||||
|
/// <summary>Checks whether the value of this NSNumber is a real number.</summary>
|
||||||
|
/// <returns>Whether the number's value is a real number.</returns>
|
||||||
|
public bool isReal() => type == REAL;
|
||||||
|
|
||||||
|
/// <summary>The number's bool value.</summary>
|
||||||
|
/// <returns><c>true</c> if the value is true or non-zero, <c>false</c> otherwise.</returns>
|
||||||
|
public bool ToBool()
|
||||||
|
{
|
||||||
|
if(type == BOOLEAN) return boolValue;
|
||||||
|
|
||||||
|
return longValue != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The number's long value.</summary>
|
||||||
|
/// <returns>The value of the number as long</returns>
|
||||||
|
public long ToLong() => longValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number's int value.
|
||||||
|
/// <i>
|
||||||
|
/// Note: Even though the number's type might be INTEGER it can be larger than a Java int. Use intValue() only if
|
||||||
|
/// you are certain that it contains a number from the int range. Otherwise the value might be inaccurate.
|
||||||
|
/// </i>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The value of the number as int.</returns>
|
||||||
|
public int ToInt() => (int)longValue;
|
||||||
|
|
||||||
|
/// <summary>The number's double value.</summary>
|
||||||
|
/// <returns>The value of the number as double.</returns>
|
||||||
|
public double ToDouble() => doubleValue;
|
||||||
|
|
||||||
|
/// <summary>The number's float value. WARNING: Possible loss of precision if the value is outside the float range.</summary>
|
||||||
|
/// <returns>The value of the number as float.</returns>
|
||||||
|
public float floatValue() => (float)doubleValue;
|
||||||
|
|
||||||
|
/// <summary>Checks whether the other object is a NSNumber of the same value.</summary>
|
||||||
|
/// <param name="obj">The object to compare to.</param>
|
||||||
|
/// <returns>Whether the objects are equal in terms of numeric value and type.</returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSNumber number) return false;
|
||||||
|
|
||||||
|
return type == number.type &&
|
||||||
|
longValue == number.longValue &&
|
||||||
|
doubleValue == number.doubleValue &&
|
||||||
|
boolValue == number.boolValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSNumber" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = type;
|
||||||
|
hash = 37 * hash + (int)(longValue ^ (uint)longValue >> 32);
|
||||||
|
|
||||||
|
hash = 37 * hash +
|
||||||
|
(int)(BitConverter.DoubleToInt64Bits(doubleValue) ^
|
||||||
|
(uint)(BitConverter.DoubleToInt64Bits(doubleValue) >> 32));
|
||||||
|
|
||||||
|
hash = 37 * hash + (ToBool() ? 1 : 0);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="System.String" /> that represents the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="System.String" /> that represents the current <see cref="Claunia.PropertyList.NSNumber" />.</returns>
|
||||||
|
public override string ToString() => type switch
|
||||||
|
{
|
||||||
|
INTEGER => ToLong().ToString(),
|
||||||
|
REAL => ToDouble().ToString("R", CultureInfo.InvariantCulture),
|
||||||
|
BOOLEAN => ToBool().ToString(),
|
||||||
|
_ => base.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case INTEGER:
|
||||||
|
{
|
||||||
|
xml.Append("<integer>");
|
||||||
|
xml.Append(ToLong());
|
||||||
|
xml.Append("</integer>");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REAL:
|
||||||
|
{
|
||||||
|
xml.Append("<real>");
|
||||||
|
|
||||||
|
if(doubleValue == 0)
|
||||||
|
xml.Append("0.0");
|
||||||
|
else
|
||||||
|
xml.Append(ToDouble().ToString("R", CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
xml.Append("</real>");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BOOLEAN:
|
||||||
|
{
|
||||||
|
xml.Append(ToBool() ? "<true/>" : "<false/>");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
switch(GetNSNumberType())
|
||||||
|
{
|
||||||
|
case INTEGER:
|
||||||
|
{
|
||||||
|
if(ToLong() < 0)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x13);
|
||||||
|
outPlist.WriteBytes(ToLong(), 8);
|
||||||
|
}
|
||||||
|
else if(ToLong() <= 0xff)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x10);
|
||||||
|
outPlist.WriteBytes(ToLong(), 1);
|
||||||
|
}
|
||||||
|
else if(ToLong() <= 0xffff)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x11);
|
||||||
|
outPlist.WriteBytes(ToLong(), 2);
|
||||||
|
}
|
||||||
|
else if(ToLong() <= 0xffffffffL)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x12);
|
||||||
|
outPlist.WriteBytes(ToLong(), 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outPlist.Write(0x13);
|
||||||
|
outPlist.WriteBytes(ToLong(), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REAL:
|
||||||
|
{
|
||||||
|
outPlist.Write(0x23);
|
||||||
|
outPlist.WriteDouble(ToDouble());
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BOOLEAN:
|
||||||
|
{
|
||||||
|
outPlist.Write(ToBool() ? 0x09 : 0x08);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
|
||||||
|
if(type == BOOLEAN)
|
||||||
|
ascii.Append(boolValue ? "YES" : "NO");
|
||||||
|
else
|
||||||
|
ascii.Append(ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case INTEGER:
|
||||||
|
{
|
||||||
|
ascii.Append("<*I");
|
||||||
|
ascii.Append(ToString());
|
||||||
|
ascii.Append(">");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REAL:
|
||||||
|
{
|
||||||
|
ascii.Append("<*R");
|
||||||
|
ascii.Append(ToString());
|
||||||
|
ascii.Append(">");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BOOLEAN:
|
||||||
|
{
|
||||||
|
ascii.Append(boolValue ? "<*BY>" : "<*BN>");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines if an object is a number. Substitutes .NET's Number class comparison</summary>
|
||||||
|
/// <returns><c>true</c> if it is a number.</returns>
|
||||||
|
/// <param name="o">Object.</param>
|
||||||
|
static bool IsNumber(object o) =>
|
||||||
|
o is sbyte or byte or short or ushort or int or uint or long or ulong or float or double or decimal;
|
||||||
|
|
||||||
|
static double GetDoubleFromObject(object o) => o switch
|
||||||
|
{
|
||||||
|
sbyte @sbyte => @sbyte,
|
||||||
|
byte b => b,
|
||||||
|
short s => s,
|
||||||
|
ushort @ushort => @ushort,
|
||||||
|
int i => i,
|
||||||
|
uint u => u,
|
||||||
|
long l => l,
|
||||||
|
ulong @ulong => @ulong,
|
||||||
|
float f => f,
|
||||||
|
double d => d,
|
||||||
|
decimal @decimal => (double)@decimal,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSNumber" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSNumber" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSNumber number) return false;
|
||||||
|
|
||||||
|
if(number.GetNSNumberType() != type) return false;
|
||||||
|
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
INTEGER => longValue == number.ToLong(),
|
||||||
|
REAL => doubleValue == number.ToDouble(),
|
||||||
|
BOOLEAN => boolValue == number.ToBool(),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator ulong(NSNumber value) => (ulong)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator long(NSNumber value) => value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator uint(NSNumber value) => (uint)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator int(NSNumber value) => (int)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator ushort(NSNumber value) => (ushort)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator short(NSNumber value) => (short)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator byte(NSNumber value) => (byte)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator sbyte(NSNumber value) => (sbyte)value.longValue;
|
||||||
|
|
||||||
|
public static explicit operator double(NSNumber value) => value.doubleValue;
|
||||||
|
|
||||||
|
public static explicit operator float(NSNumber value) => (float)value.doubleValue;
|
||||||
|
|
||||||
|
public static explicit operator bool(NSNumber value) => value.boolValue;
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(ulong value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(long value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(uint value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(int value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(ushort value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(short value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(byte value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(sbyte value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(double value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(float value) => new(value);
|
||||||
|
|
||||||
|
public static explicit operator NSNumber(bool value) => new(value);
|
||||||
}
|
}
|
||||||
@@ -27,426 +27,401 @@ 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>
|
/// <summary>
|
||||||
/// <para>Abstract interface for any object contained in a property list.</para>
|
/// The newline character used for generating the XML output. To maintain compatibility with the Apple format,
|
||||||
/// <para>The names and functions of the various objects orient themselves towards Apple's Cocoa API.</para>
|
/// only a newline character is used (as opposed to cr+lf which is normally used on Windows).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// @author Daniel Dreibrodt
|
internal static readonly string NEWLINE = "\n";
|
||||||
/// @author Natalia Portillo
|
|
||||||
public abstract class NSObject
|
/// <summary>The indentation character used for generating the XML output. This is the tabulator character.</summary>
|
||||||
|
static readonly string INDENT = "\t";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum length of the text lines to be used when generating ASCII property lists. But this number is only
|
||||||
|
/// a guideline it is not guaranteed that it will not be overstepped.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly int ASCII_LINE_LENGTH = 80;
|
||||||
|
|
||||||
|
/// <summary>Generates the XML representation of the object (without XML headers or enclosing plist-tags).</summary>
|
||||||
|
/// <param name="xml">The StringBuilder onto which the XML representation is appended.</param>
|
||||||
|
/// <param name="level">The indentation level of the object.</param>
|
||||||
|
internal abstract void ToXml(StringBuilder xml, int level);
|
||||||
|
|
||||||
|
/// <summary>Assigns IDs to all the objects in this NSObject subtree.</summary>
|
||||||
|
/// <param name="outPlist">The writer object that handles the binary serialization.</param>
|
||||||
|
internal virtual void AssignIDs(BinaryPropertyListWriter outPlist) => outPlist.AssignID(this);
|
||||||
|
|
||||||
|
/// <summary>Generates the binary representation of the object.</summary>
|
||||||
|
/// <param name="outPlist">The output stream to serialize the object to.</param>
|
||||||
|
internal abstract void ToBinary(BinaryPropertyListWriter outPlist);
|
||||||
|
|
||||||
|
/// <summary>Generates a valid XML property list including headers using this object as root.</summary>
|
||||||
|
/// <returns>The XML representation of the property list including XML header and doctype information.</returns>
|
||||||
|
public string ToXmlPropertyList()
|
||||||
{
|
{
|
||||||
/// <summary>
|
var xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
/// The newline character used for generating the XML output. To maintain compatibility with the Apple format,
|
xml.Append(NEWLINE);
|
||||||
/// only a newline character is used (as opposed to cr+lf which is normally used on Windows).
|
xml.Append("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
|
||||||
/// </summary>
|
xml.Append(NEWLINE);
|
||||||
internal static readonly string NEWLINE = "\n";
|
xml.Append("<plist version=\"1.0\">");
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
ToXml(xml, 0);
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
xml.Append("</plist>");
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
|
||||||
/// <summary>The indentation character used for generating the XML output. This is the tabulator character.</summary>
|
return xml.ToString();
|
||||||
static readonly string INDENT = "\t";
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum length of the text lines to be used when generating ASCII property lists. But this number is only
|
/// Generates the ASCII representation of this object. The generated ASCII representation does not end with a
|
||||||
/// a guideline it is not guaranteed that it will not be overstepped.
|
/// newline. Complies with
|
||||||
/// </summary>
|
/// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
||||||
internal static readonly int ASCII_LINE_LENGTH = 80;
|
/// </summary>
|
||||||
|
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
||||||
|
/// <param name="level">The indentation level of the object.</param>
|
||||||
|
internal abstract void ToASCII(StringBuilder ascii, int level);
|
||||||
|
|
||||||
/// <summary>Generates the XML representation of the object (without XML headers or enclosing plist-tags).</summary>
|
/// <summary>
|
||||||
/// <param name="xml">The StringBuilder onto which the XML representation is appended.</param>
|
/// Generates the ASCII representation of this object in the GnuStep format. The generated ASCII representation
|
||||||
/// <param name="level">The indentation level of the object.</param>
|
/// does not end with a newline.
|
||||||
internal abstract void ToXml(StringBuilder xml, int level);
|
/// </summary>
|
||||||
|
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
||||||
|
/// <param name="level">The indentation level of the object.</param>
|
||||||
|
internal abstract void ToASCIIGnuStep(StringBuilder ascii, int level);
|
||||||
|
|
||||||
/// <summary>Assigns IDs to all the objects in this NSObject subtree.</summary>
|
/// <summary>
|
||||||
/// <param name="outPlist">The writer object that handles the binary serialization.</param>
|
/// Helper method that adds correct indentation to the xml output. Calling this method will add <c>level</c>
|
||||||
internal virtual void AssignIDs(BinaryPropertyListWriter outPlist) => outPlist.AssignID(this);
|
/// number of tab characters to the <c>xml</c> string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xml">The string builder for the XML document.</param>
|
||||||
|
/// <param name="level">The level of indentation.</param>
|
||||||
|
internal static void Indent(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < level; i++) xml.Append(INDENT);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Generates the binary representation of the object.</summary>
|
/// <summary>Wraps the given value inside a NSObject.</summary>
|
||||||
/// <param name="outPlist">The output stream to serialize the object to.</param>
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
internal abstract void ToBinary(BinaryPropertyListWriter outPlist);
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
public static NSNumber Wrap(long value) => new(value);
|
||||||
|
|
||||||
/// <summary>Generates a valid XML property list including headers using this object as root.</summary>
|
/// <summary>Wraps the given value inside a NSObject.</summary>
|
||||||
/// <returns>The XML representation of the property list including XML header and doctype information.</returns>
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
public string ToXmlPropertyList()
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
public static NSNumber Wrap(double value) => new(value);
|
||||||
|
|
||||||
|
/// <summary>Wraps the given value inside a NSObject.</summary>
|
||||||
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
public static NSNumber Wrap(bool value) => new(value);
|
||||||
|
|
||||||
|
/// <summary>Wraps the given value inside a NSObject.</summary>
|
||||||
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
public static NSData Wrap(byte[] value) => new(value);
|
||||||
|
|
||||||
|
/// <summary>Creates a NSArray with the contents of the given array.</summary>
|
||||||
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
/// <exception cref="SystemException">When one of the objects contained in the array cannot be represented by a NSObject.</exception>
|
||||||
|
public static NSArray Wrap(object[] value)
|
||||||
|
{
|
||||||
|
var arr = new NSArray(value.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < value.Length; i++) arr.Add(Wrap(value[i]));
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a NSDictionary with the contents of the given map.</summary>
|
||||||
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
/// <exception cref="SystemException">When one of the values contained in the map cannot be represented by a NSObject.</exception>
|
||||||
|
public static NSDictionary Wrap(Dictionary<string, object> value)
|
||||||
|
{
|
||||||
|
var dict = new NSDictionary();
|
||||||
|
|
||||||
|
foreach(KeyValuePair<string, object> kvp in value) dict.Add(kvp.Key, Wrap(kvp.Value));
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a NSSet with the contents of this set.</summary>
|
||||||
|
/// <param name="value">The value to represent as a NSObject.</param>
|
||||||
|
/// <returns>A NSObject representing the given value.</returns>
|
||||||
|
/// <exception cref="SystemException">When one of the values contained in the map cannot be represented by a NSObject.</exception>
|
||||||
|
public static NSSet Wrap(List<object> value)
|
||||||
|
{
|
||||||
|
var set = new NSSet();
|
||||||
|
|
||||||
|
foreach(object o in value) set.AddObject(Wrap(o));
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Creates a NSObject representing the given .NET Object.</para>
|
||||||
|
/// <para>
|
||||||
|
/// Numerics of type <see cref="bool" />, <see cref="int" />, <see cref="long" />, <see cref="short" />,
|
||||||
|
/// <see cref="byte" />, <see cref="float" /> or <see cref="double" /> are wrapped as NSNumber objects.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Strings are wrapped as <see cref="NSString" /> objects and byte arrays as <see cref="NSData" /> objects.</para>
|
||||||
|
/// <para>DateTime objects are wrapped as <see cref="NSDate" /> objects.</para>
|
||||||
|
/// <para>Serializable classes are serialized and their data is stored in <see cref="NSData" /> objects.</para>
|
||||||
|
/// <para>
|
||||||
|
/// Arrays and Collection objects are converted to <see cref="NSArray" /> where each array member is wrapped into
|
||||||
|
/// a <see cref="NSObject" />.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Dictionaries are converted to <see cref="NSDictionary" />. Each key is converted to a string and each value
|
||||||
|
/// wrapped into a <see cref="NSObject" />.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="o">The object to represent.</param>
|
||||||
|
/// <returns>A NSObject equivalent to the given object.</returns>
|
||||||
|
public static NSObject Wrap(object o)
|
||||||
|
{
|
||||||
|
if(o == null) throw new NullReferenceException("A null object cannot be wrapped as a NSObject");
|
||||||
|
|
||||||
|
if(o is NSObject nsObject) return nsObject;
|
||||||
|
|
||||||
|
Type c = o.GetType();
|
||||||
|
|
||||||
|
if(typeof(bool).Equals(c)) return Wrap((bool)o);
|
||||||
|
|
||||||
|
if(typeof(byte).Equals(c)) return Wrap((byte)o);
|
||||||
|
|
||||||
|
if(typeof(short).Equals(c)) return Wrap((short)o);
|
||||||
|
|
||||||
|
if(typeof(int).Equals(c)) return Wrap((int)o);
|
||||||
|
|
||||||
|
if(typeof(long).IsAssignableFrom(c)) return Wrap((long)o);
|
||||||
|
|
||||||
|
if(typeof(float).Equals(c)) return Wrap((float)o);
|
||||||
|
|
||||||
|
if(typeof(double).IsAssignableFrom(c)) return Wrap((double)o);
|
||||||
|
|
||||||
|
if(typeof(string).Equals(c)) return new NSString((string)o);
|
||||||
|
|
||||||
|
if(typeof(DateTime).Equals(c)) return new NSDate((DateTime)o);
|
||||||
|
|
||||||
|
if(c.IsArray)
|
||||||
{
|
{
|
||||||
var xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
Type cc = c.GetElementType();
|
||||||
xml.Append(NEWLINE);
|
|
||||||
xml.Append("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
xml.Append("<plist version=\"1.0\">");
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
ToXml(xml, 0);
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
xml.Append("</plist>");
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
|
|
||||||
return xml.ToString();
|
if(cc.Equals(typeof(byte))) return Wrap((byte[])o);
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(bool)))
|
||||||
|
{
|
||||||
|
bool[] array = (bool[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(float)))
|
||||||
|
{
|
||||||
|
float[] array = (float[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(double)))
|
||||||
|
{
|
||||||
|
double[] array = (double[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(short)))
|
||||||
|
{
|
||||||
|
short[] array = (short[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(int)))
|
||||||
|
{
|
||||||
|
int[] array = (int[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cc.Equals(typeof(long)))
|
||||||
|
{
|
||||||
|
long[] array = (long[])o;
|
||||||
|
var nsa = new NSArray(array.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++) nsa.Add(Wrap(array[i]));
|
||||||
|
|
||||||
|
return nsa;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wrap((object[])o);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
if(typeof(Dictionary<string, object>).IsAssignableFrom(c))
|
||||||
/// Generates the ASCII representation of this object. The generated ASCII representation does not end with a
|
|
||||||
/// newline. Complies with
|
|
||||||
/// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
|
||||||
/// <param name="level">The indentation level of the object.</param>
|
|
||||||
internal abstract void ToASCII(StringBuilder ascii, int level);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates the ASCII representation of this object in the GnuStep format. The generated ASCII representation
|
|
||||||
/// does not end with a newline.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ascii">The StringBuilder onto which the ASCII representation is appended.</param>
|
|
||||||
/// <param name="level">The indentation level of the object.</param>
|
|
||||||
internal abstract void ToASCIIGnuStep(StringBuilder ascii, int level);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method that adds correct indentation to the xml output. Calling this method will add <c>level</c>
|
|
||||||
/// number of tab characters to the <c>xml</c> string.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="xml">The string builder for the XML document.</param>
|
|
||||||
/// <param name="level">The level of indentation.</param>
|
|
||||||
internal static void Indent(StringBuilder xml, int level)
|
|
||||||
{
|
{
|
||||||
for(int i = 0; i < level; i++)
|
var netDict = (Dictionary<string, object>)o;
|
||||||
xml.Append(INDENT);
|
var dict = new NSDictionary();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Wraps the given value inside a NSObject.</summary>
|
foreach(KeyValuePair<string, object> kvp in netDict) dict.Add(kvp.Key, Wrap(kvp.Value));
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
public static NSNumber Wrap(long value) => new(value);
|
|
||||||
|
|
||||||
/// <summary>Wraps the given value inside a NSObject.</summary>
|
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
public static NSNumber Wrap(double value) => new(value);
|
|
||||||
|
|
||||||
/// <summary>Wraps the given value inside a NSObject.</summary>
|
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
public static NSNumber Wrap(bool value) => new(value);
|
|
||||||
|
|
||||||
/// <summary>Wraps the given value inside a NSObject.</summary>
|
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
public static NSData Wrap(byte[] value) => new(value);
|
|
||||||
|
|
||||||
/// <summary>Creates a NSArray with the contents of the given array.</summary>
|
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
/// <exception cref="SystemException">When one of the objects contained in the array cannot be represented by a NSObject.</exception>
|
|
||||||
public static NSArray Wrap(object[] value)
|
|
||||||
{
|
|
||||||
var arr = new NSArray(value.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < value.Length; i++)
|
|
||||||
arr.Add(Wrap(value[i]));
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates a NSDictionary with the contents of the given map.</summary>
|
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
|
||||||
/// <exception cref="SystemException">When one of the values contained in the map cannot be represented by a NSObject.</exception>
|
|
||||||
public static NSDictionary Wrap(Dictionary<string, object> value)
|
|
||||||
{
|
|
||||||
var dict = new NSDictionary();
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, object> kvp in value)
|
|
||||||
dict.Add(kvp.Key, Wrap(kvp.Value));
|
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a NSSet with the contents of this set.</summary>
|
if(typeof(List<object>).IsAssignableFrom(c)) return Wrap(((List<object>)o).ToArray());
|
||||||
/// <param name="value">The value to represent as a NSObject.</param>
|
|
||||||
/// <returns>A NSObject representing the given value.</returns>
|
if(typeof(List<Dictionary<string, object>>).IsAssignableFrom(c))
|
||||||
/// <exception cref="SystemException">When one of the values contained in the map cannot be represented by a NSObject.</exception>
|
|
||||||
public static NSSet Wrap(List<object> value)
|
|
||||||
{
|
{
|
||||||
var set = new NSSet();
|
var list = new NSArray();
|
||||||
|
foreach(Dictionary<string, object> dict in (List<Dictionary<string, object>>)o) list.Add(Wrap(dict));
|
||||||
|
|
||||||
foreach(object o in value)
|
return list;
|
||||||
set.AddObject(Wrap(o));
|
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
throw new PropertyListException($"Cannot wrap an object of type {o.GetType().Name}.");
|
||||||
/// <para>Creates a NSObject representing the given .NET Object.</para>
|
|
||||||
/// <para>
|
|
||||||
/// Numerics of type <see cref="bool" />, <see cref="int" />, <see cref="long" />, <see cref="short" />,
|
|
||||||
/// <see cref="byte" />, <see cref="float" /> or <see cref="double" /> are wrapped as NSNumber objects.
|
|
||||||
/// </para>
|
|
||||||
/// <para>Strings are wrapped as <see cref="NSString" /> objects and byte arrays as <see cref="NSData" /> objects.</para>
|
|
||||||
/// <para>DateTime objects are wrapped as <see cref="NSDate" /> objects.</para>
|
|
||||||
/// <para>Serializable classes are serialized and their data is stored in <see cref="NSData" /> objects.</para>
|
|
||||||
/// <para>
|
|
||||||
/// Arrays and Collection objects are converted to <see cref="NSArray" /> where each array member is wrapped into
|
|
||||||
/// a <see cref="NSObject" />.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Dictionaries are converted to <see cref="NSDictionary" />. Each key is converted to a string and each value
|
|
||||||
/// wrapped into a <see cref="NSObject" />.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="o">The object to represent.</param>
|
|
||||||
/// <returns>A NSObject equivalent to the given object.</returns>
|
|
||||||
public static NSObject Wrap(object o)
|
|
||||||
{
|
|
||||||
if(o == null)
|
|
||||||
throw new NullReferenceException("A null object cannot be wrapped as a NSObject");
|
|
||||||
|
|
||||||
if(o is NSObject nsObject)
|
|
||||||
return nsObject;
|
|
||||||
|
|
||||||
Type c = o.GetType();
|
|
||||||
|
|
||||||
if(typeof(bool).Equals(c))
|
|
||||||
return Wrap((bool)o);
|
|
||||||
|
|
||||||
if(typeof(byte).Equals(c))
|
|
||||||
return Wrap((byte)o);
|
|
||||||
|
|
||||||
if(typeof(short).Equals(c))
|
|
||||||
return Wrap((short)o);
|
|
||||||
|
|
||||||
if(typeof(int).Equals(c))
|
|
||||||
return Wrap((int)o);
|
|
||||||
|
|
||||||
if(typeof(long).IsAssignableFrom(c))
|
|
||||||
return Wrap((long)o);
|
|
||||||
|
|
||||||
if(typeof(float).Equals(c))
|
|
||||||
return Wrap((float)o);
|
|
||||||
|
|
||||||
if(typeof(double).IsAssignableFrom(c))
|
|
||||||
return Wrap((double)o);
|
|
||||||
|
|
||||||
if(typeof(string).Equals(c))
|
|
||||||
return new NSString((string)o);
|
|
||||||
|
|
||||||
if(typeof(DateTime).Equals(c))
|
|
||||||
return new NSDate((DateTime)o);
|
|
||||||
|
|
||||||
if(c.IsArray)
|
|
||||||
{
|
|
||||||
Type cc = c.GetElementType();
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(byte)))
|
|
||||||
return Wrap((byte[])o);
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(bool)))
|
|
||||||
{
|
|
||||||
bool[] array = (bool[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(float)))
|
|
||||||
{
|
|
||||||
float[] array = (float[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(double)))
|
|
||||||
{
|
|
||||||
double[] array = (double[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(short)))
|
|
||||||
{
|
|
||||||
short[] array = (short[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(int)))
|
|
||||||
{
|
|
||||||
int[] array = (int[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cc.Equals(typeof(long)))
|
|
||||||
{
|
|
||||||
long[] array = (long[])o;
|
|
||||||
var nsa = new NSArray(array.Length);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
nsa.Add(Wrap(array[i]));
|
|
||||||
|
|
||||||
return nsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Wrap((object[])o);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof(Dictionary<string, object>).IsAssignableFrom(c))
|
|
||||||
{
|
|
||||||
Dictionary<string, object> netDict = (Dictionary<string, object>)o;
|
|
||||||
var dict = new NSDictionary();
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, object> kvp in netDict)
|
|
||||||
dict.Add(kvp.Key, Wrap(kvp.Value));
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof(List<object>).IsAssignableFrom(c))
|
|
||||||
return Wrap(((List<object>)o).ToArray());
|
|
||||||
|
|
||||||
if(typeof(List<Dictionary<string, object>>).IsAssignableFrom(c))
|
|
||||||
{
|
|
||||||
var list = new NSArray();
|
|
||||||
foreach(Dictionary<string, object> dict in (List<Dictionary<string, object>>)o)
|
|
||||||
list.Add(Wrap(dict));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new PropertyListException($"Cannot wrap an object of type {o.GetType().Name}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts this NSObject into an equivalent object of the .NET Runtime Environment.
|
|
||||||
/// <para><see cref="NSArray" /> objects are converted to arrays.</para>
|
|
||||||
/// <para>
|
|
||||||
/// <see cref="NSDictionary" /> objects are converted to objects extending the
|
|
||||||
/// <see cref="Dictionary{TKey, TValue}" /> class.
|
|
||||||
/// </para>
|
|
||||||
/// <para><see cref="NSSet" /> objects are converted to objects extending the <see cref="List{NSObject}" /> class.</para>
|
|
||||||
/// <para>
|
|
||||||
/// <see cref="NSNumber" /> objects are converted to primitive number values (<see cref="int" />,
|
|
||||||
/// <see cref="long" />, <see cref="double" /> or <see cref="bool" />).
|
|
||||||
/// </para>
|
|
||||||
/// <para><see cref="NSString" /> objects are converted to <see cref="string" /> objects.</para>
|
|
||||||
/// <para><see cref="NSData" /> objects are converted to <see cref="byte" /> arrays.</para>
|
|
||||||
/// <para><see cref="NSDate" /> objects are converted to <see cref="System.DateTime" /> objects.</para>
|
|
||||||
/// <para><see cref="UID" /> objects are converted to <see cref="byte" /> arrays.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A native .NET object representing this NSObject's value.</returns>
|
|
||||||
public object ToObject()
|
|
||||||
{
|
|
||||||
switch(this)
|
|
||||||
{
|
|
||||||
case NSArray:
|
|
||||||
{
|
|
||||||
var nsArray = (NSArray)this;
|
|
||||||
object[] array = new object[nsArray.Count];
|
|
||||||
|
|
||||||
for(int i = 0; i < nsArray.Count; i++)
|
|
||||||
array[i] = nsArray[i].ToObject();
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
case NSDictionary:
|
|
||||||
{
|
|
||||||
Dictionary<string, NSObject> dictA = ((NSDictionary)this).GetDictionary();
|
|
||||||
Dictionary<string, object> dictB = new(dictA.Count);
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, NSObject> kvp in dictA)
|
|
||||||
dictB.Add(kvp.Key, kvp.Value.ToObject());
|
|
||||||
|
|
||||||
return dictB;
|
|
||||||
}
|
|
||||||
case NSSet:
|
|
||||||
{
|
|
||||||
List<NSObject> setA = ((NSSet)this).GetSet();
|
|
||||||
List<object> setB = new();
|
|
||||||
|
|
||||||
foreach(NSObject o in setA)
|
|
||||||
setB.Add(o.ToObject());
|
|
||||||
|
|
||||||
return setB;
|
|
||||||
}
|
|
||||||
case NSNumber:
|
|
||||||
{
|
|
||||||
var num = (NSNumber)this;
|
|
||||||
|
|
||||||
switch(num.GetNSNumberType())
|
|
||||||
{
|
|
||||||
case NSNumber.INTEGER:
|
|
||||||
{
|
|
||||||
long longVal = num.ToLong();
|
|
||||||
|
|
||||||
if(longVal is > int.MaxValue or < int.MinValue)
|
|
||||||
return longVal;
|
|
||||||
|
|
||||||
return num.ToInt();
|
|
||||||
}
|
|
||||||
case NSNumber.REAL: return num.ToDouble();
|
|
||||||
case NSNumber.BOOLEAN: return num.ToBool();
|
|
||||||
default: return num.ToDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NSString: return ((NSString)this).Content;
|
|
||||||
case NSData: return ((NSData)this).Bytes;
|
|
||||||
case NSDate: return ((NSDate)this).Date;
|
|
||||||
case UID: return ((UID)this).Bytes;
|
|
||||||
default: return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
|
||||||
{
|
|
||||||
if(arrayA.Length != arrayB.Length)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < arrayA.Length; i++)
|
|
||||||
if(arrayA[i] != arrayB[i])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool ArrayEquals(IList<NSObject> arrayA, IList<NSObject> arrayB)
|
|
||||||
{
|
|
||||||
if(arrayA.Count != arrayB.Count)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for(int i = 0; i < arrayA.Count; i++)
|
|
||||||
if(arrayA[i] != arrayB[i])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Determines if the specific NSObject is the same as the NSObject overriding this method.</summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSObject" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSObject" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public abstract bool Equals(NSObject obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts this NSObject into an equivalent object of the .NET Runtime Environment.
|
||||||
|
/// <para><see cref="NSArray" /> objects are converted to arrays.</para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="NSDictionary" /> objects are converted to objects extending the
|
||||||
|
/// <see cref="Dictionary{TKey, TValue}" /> class.
|
||||||
|
/// </para>
|
||||||
|
/// <para><see cref="NSSet" /> objects are converted to objects extending the <see cref="List{NSObject}" /> class.</para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="NSNumber" /> objects are converted to primitive number values (<see cref="int" />,
|
||||||
|
/// <see cref="long" />, <see cref="double" /> or <see cref="bool" />).
|
||||||
|
/// </para>
|
||||||
|
/// <para><see cref="NSString" /> objects are converted to <see cref="string" /> objects.</para>
|
||||||
|
/// <para><see cref="NSData" /> objects are converted to <see cref="byte" /> arrays.</para>
|
||||||
|
/// <para><see cref="NSDate" /> objects are converted to <see cref="System.DateTime" /> objects.</para>
|
||||||
|
/// <para><see cref="UID" /> objects are converted to <see cref="byte" /> arrays.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A native .NET object representing this NSObject's value.</returns>
|
||||||
|
public object ToObject()
|
||||||
|
{
|
||||||
|
switch(this)
|
||||||
|
{
|
||||||
|
case NSArray:
|
||||||
|
{
|
||||||
|
var nsArray = (NSArray)this;
|
||||||
|
object[] array = new object[nsArray.Count];
|
||||||
|
|
||||||
|
for(int i = 0; i < nsArray.Count; i++) array[i] = nsArray[i].ToObject();
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
case NSDictionary:
|
||||||
|
{
|
||||||
|
Dictionary<string, NSObject> dictA = ((NSDictionary)this).GetDictionary();
|
||||||
|
Dictionary<string, object> dictB = new(dictA.Count);
|
||||||
|
|
||||||
|
foreach(KeyValuePair<string, NSObject> kvp in dictA) dictB.Add(kvp.Key, kvp.Value.ToObject());
|
||||||
|
|
||||||
|
return dictB;
|
||||||
|
}
|
||||||
|
case NSSet:
|
||||||
|
{
|
||||||
|
List<NSObject> setA = ((NSSet)this).GetSet();
|
||||||
|
List<object> setB = new();
|
||||||
|
|
||||||
|
foreach(NSObject o in setA) setB.Add(o.ToObject());
|
||||||
|
|
||||||
|
return setB;
|
||||||
|
}
|
||||||
|
case NSNumber:
|
||||||
|
{
|
||||||
|
var num = (NSNumber)this;
|
||||||
|
|
||||||
|
switch(num.GetNSNumberType())
|
||||||
|
{
|
||||||
|
case NSNumber.INTEGER:
|
||||||
|
{
|
||||||
|
long longVal = num.ToLong();
|
||||||
|
|
||||||
|
if(longVal is > int.MaxValue or < int.MinValue) return longVal;
|
||||||
|
|
||||||
|
return num.ToInt();
|
||||||
|
}
|
||||||
|
case NSNumber.REAL:
|
||||||
|
return num.ToDouble();
|
||||||
|
case NSNumber.BOOLEAN:
|
||||||
|
return num.ToBool();
|
||||||
|
default:
|
||||||
|
return num.ToDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NSString:
|
||||||
|
return ((NSString)this).Content;
|
||||||
|
case NSData:
|
||||||
|
return ((NSData)this).Bytes;
|
||||||
|
case NSDate:
|
||||||
|
return ((NSDate)this).Date;
|
||||||
|
case UID:
|
||||||
|
return ((UID)this).Bytes;
|
||||||
|
default:
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool ArrayEquals(byte[] arrayA, byte[] arrayB)
|
||||||
|
{
|
||||||
|
if(arrayA.Length != arrayB.Length) return false;
|
||||||
|
|
||||||
|
for(int i = 0; i < arrayA.Length; i++)
|
||||||
|
if(arrayA[i] != arrayB[i]) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool ArrayEquals(IList<NSObject> arrayA, IList<NSObject> arrayB)
|
||||||
|
{
|
||||||
|
if(arrayA.Count != arrayB.Count) return false;
|
||||||
|
|
||||||
|
for(int i = 0; i < arrayA.Count; i++)
|
||||||
|
if(arrayA[i] != arrayB[i]) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines if the specific NSObject is the same as the NSObject overriding this method.</summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSObject" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSObject" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public abstract bool Equals(NSObject obj);
|
||||||
}
|
}
|
||||||
@@ -28,374 +28,359 @@ 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>
|
readonly bool ordered;
|
||||||
/// <para>A set is an interface to an unordered collection of objects.</para>
|
readonly List<NSObject> set;
|
||||||
/// <para>This implementation uses a <see cref="List{T}" />as the underlying data structure.</para>
|
|
||||||
/// </summary>
|
/// <summary>Creates an empty unordered set.</summary>
|
||||||
/// @author Daniel Dreibrodt
|
public NSSet() => set = [];
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class NSSet : NSObject, IEnumerable
|
/// <summary>Creates an empty set.</summary>
|
||||||
|
/// <param name="ordered">Should the set be ordered on operations?</param>
|
||||||
|
public NSSet(bool ordered)
|
||||||
{
|
{
|
||||||
readonly bool ordered;
|
this.ordered = ordered;
|
||||||
readonly List<NSObject> set;
|
set = [];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Creates an empty unordered set.</summary>
|
/// <summary>Creates a set and fill it with the given objects.</summary>
|
||||||
public NSSet() => set = new List<NSObject>();
|
/// <param name="objects">The objects to populate the set.</param>
|
||||||
|
public NSSet(params NSObject[] objects) => set = [..objects];
|
||||||
|
|
||||||
/// <summary>Creates an empty set.</summary>
|
/// <summary>Creates a set and fill it with the given objects.</summary>
|
||||||
/// <param name="ordered">Should the set be ordered on operations?</param>
|
/// <param name="objects">The objects to populate the set.</param>
|
||||||
public NSSet(bool ordered)
|
/// <param name="ordered">Should the set be ordered on operations?</param>
|
||||||
{
|
public NSSet(bool ordered, params NSObject[] objects)
|
||||||
this.ordered = ordered;
|
{
|
||||||
set = new List<NSObject>();
|
this.ordered = ordered;
|
||||||
}
|
set = new List<NSObject>(objects);
|
||||||
|
|
||||||
/// <summary>Creates a set and fill it with the given objects.</summary>
|
if(ordered) set.Sort();
|
||||||
/// <param name="objects">The objects to populate the set.</param>
|
}
|
||||||
public NSSet(params NSObject[] objects) => set = new List<NSObject>(objects);
|
|
||||||
|
|
||||||
/// <summary>Creates a set and fill it with the given objects.</summary>
|
/// <summary>Gets the number of elements in the set.</summary>
|
||||||
/// <param name="objects">The objects to populate the set.</param>
|
/// <value>The number of elements in the set.</value>
|
||||||
/// <param name="ordered">Should the set be ordered on operations?</param>
|
public int Count
|
||||||
public NSSet(bool ordered, params NSObject[] objects)
|
{
|
||||||
{
|
get
|
||||||
this.ordered = ordered;
|
|
||||||
set = new List<NSObject>(objects);
|
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Gets the number of elements in the set.</summary>
|
|
||||||
/// <value>The number of elements in the set.</value>
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock(set)
|
|
||||||
return set.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an enumerator object that lets you iterate over all elements of the set. This is the equivalent to
|
|
||||||
/// <c>objectEnumerator</c> in the Cocoa implementation of NSSet.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The iterator for the set.</returns>
|
|
||||||
public IEnumerator GetEnumerator()
|
|
||||||
{
|
|
||||||
lock(set)
|
|
||||||
return set.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Adds an object to the set.</summary>
|
|
||||||
/// <param name="obj">The object to add.</param>
|
|
||||||
public void AddObject(NSObject obj)
|
|
||||||
{
|
{
|
||||||
lock(set)
|
lock(set)
|
||||||
{
|
{
|
||||||
set.Add(obj);
|
return set.Count;
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Removes an object from the set.</summary>
|
/// <summary>
|
||||||
/// <param name="obj">The object to remove.</param>
|
/// Returns an enumerator object that lets you iterate over all elements of the set. This is the equivalent to
|
||||||
public void RemoveObject(NSObject obj)
|
/// <c>objectEnumerator</c> in the Cocoa implementation of NSSet.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The iterator for the set.</returns>
|
||||||
|
public IEnumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
lock(set)
|
return set.GetEnumerator();
|
||||||
{
|
|
||||||
set.Remove(obj);
|
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Returns all objects contained in the set.</summary>
|
/// <summary>Adds an object to the set.</summary>
|
||||||
/// <returns>An array of all objects in the set.</returns>
|
/// <param name="obj">The object to add.</param>
|
||||||
public NSObject[] AllObjects()
|
public void AddObject(NSObject obj)
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
lock(set)
|
set.Add(obj);
|
||||||
return set.ToArray();
|
|
||||||
|
if(ordered) set.Sort();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Returns one of the objects in the set, or <c>null</c> if the set contains no objects.</summary>
|
/// <summary>Removes an object from the set.</summary>
|
||||||
/// <returns>The first object in the set, or <c>null</c> if the set is empty.</returns>
|
/// <param name="obj">The object to remove.</param>
|
||||||
public NSObject AnyObject()
|
public void RemoveObject(NSObject obj)
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
lock(set)
|
set.Remove(obj);
|
||||||
return set.Count == 0 ? null : set[0];
|
|
||||||
|
if(ordered) set.Sort();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Finds out whether a given object is contained in the set.</summary>
|
/// <summary>Returns all objects contained in the set.</summary>
|
||||||
/// <returns><c>true</c>, when the object was found, <c>false</c> otherwise.</returns>
|
/// <returns>An array of all objects in the set.</returns>
|
||||||
/// <param name="obj">The object to look for.</param>
|
public NSObject[] AllObjects()
|
||||||
public bool ContainsObject(NSObject obj) => set.Contains(obj);
|
{
|
||||||
|
lock(set)
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the set contains an object equal to a given object and returns that object if it is
|
|
||||||
/// present.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to look for.</param>
|
|
||||||
/// <returns>The object if it is present, <c>null</c> otherwise.</returns>
|
|
||||||
public NSObject Member(NSObject obj)
|
|
||||||
{
|
{
|
||||||
lock(set)
|
return set.ToArray();
|
||||||
{
|
|
||||||
foreach(NSObject o in set)
|
|
||||||
if(o.Equals(obj))
|
|
||||||
return o;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Finds out whether at least one object is present in both sets.</summary>
|
/// <summary>Returns one of the objects in the set, or <c>null</c> if the set contains no objects.</summary>
|
||||||
/// <returns><c>true</c> if the intersection of both sets is empty, <c>false</c> otherwise.</returns>
|
/// <returns>The first object in the set, or <c>null</c> if the set is empty.</returns>
|
||||||
/// <param name="otherSet">The other set.</param>
|
public NSObject AnyObject()
|
||||||
public bool IntersectsSet(NSSet otherSet)
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
lock(set)
|
return set.Count == 0 ? null : set[0];
|
||||||
{
|
|
||||||
foreach(NSObject o in set)
|
|
||||||
if(otherSet.ContainsObject(o))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Finds out if this set is a subset of the given set.</summary>
|
/// <summary>Finds out whether a given object is contained in the set.</summary>
|
||||||
/// <returns><c>true</c> if all elements in this set are also present in the other set, <c>false</c>otherwise.</returns>
|
/// <returns><c>true</c>, when the object was found, <c>false</c> otherwise.</returns>
|
||||||
/// <param name="otherSet">The other set.</param>
|
/// <param name="obj">The object to look for.</param>
|
||||||
public bool IsSubsetOfSet(NSSet otherSet)
|
public bool ContainsObject(NSObject obj) => set.Contains(obj);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the set contains an object equal to a given object and returns that object if it is
|
||||||
|
/// present.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to look for.</param>
|
||||||
|
/// <returns>The object if it is present, <c>null</c> otherwise.</returns>
|
||||||
|
public NSObject Member(NSObject obj)
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
lock(set)
|
|
||||||
{
|
|
||||||
foreach(NSObject o in set)
|
|
||||||
if(!otherSet.ContainsObject(o))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Gets the underlying data structure in which this NSSets stores its content.</summary>
|
|
||||||
/// <returns>A Set object.</returns>
|
|
||||||
internal List<NSObject> GetSet() => set;
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSSet" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
int hash = 7;
|
|
||||||
hash = (29 * hash) + (set != null ? set.GetHashCode() : 0);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="System.Object" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if(obj == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(GetType() != obj.GetType())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var other = (NSSet)obj;
|
|
||||||
|
|
||||||
return !(set != other.set && (set == null || !set.Equals(other.set)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the XML representation for this set. There is no official XML representation specified for sets. In
|
|
||||||
/// this implementation it is represented by an array.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="xml">The XML StringBuilder</param>
|
|
||||||
/// <param name="level">The indentation level</param>
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<array>");
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
foreach(NSObject o in set)
|
foreach(NSObject o in set)
|
||||||
{
|
if(o.Equals(obj)) return o;
|
||||||
o.ToXml(xml, level + 1);
|
|
||||||
xml.Append(NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Indent(xml, level);
|
return null;
|
||||||
xml.Append("</array>");
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal override void AssignIDs(BinaryPropertyListWriter outPlist)
|
/// <summary>Finds out whether at least one object is present in both sets.</summary>
|
||||||
|
/// <returns><c>true</c> if the intersection of both sets is empty, <c>false</c> otherwise.</returns>
|
||||||
|
/// <param name="otherSet">The other set.</param>
|
||||||
|
public bool IntersectsSet(NSSet otherSet)
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
base.AssignIDs(outPlist);
|
foreach(NSObject o in set)
|
||||||
|
if(otherSet.ContainsObject(o)) return true;
|
||||||
|
|
||||||
foreach(NSObject obj in set)
|
return false;
|
||||||
obj.AssignIDs(outPlist);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
/// <summary>Finds out if this set is a subset of the given set.</summary>
|
||||||
|
/// <returns><c>true</c> if all elements in this set are also present in the other set, <c>false</c>otherwise.</returns>
|
||||||
|
/// <param name="otherSet">The other set.</param>
|
||||||
|
public bool IsSubsetOfSet(NSSet otherSet)
|
||||||
|
{
|
||||||
|
lock(set)
|
||||||
{
|
{
|
||||||
if(ordered)
|
foreach(NSObject o in set)
|
||||||
{
|
if(!otherSet.ContainsObject(o)) return false;
|
||||||
set.Sort();
|
|
||||||
outPlist.WriteIntHeader(0xB, set.Count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
outPlist.WriteIntHeader(0xC, set.Count);
|
|
||||||
|
|
||||||
foreach(NSObject obj in set)
|
|
||||||
outPlist.WriteID(outPlist.GetID(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the ASCII representation of this set. There is no official ASCII representation for sets. In this
|
|
||||||
/// implementation sets are represented as arrays.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ascii">The ASCII file string builder</param>
|
|
||||||
/// <param name="level">The indentation level</param>
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
NSObject[] array = AllObjects();
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
|
||||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
{
|
|
||||||
Type objClass = array[i].GetType();
|
|
||||||
|
|
||||||
if((objClass.Equals(typeof(NSDictionary)) || objClass.Equals(typeof(NSArray)) ||
|
|
||||||
objClass.Equals(typeof(NSData))) &&
|
|
||||||
indexOfLastNewLine != ascii.Length)
|
|
||||||
{
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
array[i].ToASCII(ascii, level + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(i != 0)
|
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCII(ascii, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i != array.Length - 1)
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the ASCII representation of this set according to the GnuStep format. There is no official ASCII
|
|
||||||
/// representation for sets. In this implementation sets are represented as arrays.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ascii">The ASCII file string builder</param>
|
|
||||||
/// <param name="level">The indentation level</param>
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
|
|
||||||
if(ordered)
|
|
||||||
set.Sort();
|
|
||||||
|
|
||||||
NSObject[] array = AllObjects();
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
|
||||||
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
for(int i = 0; i < array.Length; i++)
|
|
||||||
{
|
|
||||||
if(array[i] is NSDictionary or NSArray or NSData &&
|
|
||||||
indexOfLastNewLine != ascii.Length)
|
|
||||||
{
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
array[i].ToASCIIGnuStep(ascii, level + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(i != 0)
|
|
||||||
ascii.Append(" ");
|
|
||||||
|
|
||||||
array[i].ToASCIIGnuStep(ascii, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i != array.Length - 1)
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
|
||||||
|
|
||||||
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ascii.Append(NEWLINE);
|
|
||||||
indexOfLastNewLine = ascii.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSSet" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSSet nsSet)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(set.Count != nsSet.Count)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach(NSObject objS in nsSet)
|
|
||||||
if(!set.Contains(objS))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the underlying data structure in which this NSSets stores its content.</summary>
|
||||||
|
/// <returns>A Set object.</returns>
|
||||||
|
internal List<NSObject> GetSet() => set;
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSSet" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
int hash = 7;
|
||||||
|
hash = 29 * hash + (set != null ? set.GetHashCode() : 0);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="System.Object" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if(obj == null) return false;
|
||||||
|
|
||||||
|
if(GetType() != obj.GetType()) return false;
|
||||||
|
|
||||||
|
var other = (NSSet)obj;
|
||||||
|
|
||||||
|
return !(set != other.set && (set == null || !set.Equals(other.set)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the XML representation for this set. There is no official XML representation specified for sets. In
|
||||||
|
/// this implementation it is represented by an array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xml">The XML StringBuilder</param>
|
||||||
|
/// <param name="level">The indentation level</param>
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("<array>");
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
|
||||||
|
if(ordered) set.Sort();
|
||||||
|
|
||||||
|
foreach(NSObject o in set)
|
||||||
|
{
|
||||||
|
o.ToXml(xml, level + 1);
|
||||||
|
xml.Append(NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("</array>");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void AssignIDs(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
base.AssignIDs(outPlist);
|
||||||
|
|
||||||
|
foreach(NSObject obj in set) obj.AssignIDs(outPlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
if(ordered)
|
||||||
|
{
|
||||||
|
set.Sort();
|
||||||
|
outPlist.WriteIntHeader(0xB, set.Count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
outPlist.WriteIntHeader(0xC, set.Count);
|
||||||
|
|
||||||
|
foreach(NSObject obj in set) outPlist.WriteID(outPlist.GetID(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the ASCII representation of this set. There is no official ASCII representation for sets. In this
|
||||||
|
/// implementation sets are represented as arrays.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ascii">The ASCII file string builder</param>
|
||||||
|
/// <param name="level">The indentation level</param>
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
|
||||||
|
if(ordered) set.Sort();
|
||||||
|
|
||||||
|
NSObject[] array = AllObjects();
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
|
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
Type objClass = array[i].GetType();
|
||||||
|
|
||||||
|
if((objClass.Equals(typeof(NSDictionary)) ||
|
||||||
|
objClass.Equals(typeof(NSArray)) ||
|
||||||
|
objClass.Equals(typeof(NSData))) &&
|
||||||
|
indexOfLastNewLine != ascii.Length)
|
||||||
|
{
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
array[i].ToASCII(ascii, level + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(i != 0) ascii.Append(" ");
|
||||||
|
|
||||||
|
array[i].ToASCII(ascii, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i != array.Length - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
|
|
||||||
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
|
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the ASCII representation of this set according to the GnuStep format. There is no official ASCII
|
||||||
|
/// representation for sets. In this implementation sets are represented as arrays.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ascii">The ASCII file string builder</param>
|
||||||
|
/// <param name="level">The indentation level</param>
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
|
||||||
|
if(ordered) set.Sort();
|
||||||
|
|
||||||
|
NSObject[] array = AllObjects();
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN);
|
||||||
|
int indexOfLastNewLine = ascii.ToString().LastIndexOf(NEWLINE, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
if(array[i] is NSDictionary or NSArray or NSData && indexOfLastNewLine != ascii.Length)
|
||||||
|
{
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
array[i].ToASCIIGnuStep(ascii, level + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(i != 0) ascii.Append(" ");
|
||||||
|
|
||||||
|
array[i].ToASCIIGnuStep(ascii, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i != array.Length - 1) ascii.Append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN);
|
||||||
|
|
||||||
|
if(ascii.Length - indexOfLastNewLine <= ASCII_LINE_LENGTH) continue;
|
||||||
|
|
||||||
|
ascii.Append(NEWLINE);
|
||||||
|
indexOfLastNewLine = ascii.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
ascii.Append(ASCIIPropertyListParser.ARRAY_END_TOKEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSSet" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSSet nsSet) return false;
|
||||||
|
|
||||||
|
if(set.Count != nsSet.Count) return false;
|
||||||
|
|
||||||
|
foreach(NSObject objS in nsSet)
|
||||||
|
if(!set.Contains(objS)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,237 +27,240 @@ 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>
|
static Encoding asciiEncoder, utf16beEncoder, utf8Encoder;
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
/// <summary>Creates a NSString from its binary representation.</summary>
|
||||||
public class NSString : NSObject, IComparable
|
/// <param name="bytes">The binary representation.</param>
|
||||||
|
/// <param name="encoding">The encoding of the binary representation, the name of a supported charset.</param>
|
||||||
|
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
||||||
|
public NSString(ReadOnlySpan<byte> bytes, string encoding) : this(bytes, Encoding.GetEncoding(encoding)) {}
|
||||||
|
|
||||||
|
/// <summary>Creates a NSString from its binary representation.</summary>
|
||||||
|
/// <param name="bytes">The binary representation.</param>
|
||||||
|
/// <param name="encoding">The encoding of the binary representation.</param>
|
||||||
|
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
||||||
|
public NSString(ReadOnlySpan<byte> bytes, Encoding encoding)
|
||||||
{
|
{
|
||||||
static Encoding asciiEncoder, utf16beEncoder, utf8Encoder;
|
#if NATIVE_SPAN
|
||||||
|
Content = encoding.GetString(bytes);
|
||||||
/// <summary>Creates a NSString from its binary representation.</summary>
|
#else
|
||||||
/// <param name="bytes">The binary representation.</param>
|
Content = encoding.GetString(bytes.ToArray());
|
||||||
/// <param name="encoding">The encoding of the binary representation, the name of a supported charset.</param>
|
#endif
|
||||||
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
|
||||||
public NSString(ReadOnlySpan<byte> bytes, string encoding) : this(bytes, Encoding.GetEncoding(encoding)) {}
|
|
||||||
|
|
||||||
/// <summary>Creates a NSString from its binary representation.</summary>
|
|
||||||
/// <param name="bytes">The binary representation.</param>
|
|
||||||
/// <param name="encoding">The encoding of the binary representation.</param>
|
|
||||||
/// <exception cref="ArgumentException">The encoding charset is invalid or not supported by the underlying platform.</exception>
|
|
||||||
public NSString(ReadOnlySpan<byte> bytes, Encoding encoding)
|
|
||||||
{
|
|
||||||
#if NATIVE_SPAN
|
|
||||||
Content = encoding.GetString(bytes);
|
|
||||||
#else
|
|
||||||
Content = encoding.GetString(bytes.ToArray());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates a NSString from a string.</summary>
|
|
||||||
/// <param name="text">The string that will be contained in the NSString.</param>
|
|
||||||
public NSString(string text) => Content = text;
|
|
||||||
|
|
||||||
/// <summary>Gets this strings content.</summary>
|
|
||||||
/// <returns>This NSString as .NET string object.</returns>
|
|
||||||
public string Content { get; set; }
|
|
||||||
|
|
||||||
/// <summary>Compares the current <see cref="Claunia.PropertyList.NSString" /> to the specified object.</summary>
|
|
||||||
/// <returns>A 32-bit signed integer that indicates the lexical relationship between the two comparands.</returns>
|
|
||||||
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSString" />.</param>
|
|
||||||
public int CompareTo(object o) => o switch
|
|
||||||
{
|
|
||||||
NSString nsString => string.Compare(Content, nsString.Content, StringComparison.Ordinal),
|
|
||||||
string s => string.Compare(Content, s, StringComparison.Ordinal),
|
|
||||||
_ => -1
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>Appends a string to this string.</summary>
|
|
||||||
/// <param name="s">The string to append.</param>
|
|
||||||
public void Append(NSString s) => Append(s.Content);
|
|
||||||
|
|
||||||
/// <summary>Appends a string to this string.</summary>
|
|
||||||
/// <param name="s">The string to append.</param>
|
|
||||||
public void Append(string s) => Content += s;
|
|
||||||
|
|
||||||
/// <summary>Prepends a string to this string.</summary>
|
|
||||||
/// <param name="s">The string to prepend.</param>
|
|
||||||
public void Prepend(string s) => Content = s + Content;
|
|
||||||
|
|
||||||
/// <summary>Prepends a string to this string.</summary>
|
|
||||||
/// <param name="s">The string to prepend.</param>
|
|
||||||
public void Prepend(NSString s) => Prepend(s.Content);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="System.Object" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(object obj) => obj is NSString nsString && Content.Equals(nsString.Content);
|
|
||||||
|
|
||||||
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSString" /> object.</summary>
|
|
||||||
/// <returns>
|
|
||||||
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
|
||||||
/// hash table.
|
|
||||||
/// </returns>
|
|
||||||
public override int GetHashCode() => Content.GetHashCode();
|
|
||||||
|
|
||||||
/// <summary>The textual representation of this NSString.</summary>
|
|
||||||
/// <returns>The NSString's contents.</returns>
|
|
||||||
public override string ToString() => Content;
|
|
||||||
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<string>");
|
|
||||||
|
|
||||||
//Make sure that the string is encoded in UTF-8 for the XML output
|
|
||||||
lock(typeof(NSString))
|
|
||||||
{
|
|
||||||
utf8Encoder ??= Encoding.GetEncoding("UTF-8");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] bytes = utf8Encoder.GetBytes(Content);
|
|
||||||
Content = utf8Encoder.GetString(bytes);
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
throw new PropertyListException("Could not encode the NSString into UTF-8: " + ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//According to http://www.w3.org/TR/REC-xml/#syntax node values must not
|
|
||||||
//contain the characters < or &. Also the > character should be escaped.
|
|
||||||
xml.Append(SecurityElement.Escape(Content));
|
|
||||||
|
|
||||||
xml.Append("</string>");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
int kind;
|
|
||||||
byte[] byteBuf;
|
|
||||||
|
|
||||||
lock(typeof(NSString))
|
|
||||||
{
|
|
||||||
// Not much use, because some characters do not fallback to exception, even if not ASCII
|
|
||||||
asciiEncoder ??= Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback,
|
|
||||||
DecoderFallback.ExceptionFallback);
|
|
||||||
|
|
||||||
if(IsASCIIEncodable(Content))
|
|
||||||
{
|
|
||||||
kind = 0x5; // standard ASCII
|
|
||||||
byteBuf = asciiEncoder.GetBytes(Content);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
utf16beEncoder ??= Encoding.BigEndianUnicode;
|
|
||||||
|
|
||||||
kind = 0x6; // UTF-16-BE
|
|
||||||
byteBuf = utf16beEncoder.GetBytes(Content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outPlist.WriteIntHeader(kind, Content.Length);
|
|
||||||
outPlist.Write(byteBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append("\"");
|
|
||||||
|
|
||||||
//According to https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
|
||||||
//non-ASCII characters are not escaped but simply written into the
|
|
||||||
//file, thus actually violating the ASCII plain text format.
|
|
||||||
//We will escape the string anyway because current Xcode project files (ASCII property lists) also escape their strings.
|
|
||||||
ascii.Append(EscapeStringForASCII(Content));
|
|
||||||
ascii.Append("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append("\"");
|
|
||||||
ascii.Append(EscapeStringForASCII(Content));
|
|
||||||
ascii.Append("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Escapes a string for use in ASCII property lists.</summary>
|
|
||||||
/// <returns>The unescaped string.</returns>
|
|
||||||
/// <param name="s">S.</param>
|
|
||||||
internal static string EscapeStringForASCII(string s)
|
|
||||||
{
|
|
||||||
string outString = "";
|
|
||||||
char[] cArray = s.ToCharArray();
|
|
||||||
|
|
||||||
foreach(char c in cArray)
|
|
||||||
if(c > 127)
|
|
||||||
{
|
|
||||||
//non-ASCII Unicode
|
|
||||||
outString += "\\U";
|
|
||||||
string hex = $"{c:x}";
|
|
||||||
|
|
||||||
while(hex.Length < 4)
|
|
||||||
hex = "0" + hex;
|
|
||||||
|
|
||||||
outString += hex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
outString += c switch
|
|
||||||
{
|
|
||||||
'\\' => "\\\\",
|
|
||||||
'\"' => "\\\"",
|
|
||||||
'\b' => "\\b",
|
|
||||||
'\n' => "\\n",
|
|
||||||
'\r' => "\\r",
|
|
||||||
'\t' => "\\t",
|
|
||||||
_ => c
|
|
||||||
};
|
|
||||||
|
|
||||||
return outString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.NSString" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj)
|
|
||||||
{
|
|
||||||
if(obj is not NSString nsString)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Content == nsString.Content;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsASCIIEncodable(string text)
|
|
||||||
{
|
|
||||||
foreach(char c in text)
|
|
||||||
if(c > 0x7F)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static explicit operator string(NSString value) => value.Content;
|
|
||||||
|
|
||||||
public static explicit operator NSString(string value) => new(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a NSString from a string.</summary>
|
||||||
|
/// <param name="text">The string that will be contained in the NSString.</param>
|
||||||
|
public NSString(string text) => Content = text;
|
||||||
|
|
||||||
|
/// <summary>Gets this strings content.</summary>
|
||||||
|
/// <returns>This NSString as .NET string object.</returns>
|
||||||
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Compares the current <see cref="Claunia.PropertyList.NSString" /> to the specified object.</summary>
|
||||||
|
/// <returns>A 32-bit signed integer that indicates the lexical relationship between the two comparands.</returns>
|
||||||
|
/// <param name="o">Object to compare to the current <see cref="Claunia.PropertyList.NSString" />.</param>
|
||||||
|
public int CompareTo(object o) => o switch
|
||||||
|
{
|
||||||
|
NSString nsString => string.Compare(Content,
|
||||||
|
nsString.Content,
|
||||||
|
StringComparison.Ordinal),
|
||||||
|
string s => string.Compare(Content, s, StringComparison.Ordinal),
|
||||||
|
_ => -1
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>Appends a string to this string.</summary>
|
||||||
|
/// <param name="s">The string to append.</param>
|
||||||
|
public void Append(NSString s) => Append(s.Content);
|
||||||
|
|
||||||
|
/// <summary>Appends a string to this string.</summary>
|
||||||
|
/// <param name="s">The string to append.</param>
|
||||||
|
public void Append(string s) => Content += s;
|
||||||
|
|
||||||
|
/// <summary>Prepends a string to this string.</summary>
|
||||||
|
/// <param name="s">The string to prepend.</param>
|
||||||
|
public void Prepend(string s) => Content = s + Content;
|
||||||
|
|
||||||
|
/// <summary>Prepends a string to this string.</summary>
|
||||||
|
/// <param name="s">The string to prepend.</param>
|
||||||
|
public void Prepend(NSString s) => Prepend(s.Content);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="System.Object" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(object obj) => obj is NSString nsString && Content.Equals(nsString.Content);
|
||||||
|
|
||||||
|
/// <summary>Serves as a hash function for a <see cref="Claunia.PropertyList.NSString" /> object.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a
|
||||||
|
/// hash table.
|
||||||
|
/// </returns>
|
||||||
|
public override int GetHashCode() => Content.GetHashCode();
|
||||||
|
|
||||||
|
/// <summary>The textual representation of this NSString.</summary>
|
||||||
|
/// <returns>The NSString's contents.</returns>
|
||||||
|
public override string ToString() => Content;
|
||||||
|
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("<string>");
|
||||||
|
|
||||||
|
//Make sure that the string is encoded in UTF-8 for the XML output
|
||||||
|
lock(typeof(NSString))
|
||||||
|
{
|
||||||
|
utf8Encoder ??= Encoding.GetEncoding("UTF-8");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] bytes = utf8Encoder.GetBytes(Content);
|
||||||
|
Content = utf8Encoder.GetString(bytes);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
throw new PropertyListException("Could not encode the NSString into UTF-8: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//According to http://www.w3.org/TR/REC-xml/#syntax node values must not
|
||||||
|
//contain the characters < or &. Also the > character should be escaped.
|
||||||
|
xml.Append(SecurityElement.Escape(Content));
|
||||||
|
|
||||||
|
xml.Append("</string>");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
int kind;
|
||||||
|
byte[] byteBuf;
|
||||||
|
|
||||||
|
lock(typeof(NSString))
|
||||||
|
{
|
||||||
|
// Not much use, because some characters do not fallback to exception, even if not ASCII
|
||||||
|
asciiEncoder ??= Encoding.GetEncoding("ascii",
|
||||||
|
EncoderFallback.ExceptionFallback,
|
||||||
|
DecoderFallback.ExceptionFallback);
|
||||||
|
|
||||||
|
if(IsASCIIEncodable(Content))
|
||||||
|
{
|
||||||
|
kind = 0x5; // standard ASCII
|
||||||
|
byteBuf = asciiEncoder.GetBytes(Content);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
utf16beEncoder ??= Encoding.BigEndianUnicode;
|
||||||
|
|
||||||
|
kind = 0x6; // UTF-16-BE
|
||||||
|
byteBuf = utf16beEncoder.GetBytes(Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outPlist.WriteIntHeader(kind, Content.Length);
|
||||||
|
outPlist.Write(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append("\"");
|
||||||
|
|
||||||
|
//According to https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
|
||||||
|
//non-ASCII characters are not escaped but simply written into the
|
||||||
|
//file, thus actually violating the ASCII plain text format.
|
||||||
|
//We will escape the string anyway because current Xcode project files (ASCII property lists) also escape their strings.
|
||||||
|
ascii.Append(EscapeStringForASCII(Content));
|
||||||
|
ascii.Append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append("\"");
|
||||||
|
ascii.Append(EscapeStringForASCII(Content));
|
||||||
|
ascii.Append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Escapes a string for use in ASCII property lists.</summary>
|
||||||
|
/// <returns>The unescaped string.</returns>
|
||||||
|
/// <param name="s">S.</param>
|
||||||
|
internal static string EscapeStringForASCII(string s)
|
||||||
|
{
|
||||||
|
string outString = "";
|
||||||
|
char[] cArray = s.ToCharArray();
|
||||||
|
|
||||||
|
foreach(char c in cArray)
|
||||||
|
{
|
||||||
|
if(c > 127)
|
||||||
|
{
|
||||||
|
//non-ASCII Unicode
|
||||||
|
outString += "\\U";
|
||||||
|
string hex = $"{c:x}";
|
||||||
|
|
||||||
|
while(hex.Length < 4) hex = "0" + hex;
|
||||||
|
|
||||||
|
outString += hex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outString += c switch
|
||||||
|
{
|
||||||
|
'\\' => "\\\\",
|
||||||
|
'\"' => "\\\"",
|
||||||
|
'\b' => "\\b",
|
||||||
|
'\n' => "\\n",
|
||||||
|
'\r' => "\\r",
|
||||||
|
'\t' => "\\t",
|
||||||
|
_ => c
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.NSString" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj)
|
||||||
|
{
|
||||||
|
if(obj is not NSString nsString) return false;
|
||||||
|
|
||||||
|
return Content == nsString.Content;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsASCIIEncodable(string text)
|
||||||
|
{
|
||||||
|
foreach(char c in text)
|
||||||
|
if(c > 0x7F) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator string(NSString value) => value.Content;
|
||||||
|
|
||||||
|
public static explicit operator NSString(string value) => new(value);
|
||||||
}
|
}
|
||||||
@@ -27,27 +27,26 @@
|
|||||||
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>
|
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
||||||
[Serializable]
|
public PropertyListException() {}
|
||||||
public class PropertyListException : Exception
|
|
||||||
{
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
|
||||||
public PropertyListException() {}
|
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
||||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||||
public PropertyListException(string message) : base(message) {}
|
public PropertyListException(string message) : base(message) {}
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
/// <summary>Initializes a new instance of the <see cref="PropertyListException" /> class.</summary>
|
||||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||||
/// <param name="inner">
|
/// <param name="inner">
|
||||||
/// The exception that is the cause of the current exception, or <see langword="null" /> if no inner
|
/// The exception that is the cause of the current exception, or <see langword="null" /> if no inner
|
||||||
/// exception is specified.
|
/// exception is specified.
|
||||||
/// </param>
|
/// </param>
|
||||||
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>
|
/// <summary>Creates a new exception with the given message.</summary>
|
||||||
/// A PropertyListFormatException is thrown by the various property list format parsers when an error in the
|
/// <param name="message">A message containing information about the nature of the exception.</param>
|
||||||
/// format of the given property list is encountered.
|
public PropertyListFormatException(string message) : base(message) {}
|
||||||
/// </summary>
|
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
|
||||||
public class PropertyListFormatException : PropertyListException
|
|
||||||
{
|
|
||||||
/// <summary>Creates a new exception with the given message.</summary>
|
|
||||||
/// <param name="message">A message containing information about the nature of the exception.</param>
|
|
||||||
public PropertyListFormatException(string message) : base(message) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,379 +27,367 @@ 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>
|
const int TYPE_XML = 0;
|
||||||
/// This class provides methods to parse property lists. It can handle files, input streams and byte arrays. All
|
const int TYPE_BINARY = 1;
|
||||||
/// known property list formats are supported. This class also provides methods to save and convert property lists.
|
const int TYPE_ASCII = 2;
|
||||||
/// </summary>
|
const int TYPE_ERROR_BLANK = 10;
|
||||||
/// @author Daniel Dreibrodt
|
const int TYPE_ERROR_UNKNOWN = 11;
|
||||||
/// @author Natalia Portillo
|
|
||||||
public static class PropertyListParser
|
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
||||||
|
/// <returns>The type of the property list</returns>
|
||||||
|
/// <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)
|
||||||
{
|
{
|
||||||
const int TYPE_XML = 0;
|
if(dataBeginning.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
const int TYPE_BINARY = 1;
|
|
||||||
const int TYPE_ASCII = 2;
|
|
||||||
const int TYPE_ERROR_BLANK = 10;
|
|
||||||
const int TYPE_ERROR_UNKNOWN = 11;
|
|
||||||
|
|
||||||
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
if(dataBeginning[0] == '(' || dataBeginning[0] == '{' || dataBeginning[0] == '/') return TYPE_ASCII;
|
||||||
/// <returns>The type of the property list</returns>
|
|
||||||
/// <param name="dataBeginning">The very first bytes of data of the property list (minus any whitespace) as a string</param>
|
if(dataBeginning[0] == '<') return TYPE_XML;
|
||||||
static int DetermineTypeExact(ReadOnlySpan<byte> dataBeginning)
|
|
||||||
|
if(dataBeginning.Length >= 6 &&
|
||||||
|
dataBeginning[0] == 'b' &&
|
||||||
|
dataBeginning[1] == 'p' &&
|
||||||
|
dataBeginning[2] == 'l' &&
|
||||||
|
dataBeginning[3] == 'i' &&
|
||||||
|
dataBeginning[4] == 's' &&
|
||||||
|
dataBeginning[5] == 't')
|
||||||
|
return TYPE_BINARY;
|
||||||
|
|
||||||
|
return TYPE_ERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
||||||
|
/// <returns>The very first bytes of data of the property list (minus any whitespace)</returns>
|
||||||
|
/// <param name="bytes">The type of the property list</param>
|
||||||
|
static int DetermineType(ReadOnlySpan<byte> bytes)
|
||||||
|
{
|
||||||
|
if(bytes.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
|
|
||||||
|
//Skip any possible whitespace at the beginning of the file
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
if(bytes.Length >= 3 && (bytes[0] & 0xFF) == 0xEF && (bytes[1] & 0xFF) == 0xBB && (bytes[2] & 0xFF) == 0xBF)
|
||||||
|
offset += 3;
|
||||||
|
|
||||||
|
while(offset < bytes.Length &&
|
||||||
|
(bytes[offset] == ' ' ||
|
||||||
|
bytes[offset] == '\t' ||
|
||||||
|
bytes[offset] == '\r' ||
|
||||||
|
bytes[offset] == '\n' ||
|
||||||
|
bytes[offset] == '\f'))
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> header = bytes.Slice(offset, Math.Min(8, bytes.Length - offset));
|
||||||
|
|
||||||
|
return DetermineTypeExact(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
||||||
|
/// <returns>The type of the property list</returns>
|
||||||
|
/// <param name="fs">
|
||||||
|
/// An input stream pointing to the beginning of the property list data. The stream will be reset to the
|
||||||
|
/// beginning of the property list data after the type has been determined.
|
||||||
|
/// </param>
|
||||||
|
static int DetermineType(Stream fs, long offset = 0)
|
||||||
|
{
|
||||||
|
if(fs.Length == 0) return TYPE_ERROR_BLANK;
|
||||||
|
|
||||||
|
long index = offset;
|
||||||
|
long readLimit = index + 1024;
|
||||||
|
long mark = readLimit;
|
||||||
|
fs.Seek(offset, SeekOrigin.Current);
|
||||||
|
int b;
|
||||||
|
bool bom = false;
|
||||||
|
|
||||||
|
//Skip any possible whitespace at the beginning of the file
|
||||||
|
do
|
||||||
{
|
{
|
||||||
if(dataBeginning.Length == 0)
|
if(++index > readLimit)
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
if(dataBeginning[0] == '(' ||
|
|
||||||
dataBeginning[0] == '{' ||
|
|
||||||
dataBeginning[0] == '/')
|
|
||||||
return TYPE_ASCII;
|
|
||||||
|
|
||||||
if(dataBeginning[0] == '<')
|
|
||||||
return TYPE_XML;
|
|
||||||
|
|
||||||
if(dataBeginning.Length >= 6 &&
|
|
||||||
dataBeginning[0] == 'b' &&
|
|
||||||
dataBeginning[1] == 'p' &&
|
|
||||||
dataBeginning[2] == 'l' &&
|
|
||||||
dataBeginning[3] == 'i' &&
|
|
||||||
dataBeginning[4] == 's' &&
|
|
||||||
dataBeginning[5] == 't')
|
|
||||||
return TYPE_BINARY;
|
|
||||||
|
|
||||||
return TYPE_ERROR_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
|
||||||
/// <returns>The very first bytes of data of the property list (minus any whitespace)</returns>
|
|
||||||
/// <param name="bytes">The type of the property list</param>
|
|
||||||
static int DetermineType(ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
if(bytes.Length == 0)
|
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
//Skip any possible whitespace at the beginning of the file
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
if(bytes.Length >= 3 &&
|
|
||||||
(bytes[0] & 0xFF) == 0xEF &&
|
|
||||||
(bytes[1] & 0xFF) == 0xBB &&
|
|
||||||
(bytes[2] & 0xFF) == 0xBF)
|
|
||||||
offset += 3;
|
|
||||||
|
|
||||||
while(offset < bytes.Length &&
|
|
||||||
(bytes[offset] == ' ' || bytes[offset] == '\t' || bytes[offset] == '\r' || bytes[offset] == '\n' ||
|
|
||||||
bytes[offset] == '\f'))
|
|
||||||
offset++;
|
|
||||||
|
|
||||||
ReadOnlySpan<byte> header = bytes.Slice(offset, Math.Min(8, bytes.Length - offset));
|
|
||||||
|
|
||||||
return DetermineTypeExact(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Determines the type of a property list by means of the first bytes of its data</summary>
|
|
||||||
/// <returns>The type of the property list</returns>
|
|
||||||
/// <param name="fs">
|
|
||||||
/// An input stream pointing to the beginning of the property list data. The stream will be reset to the
|
|
||||||
/// beginning of the property list data after the type has been determined.
|
|
||||||
/// </param>
|
|
||||||
static int DetermineType(Stream fs, long offset = 0)
|
|
||||||
{
|
|
||||||
if(fs.Length == 0)
|
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
long index = offset;
|
|
||||||
long readLimit = index + 1024;
|
|
||||||
long mark = readLimit;
|
|
||||||
fs.Seek(offset, SeekOrigin.Current);
|
|
||||||
int b;
|
|
||||||
bool bom = false;
|
|
||||||
|
|
||||||
//Skip any possible whitespace at the beginning of the file
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
if(++index > readLimit)
|
fs.Seek(mark, SeekOrigin.Begin);
|
||||||
{
|
|
||||||
fs.Seek(mark, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
return DetermineType(fs, readLimit);
|
return DetermineType(fs, readLimit);
|
||||||
}
|
|
||||||
|
|
||||||
b = fs.ReadByte();
|
|
||||||
|
|
||||||
//Check if we are reading the Unicode byte order mark (BOM) and skip it
|
|
||||||
bom = index < 3 && ((index == 0 && b == 0xEF) ||
|
|
||||||
(bom && ((index == 1 && b == 0xBB) || (index == 2 && b == 0xBF))));
|
|
||||||
} while(b != -1 &&
|
|
||||||
(b is ' ' or '\t' or '\r' or '\n' or '\f' || bom));
|
|
||||||
|
|
||||||
if(b == -1)
|
|
||||||
return TYPE_ERROR_BLANK;
|
|
||||||
|
|
||||||
byte[] magicBytes = new byte[8];
|
|
||||||
magicBytes[0] = (byte)b;
|
|
||||||
int read = fs.Read(magicBytes, 1, 7);
|
|
||||||
|
|
||||||
int type = DetermineTypeExact(magicBytes.AsSpan(0, read));
|
|
||||||
fs.Seek(mark, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Set up preprocessing functions for plist values.</summary>
|
|
||||||
/// <param name="preprocessor">A function that preprocesses the passed string and returns the adjusted value.</param>
|
|
||||||
/// <param name="type">The type of value preprocessor to use.</param>
|
|
||||||
public static void SetValuePreprocessor<T>(Func<T, T> preprocessor, ValuePreprocessor.Type type) =>
|
|
||||||
ValuePreprocessor.Set(preprocessor, type);
|
|
||||||
|
|
||||||
/// <summary>Reads all bytes from an Stream and stores them in an array, up to a maximum count.</summary>
|
|
||||||
/// <param name="fs">The Stream pointing to the data that should be stored in the array.</param>
|
|
||||||
internal static byte[] ReadAll(Stream fs)
|
|
||||||
{
|
|
||||||
using var outputStream = new MemoryStream();
|
|
||||||
|
|
||||||
fs.CopyTo(outputStream);
|
|
||||||
|
|
||||||
return outputStream.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a property list from a file.</summary>
|
|
||||||
/// <param name="filePath">Path to the property list file.</param>
|
|
||||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(string filePath) => Parse(new FileInfo(filePath));
|
|
||||||
|
|
||||||
/// <summary>Parses a property list from a file.</summary>
|
|
||||||
/// <param name="f">The property list file.</param>
|
|
||||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(FileInfo f)
|
|
||||||
{
|
|
||||||
using FileStream fis = f.OpenRead();
|
|
||||||
|
|
||||||
return Parse(fis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a property list from a byte array.</summary>
|
|
||||||
/// <param name="bytes">The property list data as a byte array.</param>
|
|
||||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(byte[] bytes)
|
|
||||||
{
|
|
||||||
switch(DetermineType(bytes))
|
|
||||||
{
|
|
||||||
case TYPE_BINARY: return BinaryPropertyListParser.Parse(bytes);
|
|
||||||
case TYPE_XML: return XmlPropertyListParser.Parse(bytes);
|
|
||||||
case TYPE_ASCII: return ASCIIPropertyListParser.Parse(bytes);
|
|
||||||
default:
|
|
||||||
throw new
|
|
||||||
PropertyListFormatException("The given data is not a property list of a supported format.");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a property list from a byte array.</summary>
|
b = fs.ReadByte();
|
||||||
/// <param name="bytes">The property list data as a byte array.</param>
|
|
||||||
/// <param name="offset">The length of the property list.</param>
|
|
||||||
/// <param name="count">The offset at which to start reading the property list.</param>
|
|
||||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(byte[] bytes, int offset, int length) => Parse(bytes.AsSpan(offset, length));
|
|
||||||
|
|
||||||
/// <summary>Parses a property list from a byte span.</summary>
|
//Check if we are reading the Unicode byte order mark (BOM) and skip it
|
||||||
/// <param name="bytes">The property list data as a byte array.</param>
|
bom = index < 3 && (index == 0 && b == 0xEF || bom && (index == 1 && b == 0xBB || index == 2 && b == 0xBF));
|
||||||
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
} while(b != -1 && (b is ' ' or '\t' or '\r' or '\n' or '\f' || bom));
|
||||||
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
|
||||||
|
if(b == -1) return TYPE_ERROR_BLANK;
|
||||||
|
|
||||||
|
byte[] magicBytes = new byte[8];
|
||||||
|
magicBytes[0] = (byte)b;
|
||||||
|
int read = fs.Read(magicBytes, 1, 7);
|
||||||
|
|
||||||
|
int type = DetermineTypeExact(magicBytes.AsSpan(0, read));
|
||||||
|
fs.Seek(mark, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Set up preprocessing functions for plist values.</summary>
|
||||||
|
/// <param name="preprocessor">A function that preprocesses the passed string and returns the adjusted value.</param>
|
||||||
|
/// <param name="type">The type of value preprocessor to use.</param>
|
||||||
|
public static void SetValuePreprocessor<T>(Func<T, T> preprocessor, ValuePreprocessor.Type type) =>
|
||||||
|
ValuePreprocessor.Set(preprocessor, type);
|
||||||
|
|
||||||
|
/// <summary>Reads all bytes from an Stream and stores them in an array, up to a maximum count.</summary>
|
||||||
|
/// <param name="fs">The Stream pointing to the data that should be stored in the array.</param>
|
||||||
|
internal static byte[] ReadAll(Stream fs)
|
||||||
|
{
|
||||||
|
using var outputStream = new MemoryStream();
|
||||||
|
|
||||||
|
fs.CopyTo(outputStream);
|
||||||
|
|
||||||
|
return outputStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a property list from a file.</summary>
|
||||||
|
/// <param name="filePath">Path to the property list file.</param>
|
||||||
|
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(string filePath) => Parse(new FileInfo(filePath));
|
||||||
|
|
||||||
|
/// <summary>Parses a property list from a file.</summary>
|
||||||
|
/// <param name="f">The property list file.</param>
|
||||||
|
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(FileInfo f)
|
||||||
|
{
|
||||||
|
using FileStream fis = f.OpenRead();
|
||||||
|
|
||||||
|
return Parse(fis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a property list from a byte array.</summary>
|
||||||
|
/// <param name="bytes">The property list data as a byte array.</param>
|
||||||
|
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(byte[] bytes)
|
||||||
|
{
|
||||||
|
switch(DetermineType(bytes))
|
||||||
{
|
{
|
||||||
switch(DetermineType(bytes))
|
case TYPE_BINARY:
|
||||||
{
|
return BinaryPropertyListParser.Parse(bytes);
|
||||||
case TYPE_BINARY: return BinaryPropertyListParser.Parse(bytes);
|
case TYPE_XML:
|
||||||
case TYPE_XML: return XmlPropertyListParser.Parse(bytes.ToArray());
|
return XmlPropertyListParser.Parse(bytes);
|
||||||
case TYPE_ASCII: return ASCIIPropertyListParser.Parse(bytes);
|
case TYPE_ASCII:
|
||||||
default:
|
return ASCIIPropertyListParser.Parse(bytes);
|
||||||
throw new
|
default:
|
||||||
PropertyListFormatException("The given data is not a property list of a supported format.");
|
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Parses a property list from an Stream.</summary>
|
/// <summary>Parses a property list from a byte array.</summary>
|
||||||
/// <param name="fs">The Stream delivering the property list data.</param>
|
/// <param name="bytes">The property list data as a byte array.</param>
|
||||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
/// <param name="offset">The length of the property list.</param>
|
||||||
public static NSObject Parse(Stream fs) => Parse(ReadAll(fs));
|
/// <param name="count">The offset at which to start reading the property list.</param>
|
||||||
|
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(byte[] bytes, int offset, int length) => Parse(bytes.AsSpan(offset, length));
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root into a XML file.</summary>
|
/// <summary>Parses a property list from a byte span.</summary>
|
||||||
/// <param name="root">The root object.</param>
|
/// <param name="bytes">The property list data as a byte array.</param>
|
||||||
/// <param name="outFile">The output file.</param>
|
/// <returns>The root object in the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
public static NSObject Parse(ReadOnlySpan<byte> bytes)
|
||||||
public static void SaveAsXml(NSObject root, FileInfo outFile)
|
{
|
||||||
|
switch(DetermineType(bytes))
|
||||||
{
|
{
|
||||||
string parent = outFile.DirectoryName;
|
case TYPE_BINARY:
|
||||||
|
return BinaryPropertyListParser.Parse(bytes);
|
||||||
if(!Directory.Exists(parent))
|
case TYPE_XML:
|
||||||
Directory.CreateDirectory(parent);
|
return XmlPropertyListParser.Parse(bytes.ToArray());
|
||||||
|
case TYPE_ASCII:
|
||||||
// Use Create here -- to make sure that when the updated file is shorter than
|
return ASCIIPropertyListParser.Parse(bytes);
|
||||||
// the original file, no "obsolete" data is left at the end.
|
default:
|
||||||
using Stream fous = outFile.Open(FileMode.Create, FileAccess.ReadWrite);
|
throw new PropertyListFormatException("The given data is not a property list of a supported format.");
|
||||||
|
|
||||||
SaveAsXml(root, fous);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root in XML format into an output stream.</summary>
|
/// <summary>Parses a property list from an Stream.</summary>
|
||||||
/// <param name="root">The root object.</param>
|
/// <param name="fs">The Stream delivering the property list data.</param>
|
||||||
/// <param name="outStream">The output stream.</param>
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
public static NSObject Parse(Stream fs) => Parse(ReadAll(fs));
|
||||||
public static void SaveAsXml(NSObject root, Stream outStream)
|
|
||||||
|
/// <summary>Saves a property list with the given object as root into a XML file.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outFile">The output file.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsXml(NSObject root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
using Stream fous = outFile.Open(FileMode.Create, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
SaveAsXml(root, fous);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves a property list with the given object as root in XML format into an output stream.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outStream">The output stream.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsXml(NSObject root, Stream outStream)
|
||||||
|
{
|
||||||
|
using var w = new StreamWriter(outStream, Encoding.UTF8, 1024, true);
|
||||||
|
|
||||||
|
w.Write(root.ToXmlPropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts a given property list file into the OS X and iOS XML format.</summary>
|
||||||
|
/// <param name="inFile">The source file.</param>
|
||||||
|
/// <param name="outFile">The target file.</param>
|
||||||
|
public static void ConvertToXml(FileInfo inFile, FileInfo outFile)
|
||||||
|
{
|
||||||
|
NSObject root = Parse(inFile);
|
||||||
|
SaveAsXml(root, outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves a property list with the given object as root into a binary file.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outFile">The output file.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsBinary(NSObject root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
BinaryPropertyListWriter.Write(outFile, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves a property list with the given object as root in binary format into an output stream.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outStream">The output stream.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsBinary(NSObject root, Stream outStream) => BinaryPropertyListWriter.Write(outStream, root);
|
||||||
|
|
||||||
|
/// <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="outFile">The target file.</param>
|
||||||
|
public static void ConvertToBinary(FileInfo inFile, FileInfo outFile)
|
||||||
|
{
|
||||||
|
NSObject root = Parse(inFile);
|
||||||
|
SaveAsBinary(root, outFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves a property list with the given object as root into a ASCII file.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outFile">The output file.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsASCII(NSDictionary root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
using var w = new StreamWriter(fous, Encoding.ASCII);
|
||||||
|
|
||||||
|
w.Write(root.ToASCIIPropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Saves a property list with the given object as root into a ASCII file.</summary>
|
||||||
|
/// <param name="root">The root object.</param>
|
||||||
|
/// <param name="outFile">The output file.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsASCII(NSArray root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
using var w = new StreamWriter(fous, Encoding.ASCII);
|
||||||
|
|
||||||
|
w.Write(root.ToASCIIPropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts a given property list file into ASCII format.</summary>
|
||||||
|
/// <param name="inFile">The source file.</param>
|
||||||
|
/// <param name="outFile">The target file.</param>
|
||||||
|
public static void ConvertToASCII(FileInfo inFile, FileInfo outFile)
|
||||||
|
{
|
||||||
|
NSObject root = Parse(inFile);
|
||||||
|
|
||||||
|
if(root is NSDictionary dictionary)
|
||||||
|
SaveAsASCII(dictionary, outFile);
|
||||||
|
else if(root is NSArray array)
|
||||||
|
SaveAsASCII(array, outFile);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
using var w = new StreamWriter(outStream, Encoding.UTF8, 1024, true);
|
throw new PropertyListFormatException("The root of the given input property list " +
|
||||||
|
"is neither a Dictionary nor an Array!");
|
||||||
w.Write(root.ToXmlPropertyList());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Converts a given property list file into the OS X and iOS XML format.</summary>
|
/// <summary>Saves a property list with the given object as root into a GnuStep ASCII file.</summary>
|
||||||
/// <param name="inFile">The source file.</param>
|
/// <param name="root">The root object.</param>
|
||||||
/// <param name="outFile">The target file.</param>
|
/// <param name="outFile">The output file.</param>
|
||||||
public static void ConvertToXml(FileInfo inFile, FileInfo outFile)
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsGnuStepASCII(NSDictionary root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
using var w = new StreamWriter(fous, Encoding.ASCII);
|
||||||
|
|
||||||
|
w.Write(root.ToGnuStepASCIIPropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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="outFile">The output file.</param>
|
||||||
|
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
||||||
|
public static void SaveAsGnuStepASCII(NSArray root, FileInfo outFile)
|
||||||
|
{
|
||||||
|
string parent = outFile.DirectoryName;
|
||||||
|
|
||||||
|
if(!Directory.Exists(parent)) Directory.CreateDirectory(parent);
|
||||||
|
|
||||||
|
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
|
||||||
|
using var w = new StreamWriter(fous, Encoding.ASCII);
|
||||||
|
|
||||||
|
w.Write(root.ToGnuStepASCIIPropertyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Converts a given property list file into GnuStep ASCII format.</summary>
|
||||||
|
/// <param name="inFile">The source file.</param>
|
||||||
|
/// <param name="outFile">The target file.</param>
|
||||||
|
public static void ConvertToGnuStepASCII(FileInfo inFile, FileInfo outFile)
|
||||||
|
{
|
||||||
|
NSObject root = Parse(inFile);
|
||||||
|
|
||||||
|
switch(root)
|
||||||
{
|
{
|
||||||
NSObject root = Parse(inFile);
|
case NSDictionary dictionary:
|
||||||
SaveAsXml(root, outFile);
|
SaveAsGnuStepASCII(dictionary, outFile);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root into a binary file.</summary>
|
break;
|
||||||
/// <param name="root">The root object.</param>
|
case NSArray array:
|
||||||
/// <param name="outFile">The output file.</param>
|
SaveAsGnuStepASCII(array, outFile);
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsBinary(NSObject root, FileInfo outFile)
|
|
||||||
{
|
|
||||||
string parent = outFile.DirectoryName;
|
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
break;
|
||||||
Directory.CreateDirectory(parent);
|
default:
|
||||||
|
|
||||||
BinaryPropertyListWriter.Write(outFile, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root in binary format into an output stream.</summary>
|
|
||||||
/// <param name="root">The root object.</param>
|
|
||||||
/// <param name="outStream">The output stream.</param>
|
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsBinary(NSObject root, Stream outStream) =>
|
|
||||||
BinaryPropertyListWriter.Write(outStream, root);
|
|
||||||
|
|
||||||
/// <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="outFile">The target file.</param>
|
|
||||||
public static void ConvertToBinary(FileInfo inFile, FileInfo outFile)
|
|
||||||
{
|
|
||||||
NSObject root = Parse(inFile);
|
|
||||||
SaveAsBinary(root, outFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root into a ASCII file.</summary>
|
|
||||||
/// <param name="root">The root object.</param>
|
|
||||||
/// <param name="outFile">The output file.</param>
|
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsASCII(NSDictionary root, FileInfo outFile)
|
|
||||||
{
|
|
||||||
string parent = outFile.DirectoryName;
|
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
|
|
||||||
using var w = new StreamWriter(fous, Encoding.ASCII);
|
|
||||||
|
|
||||||
w.Write(root.ToASCIIPropertyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Saves a property list with the given object as root into a ASCII file.</summary>
|
|
||||||
/// <param name="root">The root object.</param>
|
|
||||||
/// <param name="outFile">The output file.</param>
|
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsASCII(NSArray root, FileInfo outFile)
|
|
||||||
{
|
|
||||||
string parent = outFile.DirectoryName;
|
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
|
|
||||||
using var w = new StreamWriter(fous, Encoding.ASCII);
|
|
||||||
|
|
||||||
w.Write(root.ToASCIIPropertyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Converts a given property list file into ASCII format.</summary>
|
|
||||||
/// <param name="inFile">The source file.</param>
|
|
||||||
/// <param name="outFile">The target file.</param>
|
|
||||||
public static void ConvertToASCII(FileInfo inFile, FileInfo outFile)
|
|
||||||
{
|
|
||||||
NSObject root = Parse(inFile);
|
|
||||||
|
|
||||||
if(root is NSDictionary dictionary)
|
|
||||||
SaveAsASCII(dictionary, outFile);
|
|
||||||
else if(root is NSArray array)
|
|
||||||
SaveAsASCII(array, outFile);
|
|
||||||
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>
|
|
||||||
/// <param name="root">The root object.</param>
|
|
||||||
/// <param name="outFile">The output file.</param>
|
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsGnuStepASCII(NSDictionary root, FileInfo outFile)
|
|
||||||
{
|
|
||||||
string parent = outFile.DirectoryName;
|
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
|
|
||||||
using var w = new StreamWriter(fous, Encoding.ASCII);
|
|
||||||
|
|
||||||
w.Write(root.ToGnuStepASCIIPropertyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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="outFile">The output file.</param>
|
|
||||||
/// <exception cref="IOException">When an error occurs during the writing process.</exception>
|
|
||||||
public static void SaveAsGnuStepASCII(NSArray root, FileInfo outFile)
|
|
||||||
{
|
|
||||||
string parent = outFile.DirectoryName;
|
|
||||||
|
|
||||||
if(!Directory.Exists(parent))
|
|
||||||
Directory.CreateDirectory(parent);
|
|
||||||
|
|
||||||
using Stream fous = outFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
|
||||||
|
|
||||||
using var w = new StreamWriter(fous, Encoding.ASCII);
|
|
||||||
|
|
||||||
w.Write(root.ToGnuStepASCIIPropertyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Converts a given property list file into GnuStep ASCII format.</summary>
|
|
||||||
/// <param name="inFile">The source file.</param>
|
|
||||||
/// <param name="outFile">The target file.</param>
|
|
||||||
public static void ConvertToGnuStepASCII(FileInfo inFile, FileInfo outFile)
|
|
||||||
{
|
|
||||||
NSObject root = Parse(inFile);
|
|
||||||
|
|
||||||
switch(root)
|
|
||||||
{
|
|
||||||
case NSDictionary dictionary:
|
|
||||||
SaveAsGnuStepASCII(dictionary, outFile);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NSArray array:
|
|
||||||
SaveAsGnuStepASCII(array, outFile);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new PropertyListFormatException("The root of the given input property list " +
|
|
||||||
"is neither a Dictionary nor an Array!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
353
plist-cil/UID.cs
353
plist-cil/UID.cs
@@ -27,187 +27,182 @@ 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>
|
readonly ulong value;
|
||||||
/// @author Daniel Dreibrodt
|
|
||||||
/// @author Natalia Portillo
|
/// <summary>Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class.</summary>
|
||||||
public class UID : NSObject
|
/// <param name="bytes">Bytes.</param>
|
||||||
|
public UID(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
readonly ulong value;
|
if(bytes.Length != 1 && bytes.Length != 2 && bytes.Length != 4 && bytes.Length != 8)
|
||||||
|
throw new ArgumentException("Type argument is not valid.");
|
||||||
|
|
||||||
/// <summary>Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class.</summary>
|
value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
|
||||||
/// <param name="bytes">Bytes.</param>
|
|
||||||
public UID(ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
if(bytes.Length != 1 &&
|
|
||||||
bytes.Length != 2 &&
|
|
||||||
bytes.Length != 4 &&
|
|
||||||
bytes.Length != 8)
|
|
||||||
throw new ArgumentException("Type argument is not valid.");
|
|
||||||
|
|
||||||
value = (ulong)BinaryPropertyListParser.ParseLong(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 8-bit
|
|
||||||
/// number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">Unsigned 8-bit number.</param>
|
|
||||||
public UID(byte number) => value = number;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 16-bit
|
|
||||||
/// number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">Name.</param>
|
|
||||||
/// <param name="number">Unsigned 16-bit number.</param>
|
|
||||||
public UID(ushort number) => value = number;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 32-bit
|
|
||||||
/// number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">Unsigned 32-bit number.</param>
|
|
||||||
public UID(uint number) => value = number;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 64-bit
|
|
||||||
/// number.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="number">Unsigned 64-bit number.</param>
|
|
||||||
public UID(ulong number) => value = number;
|
|
||||||
|
|
||||||
/// <summary>Gets the bytes.</summary>
|
|
||||||
/// <value>The bytes.</value>
|
|
||||||
public byte[] Bytes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
byte[] bytes = new byte[ByteCount];
|
|
||||||
GetBytes(bytes);
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Gets the number of bytes required to represent this <see cref="UID" />.</summary>
|
|
||||||
public int ByteCount => value switch
|
|
||||||
{
|
|
||||||
<= byte.MaxValue => 1,
|
|
||||||
<= ushort.MaxValue => 2,
|
|
||||||
<= uint.MaxValue => 4,
|
|
||||||
_ => 8
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>Writes the bytes required to represent this <see cref="UID" /> to a byte span.</summary>
|
|
||||||
/// <param name="bytes">The byte span to which to write the byte representation of this UID.</param>
|
|
||||||
public void GetBytes(Span<byte> bytes)
|
|
||||||
{
|
|
||||||
switch(ByteCount)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
bytes[0] = (byte)value;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
BinaryPrimitives.WriteUInt16BigEndian(bytes, (ushort)value);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(bytes, (uint)value);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
|
||||||
BinaryPrimitives.WriteUInt64BigEndian(bytes, value);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UIDs are represented as dictionaries in XML property lists, where the key is always <c>CF$UID</c> and the
|
|
||||||
/// value is the integer representation of the UID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="xml">The xml StringBuilder</param>
|
|
||||||
/// <param name="level">The indentation level</param>
|
|
||||||
internal override void ToXml(StringBuilder xml, int level)
|
|
||||||
{
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("<dict>");
|
|
||||||
xml.AppendLine();
|
|
||||||
|
|
||||||
Indent(xml, level + 1);
|
|
||||||
xml.Append("<key>CF$UID</key>");
|
|
||||||
xml.AppendLine();
|
|
||||||
|
|
||||||
Indent(xml, level + 1);
|
|
||||||
xml.Append($"<integer>{value}</integer>");
|
|
||||||
xml.AppendLine();
|
|
||||||
|
|
||||||
Indent(xml, level);
|
|
||||||
xml.Append("</dict>");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
|
||||||
{
|
|
||||||
outPlist.Write(0x80 + ByteCount - 1);
|
|
||||||
Span<byte> bytes = stackalloc byte[ByteCount];
|
|
||||||
GetBytes(bytes);
|
|
||||||
outPlist.Write(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCII(StringBuilder ascii, int level)
|
|
||||||
{
|
|
||||||
Indent(ascii, level);
|
|
||||||
ascii.Append("\"");
|
|
||||||
Span<byte> bytes = stackalloc byte[ByteCount];
|
|
||||||
GetBytes(bytes);
|
|
||||||
|
|
||||||
foreach(byte b in bytes)
|
|
||||||
ascii.Append($"{b:x2}");
|
|
||||||
|
|
||||||
ascii.Append("\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) => ToASCII(ascii, level);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.UID" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">
|
|
||||||
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
|
||||||
/// <see cref="Claunia.PropertyList.UID" />.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
|
||||||
/// <see cref="Claunia.PropertyList.UID" />; otherwise, <c>false</c>.
|
|
||||||
/// </returns>
|
|
||||||
public override bool Equals(NSObject obj) => Equals((object)obj);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if(obj is not UID uid)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return uid.value == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override int GetHashCode() => value.GetHashCode();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string ToString() => $"{value} (UID)";
|
|
||||||
|
|
||||||
/// <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>
|
|
||||||
public ulong ToUInt64() => value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 8-bit
|
||||||
|
/// number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">Unsigned 8-bit number.</param>
|
||||||
|
public UID(byte number) => value = number;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 16-bit
|
||||||
|
/// number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name.</param>
|
||||||
|
/// <param name="number">Unsigned 16-bit number.</param>
|
||||||
|
public UID(ushort number) => value = number;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 32-bit
|
||||||
|
/// number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">Unsigned 32-bit number.</param>
|
||||||
|
public UID(uint number) => value = number;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Claunia.PropertyList.UID" /> class using an unsigned 64-bit
|
||||||
|
/// number.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="number">Unsigned 64-bit number.</param>
|
||||||
|
public UID(ulong number) => value = number;
|
||||||
|
|
||||||
|
/// <summary>Gets the bytes.</summary>
|
||||||
|
/// <value>The bytes.</value>
|
||||||
|
public byte[] Bytes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[ByteCount];
|
||||||
|
GetBytes(bytes);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the number of bytes required to represent this <see cref="UID" />.</summary>
|
||||||
|
public int ByteCount => value switch
|
||||||
|
{
|
||||||
|
<= byte.MaxValue => 1,
|
||||||
|
<= ushort.MaxValue => 2,
|
||||||
|
<= uint.MaxValue => 4,
|
||||||
|
_ => 8
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>Writes the bytes required to represent this <see cref="UID" /> to a byte span.</summary>
|
||||||
|
/// <param name="bytes">The byte span to which to write the byte representation of this UID.</param>
|
||||||
|
public void GetBytes(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
switch(ByteCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
bytes[0] = (byte)value;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
BinaryPrimitives.WriteUInt16BigEndian(bytes, (ushort)value);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
BinaryPrimitives.WriteUInt32BigEndian(bytes, (uint)value);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
BinaryPrimitives.WriteUInt64BigEndian(bytes, value);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UIDs are represented as dictionaries in XML property lists, where the key is always <c>CF$UID</c> and the
|
||||||
|
/// value is the integer representation of the UID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xml">The xml StringBuilder</param>
|
||||||
|
/// <param name="level">The indentation level</param>
|
||||||
|
internal override void ToXml(StringBuilder xml, int level)
|
||||||
|
{
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("<dict>");
|
||||||
|
xml.AppendLine();
|
||||||
|
|
||||||
|
Indent(xml, level + 1);
|
||||||
|
xml.Append("<key>CF$UID</key>");
|
||||||
|
xml.AppendLine();
|
||||||
|
|
||||||
|
Indent(xml, level + 1);
|
||||||
|
xml.Append($"<integer>{value}</integer>");
|
||||||
|
xml.AppendLine();
|
||||||
|
|
||||||
|
Indent(xml, level);
|
||||||
|
xml.Append("</dict>");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToBinary(BinaryPropertyListWriter outPlist)
|
||||||
|
{
|
||||||
|
outPlist.Write(0x80 + ByteCount - 1);
|
||||||
|
Span<byte> bytes = stackalloc byte[ByteCount];
|
||||||
|
GetBytes(bytes);
|
||||||
|
outPlist.Write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCII(StringBuilder ascii, int level)
|
||||||
|
{
|
||||||
|
Indent(ascii, level);
|
||||||
|
ascii.Append("\"");
|
||||||
|
Span<byte> bytes = stackalloc byte[ByteCount];
|
||||||
|
GetBytes(bytes);
|
||||||
|
|
||||||
|
foreach(byte b in bytes) ascii.Append($"{b:x2}");
|
||||||
|
|
||||||
|
ascii.Append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void ToASCIIGnuStep(StringBuilder ascii, int level) => ToASCII(ascii, level);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.UID" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">
|
||||||
|
/// The <see cref="Claunia.PropertyList.NSObject" /> to compare with the current
|
||||||
|
/// <see cref="Claunia.PropertyList.UID" />.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the specified <see cref="Claunia.PropertyList.NSObject" /> is equal to the current
|
||||||
|
/// <see cref="Claunia.PropertyList.UID" />; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public override bool Equals(NSObject obj) => Equals((object)obj);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if(obj is not UID uid) return false;
|
||||||
|
|
||||||
|
return uid.value == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode() => value.GetHashCode();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString() => $"{value} (UID)";
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public ulong ToUInt64() => value;
|
||||||
}
|
}
|
||||||
@@ -1,121 +1,155 @@
|
|||||||
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>
|
/// <summary>
|
||||||
/// Allows you to override the default value class initialization for the values found
|
/// Indicates the semantic type of content the preprocessor will work on--independent
|
||||||
/// in the parsed plists by registering your own preprocessing implementations.
|
/// from the underlying data type (which will be string in most cases anyway).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ValuePreprocessor
|
public enum Type
|
||||||
{
|
{
|
||||||
/// <summary>
|
BOOL,
|
||||||
/// Indicates the semantic type of content the preprocessor will work on--independent
|
INTEGER,
|
||||||
/// from the underlying data type (which will be string in most cases anyway).
|
FLOATING_POINT,
|
||||||
/// </summary>
|
UNDEFINED_NUMBER,
|
||||||
public enum Type
|
STRING,
|
||||||
{
|
DATA,
|
||||||
BOOL, INTEGER, FLOATING_POINT,
|
DATE
|
||||||
UNDEFINED_NUMBER, STRING, DATA,
|
|
||||||
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>
|
|
||||||
/// Default preprocessors for all the standard cases.
|
|
||||||
/// </summary>
|
|
||||||
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.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.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>
|
|
||||||
/// Get a default preprocessor.
|
|
||||||
/// </summary>
|
|
||||||
public static Func<T, T> GetDefault<T>() => NullPreprocessor<T>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set up a custom preprocessor.
|
|
||||||
/// </summary>
|
|
||||||
public static void Set<T>(Func<T, T> preprocessor, Type type) =>
|
|
||||||
_preprocessors[new(type, typeof(T))] = preprocessor;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unset a specific preprocessor--replaces it with a null-implementation
|
|
||||||
/// to prevent argument errors.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
|
||||||
public static void Unset<T>(Type type) =>
|
|
||||||
_preprocessors[GetValidTypeIdentifier<T>(type)] = NullPreprocessor<T>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Completely unregister a specific preprocessor--remove it instead of
|
|
||||||
/// replacing it with a null-implementation.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was registered.</exception>
|
|
||||||
public static void Remove<T>(Type type) =>
|
|
||||||
_preprocessors.Remove(GetValidTypeIdentifier<T>(type));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Preprocess the supplied data using the appropriate registered implementation.
|
|
||||||
/// </summary>
|
|
||||||
/// <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)
|
|
||||||
? preprocess(value)
|
|
||||||
: throw new ArgumentException($"Failed to find a preprocessor for value '{value}'.");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the appropriate registered implementation--or null--and casts it back to
|
|
||||||
/// the required type.
|
|
||||||
/// </summary>
|
|
||||||
private static bool TryGetPreprocessor<T>(Type type, out Func<T, T> preprocess)
|
|
||||||
{
|
|
||||||
if(_preprocessors.TryGetValue(new TypeIdentifier(type, typeof(T)), out Delegate preprocessor))
|
|
||||||
{
|
|
||||||
preprocess = (Func<T, T>)preprocessor;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
preprocess = default;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a type identifier if a preprocessor exists for it.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
|
||||||
private static TypeIdentifier GetValidTypeIdentifier<T>(Type type)
|
|
||||||
{
|
|
||||||
var identifier = new TypeIdentifier(type, typeof(T));
|
|
||||||
|
|
||||||
if(!_preprocessors.ContainsKey(identifier))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Failed to find a valid preprocessor type identifier.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default preprocessors for all the standard cases.
|
||||||
|
/// </summary>
|
||||||
|
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.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.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>
|
||||||
|
/// Get a default preprocessor.
|
||||||
|
/// </summary>
|
||||||
|
public static Func<T, T> GetDefault<T>() => NullPreprocessor<T>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set up a custom preprocessor.
|
||||||
|
/// </summary>
|
||||||
|
public static void Set<T>(Func<T, T> preprocessor, Type type) =>
|
||||||
|
_preprocessors[new TypeIdentifier(type, typeof(T))] = preprocessor;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unset a specific preprocessor--replaces it with a null-implementation
|
||||||
|
/// to prevent argument errors.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
||||||
|
public static void Unset<T>(Type type) => _preprocessors[GetValidTypeIdentifier<T>(type)] = NullPreprocessor<T>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Completely unregister a specific preprocessor--remove it instead of
|
||||||
|
/// replacing it with a null-implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// If no appropriate preprocessor--not even a default null-implementation--was
|
||||||
|
/// registered.
|
||||||
|
/// </exception>
|
||||||
|
public static void Remove<T>(Type type) => _preprocessors.Remove(GetValidTypeIdentifier<T>(type));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Preprocess the supplied data using the appropriate registered implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <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)
|
||||||
|
? preprocess(value)
|
||||||
|
: throw new
|
||||||
|
ArgumentException($"Failed to find a preprocessor for value '{value}'.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the appropriate registered implementation--or null--and casts it back to
|
||||||
|
/// the required type.
|
||||||
|
/// </summary>
|
||||||
|
private static bool TryGetPreprocessor<T>(Type type, out Func<T, T> preprocess)
|
||||||
|
{
|
||||||
|
if(_preprocessors.TryGetValue(new TypeIdentifier(type, typeof(T)), out Delegate preprocessor))
|
||||||
|
{
|
||||||
|
preprocess = (Func<T, T>)preprocessor;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocess = default(Func<T, T>);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a type identifier if a preprocessor exists for it.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ArgumentException">If no appropriate preprocessor--not even a default null-implementation--was set up.</exception>
|
||||||
|
private static TypeIdentifier GetValidTypeIdentifier<T>(Type type)
|
||||||
|
{
|
||||||
|
var identifier = new TypeIdentifier(type, typeof(T));
|
||||||
|
|
||||||
|
if(!_preprocessors.ContainsKey(identifier))
|
||||||
|
throw new ArgumentException("Failed to find a valid preprocessor type identifier.");
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record struct TypeIdentifier(Type ValueType, System.Type DataType);
|
||||||
}
|
}
|
||||||
@@ -28,206 +28,230 @@ 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>
|
/// <summary>Parses a XML property list file.</summary>
|
||||||
/// @author Daniel Dreibrodt
|
/// <param name="f">The XML property list file.</param>
|
||||||
/// @author Natalia Portillo
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
public static class XmlPropertyListParser
|
public static NSObject Parse(FileInfo f)
|
||||||
{
|
{
|
||||||
/// <summary>Parses a XML property list file.</summary>
|
var doc = new XmlDocument();
|
||||||
/// <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>
|
|
||||||
public static NSObject Parse(FileInfo f)
|
|
||||||
{
|
|
||||||
var doc = new XmlDocument();
|
|
||||||
|
|
||||||
var settings = new XmlReaderSettings
|
var settings = new XmlReaderSettings
|
||||||
|
{
|
||||||
|
DtdProcessing = DtdProcessing.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
using(Stream stream = f.OpenRead())
|
||||||
|
{
|
||||||
|
using(var reader = XmlReader.Create(stream, settings))
|
||||||
{
|
{
|
||||||
DtdProcessing = DtdProcessing.Ignore
|
|
||||||
};
|
|
||||||
|
|
||||||
using(Stream stream = f.OpenRead())
|
|
||||||
using(var reader = XmlReader.Create(stream, settings))
|
|
||||||
doc.Load(reader);
|
|
||||||
|
|
||||||
return ParseDocument(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a XML property list from a byte array.</summary>
|
|
||||||
/// <param name="bytes">The byte array containing the property list's data.</param>
|
|
||||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(byte[] bytes)
|
|
||||||
{
|
|
||||||
var bis = new MemoryStream(bytes);
|
|
||||||
|
|
||||||
return Parse(bis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a XML property list from an input stream.</summary>
|
|
||||||
/// <param name="str">The input stream pointing to the property list's data.</param>
|
|
||||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject Parse(Stream str)
|
|
||||||
{
|
|
||||||
var doc = new XmlDocument();
|
|
||||||
|
|
||||||
var settings = new XmlReaderSettings();
|
|
||||||
settings.DtdProcessing = DtdProcessing.Ignore;
|
|
||||||
|
|
||||||
using(var reader = XmlReader.Create(str, settings))
|
|
||||||
doc.Load(reader);
|
doc.Load(reader);
|
||||||
|
|
||||||
return ParseDocument(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses a XML property list from a string.</summary>
|
|
||||||
/// <param name="value">The string pointing to the property list's data.</param>
|
|
||||||
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
|
||||||
public static NSObject ParseString(string value)
|
|
||||||
{
|
|
||||||
var doc = new XmlDocument();
|
|
||||||
|
|
||||||
var settings = new XmlReaderSettings();
|
|
||||||
settings.DtdProcessing = DtdProcessing.Ignore;
|
|
||||||
|
|
||||||
doc.LoadXml(value);
|
|
||||||
|
|
||||||
return ParseDocument(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Parses the XML document by generating the appropriate NSObjects for each XML node.</summary>
|
|
||||||
/// <returns>The root NSObject of the property list contained in the XML document.</returns>
|
|
||||||
/// <param name="doc">The XML document.</param>
|
|
||||||
static NSObject ParseDocument(XmlDocument doc)
|
|
||||||
{
|
|
||||||
XmlNode docType = doc.ChildNodes.OfType<XmlNode>().
|
|
||||||
SingleOrDefault(n => n.NodeType == XmlNodeType.DocumentType);
|
|
||||||
|
|
||||||
if(docType == null)
|
|
||||||
{
|
|
||||||
if(doc.DocumentElement != null &&
|
|
||||||
!doc.DocumentElement.Name.Equals("plist"))
|
|
||||||
throw new XmlException("The given XML document is not a property list.");
|
|
||||||
}
|
}
|
||||||
else if(!docType.Name.Equals("plist"))
|
}
|
||||||
|
|
||||||
|
return ParseDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a XML property list from a byte array.</summary>
|
||||||
|
/// <param name="bytes">The byte array containing the property list's data.</param>
|
||||||
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(byte[] bytes)
|
||||||
|
{
|
||||||
|
var bis = new MemoryStream(bytes);
|
||||||
|
|
||||||
|
return Parse(bis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a XML property list from an input stream.</summary>
|
||||||
|
/// <param name="str">The input stream pointing to the property list's data.</param>
|
||||||
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject Parse(Stream str)
|
||||||
|
{
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
|
||||||
|
var settings = new XmlReaderSettings
|
||||||
|
{
|
||||||
|
DtdProcessing = DtdProcessing.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
using(var reader = XmlReader.Create(str, settings))
|
||||||
|
{
|
||||||
|
doc.Load(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a XML property list from a string.</summary>
|
||||||
|
/// <param name="value">The string pointing to the property list's data.</param>
|
||||||
|
/// <returns>The root object of the property list. This is usually a NSDictionary but can also be a NSArray.</returns>
|
||||||
|
public static NSObject ParseString(string value)
|
||||||
|
{
|
||||||
|
var doc = new XmlDocument();
|
||||||
|
|
||||||
|
var settings = new XmlReaderSettings
|
||||||
|
{
|
||||||
|
DtdProcessing = DtdProcessing.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
doc.LoadXml(value);
|
||||||
|
|
||||||
|
return ParseDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses the XML document by generating the appropriate NSObjects for each XML node.</summary>
|
||||||
|
/// <returns>The root NSObject of the property list contained in the XML document.</returns>
|
||||||
|
/// <param name="doc">The XML document.</param>
|
||||||
|
static NSObject ParseDocument(XmlDocument doc)
|
||||||
|
{
|
||||||
|
XmlNode docType = doc.ChildNodes.OfType<XmlNode>().SingleOrDefault(n => n.NodeType == XmlNodeType.DocumentType);
|
||||||
|
|
||||||
|
if(docType == null)
|
||||||
|
{
|
||||||
|
if(doc.DocumentElement != null && !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.");
|
||||||
|
|
||||||
XmlNode rootNode;
|
|
||||||
|
|
||||||
if(doc.DocumentElement is { Name: "plist" })
|
|
||||||
{
|
|
||||||
//Root element wrapped in plist tag
|
|
||||||
List<XmlNode> rootNodes = FilterElementNodes(doc.DocumentElement.ChildNodes);
|
|
||||||
|
|
||||||
rootNode = rootNodes.Count switch
|
|
||||||
{
|
|
||||||
0 => throw new PropertyListFormatException("The given XML property list has no root element!"),
|
|
||||||
1 => rootNodes[0],
|
|
||||||
_ => throw new
|
|
||||||
PropertyListFormatException("The given XML property list has more than one root element!")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
|
|
||||||
//Root NSObject not wrapped in plist-tag
|
|
||||||
rootNode = doc.DocumentElement;
|
|
||||||
|
|
||||||
return ParseObject(rootNode);
|
|
||||||
}
|
}
|
||||||
|
else if(!docType.Name.Equals("plist")) throw new XmlException("The given XML document is not a property list.");
|
||||||
|
|
||||||
/// <summary>Parses a node in the XML structure and returns the corresponding NSObject</summary>
|
XmlNode rootNode;
|
||||||
/// <returns>The corresponding NSObject.</returns>
|
|
||||||
/// <param name="n">The XML node.</param>
|
if(doc.DocumentElement is { Name: "plist" })
|
||||||
static NSObject ParseObject(XmlNode n)
|
|
||||||
{
|
{
|
||||||
switch(n.Name)
|
//Root element wrapped in plist tag
|
||||||
|
List<XmlNode> rootNodes = FilterElementNodes(doc.DocumentElement.ChildNodes);
|
||||||
|
|
||||||
|
rootNode = rootNodes.Count switch
|
||||||
|
{
|
||||||
|
0 => throw new
|
||||||
|
PropertyListFormatException("The given XML property list has no root element!"),
|
||||||
|
1 => rootNodes[0],
|
||||||
|
_ => throw new
|
||||||
|
PropertyListFormatException("The given XML property list has more than one root element!")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
|
||||||
|
//Root NSObject not wrapped in plist-tag
|
||||||
|
rootNode = doc.DocumentElement;
|
||||||
|
|
||||||
|
return ParseObject(rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Parses a node in the XML structure and returns the corresponding NSObject</summary>
|
||||||
|
/// <returns>The corresponding NSObject.</returns>
|
||||||
|
/// <param name="n">The XML node.</param>
|
||||||
|
static NSObject ParseObject(XmlNode n)
|
||||||
|
{
|
||||||
|
switch(n.Name)
|
||||||
|
{
|
||||||
|
// Special case for UID values
|
||||||
|
case "dict" when n.ChildNodes.Count == 2 &&
|
||||||
|
n.ChildNodes[0].Name == "key" &&
|
||||||
|
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":
|
||||||
{
|
{
|
||||||
// Special case for UID values
|
var dict = new NSDictionary();
|
||||||
case "dict" when n.ChildNodes.Count == 2 && n.ChildNodes[0].Name == "key" &&
|
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
||||||
n.ChildNodes[0].InnerText == "CF$UID" && n.ChildNodes[1].Name == "integer" &&
|
|
||||||
uint.TryParse(n.ChildNodes[1].InnerText, out uint uidValue): return new UID(uidValue);
|
for(int i = 0; i < children.Count; i += 2)
|
||||||
case "dict":
|
|
||||||
{
|
{
|
||||||
var dict = new NSDictionary();
|
XmlNode key = children[i];
|
||||||
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
XmlNode val = children[i + 1];
|
||||||
|
|
||||||
for(int i = 0; i < children.Count; i += 2)
|
string keyString = GetNodeTextContents(key);
|
||||||
{
|
|
||||||
XmlNode key = children[i];
|
|
||||||
XmlNode val = children[i + 1];
|
|
||||||
|
|
||||||
string keyString = GetNodeTextContents(key);
|
dict.Add(keyString, ParseObject(val));
|
||||||
|
|
||||||
dict.Add(keyString, ParseObject(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
}
|
}
|
||||||
case "array":
|
|
||||||
{
|
|
||||||
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
|
||||||
var array = new NSArray(children.Count);
|
|
||||||
|
|
||||||
for(int i = 0; i < children.Count; i++)
|
return dict;
|
||||||
array.Add(ParseObject(children[i]));
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
case "true": return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
|
||||||
case "false": return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
|
||||||
case "integer": return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n), 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;
|
|
||||||
}
|
}
|
||||||
}
|
case "array":
|
||||||
|
|
||||||
/// <summary>Returns all element nodes that are contained in a list of nodes.</summary>
|
|
||||||
/// <returns>The sublist containing only nodes representing actual elements.</returns>
|
|
||||||
/// <param name="list">The list of nodes to search.</param>
|
|
||||||
static List<XmlNode> FilterElementNodes(XmlNodeList list)
|
|
||||||
{
|
|
||||||
List<XmlNode> result = new();
|
|
||||||
|
|
||||||
foreach(XmlNode child in list)
|
|
||||||
if(child.NodeType == XmlNodeType.Element)
|
|
||||||
result.Add(child);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a node's text content. This method will return the text value represented by the node's direct
|
|
||||||
/// children. If the given node is a TEXT or CDATA node, then its value is returned.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The node's text content.</returns>
|
|
||||||
/// <param name="n">The node.</param>
|
|
||||||
static string GetNodeTextContents(XmlNode n)
|
|
||||||
{
|
|
||||||
if(n.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
|
||||||
{
|
{
|
||||||
string content = n.Value; //This concatenates any adjacent text/cdata/entity nodes
|
List<XmlNode> children = FilterElementNodes(n.ChildNodes);
|
||||||
|
var array = new NSArray(children.Count);
|
||||||
|
|
||||||
|
for(int i = 0; i < children.Count; i++) array.Add(ParseObject(children[i]));
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
case "true":
|
||||||
|
return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
|
case "false":
|
||||||
|
return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
|
case "integer":
|
||||||
|
return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns all element nodes that are contained in a list of nodes.</summary>
|
||||||
|
/// <returns>The sublist containing only nodes representing actual elements.</returns>
|
||||||
|
/// <param name="list">The list of nodes to search.</param>
|
||||||
|
static List<XmlNode> FilterElementNodes(XmlNodeList list)
|
||||||
|
{
|
||||||
|
List<XmlNode> result = [];
|
||||||
|
|
||||||
|
foreach(XmlNode child in list)
|
||||||
|
if(child.NodeType == XmlNodeType.Element) result.Add(child);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a node's text content. This method will return the text value represented by the node's direct
|
||||||
|
/// children. If the given node is a TEXT or CDATA node, then its value is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The node's text content.</returns>
|
||||||
|
/// <param name="n">The node.</param>
|
||||||
|
static string GetNodeTextContents(XmlNode n)
|
||||||
|
{
|
||||||
|
if(n.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
||||||
|
{
|
||||||
|
string content = n.Value; //This concatenates any adjacent text/cdata/entity nodes
|
||||||
|
|
||||||
|
return content ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!n.HasChildNodes) return "";
|
||||||
|
|
||||||
|
XmlNodeList children = n.ChildNodes;
|
||||||
|
|
||||||
|
foreach(XmlNode child in children)
|
||||||
|
|
||||||
|
//Skip any non-text nodes, like comments or entities
|
||||||
|
{
|
||||||
|
if(child.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
||||||
|
{
|
||||||
|
string content = child.Value; //This concatenates any adjacent text/cdata/entity nodes
|
||||||
|
|
||||||
return content ?? "";
|
return content ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!n.HasChildNodes)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
XmlNodeList children = n.ChildNodes;
|
|
||||||
|
|
||||||
foreach(XmlNode child in children)
|
|
||||||
|
|
||||||
//Skip any non-text nodes, like comments or entities
|
|
||||||
if(child.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
|
|
||||||
{
|
|
||||||
string content = child.Value; //This concatenates any adjacent text/cdata/entity nodes
|
|
||||||
|
|
||||||
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,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">
|
||||||
<array>
|
<array>
|
||||||
<dict/>
|
<dict/>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
<dict/>
|
<dict/>
|
||||||
</array>
|
</array>
|
||||||
|
|||||||
@@ -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,21 +1,21 @@
|
|||||||
<?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>
|
||||||
<string>value&B</string>
|
<string>value&B</string>
|
||||||
<key>date</key>
|
<key>date</key>
|
||||||
<date>2011-11-28T09:21:30Z</date>
|
<date>2011-11-28T09:21:30Z</date>
|
||||||
<key>data</key>
|
<key>data</key>
|
||||||
<data>AAAA BBBB CCCC</data>
|
<data>AAAA BBBB CCCC</data>
|
||||||
<key>array</key>
|
<key>array</key>
|
||||||
<array>
|
<array>
|
||||||
<true/>
|
<true/>
|
||||||
<false/>
|
<false/>
|
||||||
<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,18 +1,18 @@
|
|||||||
<?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>
|
||||||
<integer>9223372036854775807</integer>
|
<integer>9223372036854775807</integer>
|
||||||
<key>number3</key>
|
<key>number3</key>
|
||||||
<real>-3.12312423423</real>
|
<real>-3.12312423423</real>
|
||||||
<key>number4</key>
|
<key>number4</key>
|
||||||
<integer>-9223372036854775808</integer>
|
<integer>-9223372036854775808</integer>
|
||||||
<key>number5</key>
|
<key>number5</key>
|
||||||
<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>
|
||||||
|
|||||||
12
version.json
12
version.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
|
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
|
||||||
"version": "2.2",
|
"version": "2.2",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/master$",
|
"^refs/heads/master$",
|
||||||
"^refs/heads/releases/*"
|
"^refs/heads/releases/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user