mirror of
https://github.com/claunia/plist-cil.git
synced 2025-12-16 11:04:26 +00:00
More cleanup, handle binary parsing more cleanly
This commit is contained in:
@@ -1,28 +1,3 @@
|
||||
// plist-cil - An open source library to Parse and generate property lists for .NET
|
||||
// Copyright (C) 2015 Natalia Portillo
|
||||
//
|
||||
// This code is based on:
|
||||
// plist - An open source library to Parse and generate property lists
|
||||
// Copyright (C) 2014 Daniel Dreibrodt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Claunia.PropertyList;
|
||||
@@ -30,63 +5,110 @@ using Xunit;
|
||||
|
||||
namespace plistcil.test
|
||||
{
|
||||
|
||||
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()
|
||||
{
|
||||
Assert.Equal(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL), true);
|
||||
Assert.Equal(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL), false);
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("true", ValuePreprocessor.Type.BOOL), "true");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("42",ValuePreprocessor.Type.INTEGER), "42");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("3.14159",ValuePreprocessor.Type.FLOATING_POINT), "3.14159");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("2.71828", ValuePreprocessor.Type.UNDEFINED_NUMBER), "2.71828");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("TestString",ValuePreprocessor.Type.STRING), "TestString");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("TestData",ValuePreprocessor.Type.DATA), "TestData");
|
||||
byte[] value = { 0x1, 0x2, 0x4, 0x8 };
|
||||
Assert.Equal(ValuePreprocessor.Preprocess(value,ValuePreprocessor.Type.DATA), value);
|
||||
Assert.Equal(ValuePreprocessor.Preprocess("01.02.1903",ValuePreprocessor.Type.DATE), "01.02.1903");
|
||||
Assert.Equal(ValuePreprocessor.Preprocess(23.0, ValuePreprocessor.Type.DATE), 23.0);
|
||||
Assert.Equal(true, ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||
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("3.14159", ValuePreprocessor.Preprocess("3.14159", 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("TestData", ValuePreprocessor.Preprocess("TestData", ValuePreprocessor.Type.DATA));
|
||||
byte[] value = [0x1, 0x2, 0x4, 0x8];
|
||||
Assert.Equal(value, ValuePreprocessor.Preprocess(value, ValuePreprocessor.Type.DATA));
|
||||
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()
|
||||
{
|
||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||
string testString = "TestString";
|
||||
string expected = "gnirtStseT";
|
||||
lock(_testLock)
|
||||
{
|
||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||
string testString = "TestString";
|
||||
string expected = "gnirtStseT";
|
||||
|
||||
ValuePreprocessor.Register(examplePreprocessor, ValuePreprocessor.Type.STRING);
|
||||
string actual = ValuePreprocessor.Preprocess(testString, ValuePreprocessor.Type.STRING);
|
||||
var testType = (ValuePreprocessor.Type)42;
|
||||
|
||||
Assert.Equal(actual, expected);
|
||||
ValuePreprocessor.Set(examplePreprocessor, testType);
|
||||
string actual = ValuePreprocessor.Preprocess(testString, testType);
|
||||
|
||||
ValuePreprocessor.Unregister<string>(ValuePreprocessor.Type.STRING);
|
||||
Assert.Equal(actual, expected);
|
||||
|
||||
ValuePreprocessor.Unset<string>(testType);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TestRegisteredPreprocessorSelection()
|
||||
public static void TestRegisteredPreprocessorSelection1()
|
||||
{
|
||||
Func<string, string> examplePreprocessor = value => new string(value.Reverse().ToArray());
|
||||
string testString = "TestString";
|
||||
lock(_testLock)
|
||||
{
|
||||
Func<short, short> examplePreprocessor = value => (short)(value - 1);
|
||||
short testShort = 42;
|
||||
string testString = "TestString";
|
||||
|
||||
ValuePreprocessor.Register(examplePreprocessor, ValuePreprocessor.Type.STRING);
|
||||
string actual = ValuePreprocessor.Preprocess(testString, ValuePreprocessor.Type.DATA);
|
||||
var testType = (ValuePreprocessor.Type)42;
|
||||
|
||||
// assert unchanged, since the selected preprocessor != registered preprocessor
|
||||
Assert.Equal(actual, testString);
|
||||
// correct value type, differing data type
|
||||
ValuePreprocessor.Set(examplePreprocessor, testType);
|
||||
ValuePreprocessor.Set(ValuePreprocessor.GetDefault<string>(), testType);
|
||||
|
||||
ValuePreprocessor.Unregister<string>(ValuePreprocessor.Type.STRING);
|
||||
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 TestUnregisterdPreprocessorThrows()
|
||||
public static void TestRegisteredPreprocessorSelection2()
|
||||
{
|
||||
byte[] testArray = { 0x1, 0x2, 0x4, 0x8 };
|
||||
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()
|
||||
{
|
||||
byte[] testArray = [0x1, 0x2, 0x4, 0x8];
|
||||
|
||||
// there's no registered preprocessor for byte array arguments for STRING
|
||||
Assert.Throws<ArgumentException>(() => ValuePreprocessor.Preprocess(testArray, ValuePreprocessor.Type.STRING));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,14 +243,14 @@ namespace Claunia.PropertyList
|
||||
//integer
|
||||
int length = 1 << objInfo;
|
||||
|
||||
return new NSNumber(bytes.Slice(offset + 1, length), NSNumber.INTEGER);
|
||||
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(), ValuePreprocessor.Type.INTEGER), NSNumber.INTEGER);
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
//real
|
||||
int length = 1 << objInfo;
|
||||
|
||||
return new NSNumber(bytes.Slice(offset + 1, length), NSNumber.REAL);
|
||||
return new NSNumber(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, length).ToArray(), ValuePreprocessor.Type.FLOATING_POINT), NSNumber.REAL);
|
||||
}
|
||||
case 0x3:
|
||||
{
|
||||
|
||||
@@ -69,13 +69,13 @@ namespace Claunia.PropertyList
|
||||
switch(type)
|
||||
{
|
||||
case INTEGER:
|
||||
doubleValue = longValue = ValuePreprocessor.Preprocess(BinaryPropertyListParser.ParseLong(bytes), ValuePreprocessor.Type.INTEGER);
|
||||
doubleValue = longValue = BinaryPropertyListParser.ParseLong(bytes);
|
||||
|
||||
break;
|
||||
|
||||
case REAL:
|
||||
doubleValue = ValuePreprocessor.Preprocess(BinaryPropertyListParser.ParseDouble(bytes), ValuePreprocessor.Type.FLOATING_POINT);
|
||||
longValue = ValuePreprocessor.Preprocess((long)Math.Round(doubleValue), ValuePreprocessor.Type.INTEGER);
|
||||
doubleValue = BinaryPropertyListParser.ParseDouble(bytes);
|
||||
longValue = (long)Math.Round(doubleValue);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -147,11 +147,11 @@ namespace Claunia.PropertyList
|
||||
return type;
|
||||
}
|
||||
|
||||
/// <summary>Register preprocessing functions for for plist values.</summary>
|
||||
/// <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 RegisterValuePreprocessor<T>(Func<T, T> preprocessor, ValuePreprocessor.Type type) =>
|
||||
ValuePreprocessor.Register(preprocessor, type);
|
||||
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>
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Claunia.PropertyList
|
||||
/// </summary>
|
||||
private static T NullPreprocessor<T>(T value) => value;
|
||||
|
||||
private record struct TypeIdentifier(Type ValueType, System.Type ProcessingType);
|
||||
private record struct TypeIdentifier(Type ValueType, System.Type DataType);
|
||||
|
||||
/// <summary>
|
||||
/// Default preprocessors for all the standard cases.
|
||||
@@ -35,7 +35,9 @@ namespace Claunia.PropertyList
|
||||
{ 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.DATA, typeof(string)), NullPreprocessor<string> },
|
||||
@@ -45,16 +47,32 @@ namespace Claunia.PropertyList
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Register a custom preprocessor.
|
||||
/// Get a default preprocessor.
|
||||
/// </summary>
|
||||
public static void Register<T>(Func<T, T> preprocessor, Type type) =>
|
||||
_preprocessors[new(type, typeof(T))] = preprocessor;
|
||||
public static Func<T, T> GetDefault<T>() => NullPreprocessor<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Unregister a specific preprocessor--replaces it with a null-implementation
|
||||
/// 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>
|
||||
public static void Unregister<T>(Type type) => _preprocessors[new(type, typeof(T))] = NullPreprocessor<T>;
|
||||
/// <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.
|
||||
@@ -65,7 +83,8 @@ namespace Claunia.PropertyList
|
||||
: 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.
|
||||
/// 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)
|
||||
{
|
||||
@@ -80,5 +99,21 @@ namespace Claunia.PropertyList
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user