mirror of
https://github.com/claunia/plist-cil.git
synced 2025-12-16 19:14:26 +00:00
Merge pull request #82 from JohannStudanskiEnscape/ValuePreprocessor
Integrate a way to set up custom value preprocessors
This commit is contained in:
125
plist-cil.test/ValuePreprocessorTests.cs
Normal file
125
plist-cil.test/ValuePreprocessorTests.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Claunia.PropertyList;
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
byte[] testByteArray = [0x1, 0x2, 0x4, 0x8];
|
||||||
|
|
||||||
|
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(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)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -409,15 +409,15 @@ namespace Claunia.PropertyList
|
|||||||
quotedString[4] == DATE_DATE_FIELD_DELIMITER)
|
quotedString[4] == DATE_DATE_FIELD_DELIMITER)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new NSDate(quotedString);
|
return new NSDate(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.DATE));
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch(Exception)
|
||||||
{
|
{
|
||||||
//not a date? --> return string
|
//not a date? --> return string
|
||||||
return new NSString(quotedString);
|
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NSString(quotedString);
|
return new NSString(ValuePreprocessor.Preprocess(quotedString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -429,7 +429,7 @@ namespace Claunia.PropertyList
|
|||||||
//non-numerical -> string or boolean
|
//non-numerical -> string or boolean
|
||||||
string parsedString = ParseString();
|
string parsedString = ParseString();
|
||||||
|
|
||||||
return new NSString(parsedString);
|
return new NSString(ValuePreprocessor.Preprocess(parsedString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,9 +529,9 @@ namespace Claunia.PropertyList
|
|||||||
Expect(DATA_GSBOOL_TRUE_TOKEN, DATA_GSBOOL_FALSE_TOKEN);
|
Expect(DATA_GSBOOL_TRUE_TOKEN, DATA_GSBOOL_FALSE_TOKEN);
|
||||||
|
|
||||||
if(Accept(DATA_GSBOOL_TRUE_TOKEN))
|
if(Accept(DATA_GSBOOL_TRUE_TOKEN))
|
||||||
obj = new NSNumber(true);
|
obj = new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
else
|
else
|
||||||
obj = new NSNumber(false);
|
obj = new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
|
|
||||||
//Skip the parsed boolean token
|
//Skip the parsed boolean token
|
||||||
Skip();
|
Skip();
|
||||||
@@ -541,14 +541,14 @@ namespace Claunia.PropertyList
|
|||||||
//Date
|
//Date
|
||||||
Skip();
|
Skip();
|
||||||
string dateString = ReadInputUntil(DATA_END_TOKEN);
|
string dateString = ReadInputUntil(DATA_END_TOKEN);
|
||||||
obj = new NSDate(dateString);
|
obj = new NSDate(ValuePreprocessor.Preprocess(dateString, ValuePreprocessor.Type.DATE));
|
||||||
}
|
}
|
||||||
else if(Accept(DATA_GSINT_BEGIN_TOKEN, DATA_GSREAL_BEGIN_TOKEN))
|
else if(Accept(DATA_GSINT_BEGIN_TOKEN, DATA_GSREAL_BEGIN_TOKEN))
|
||||||
{
|
{
|
||||||
//Number
|
//Number
|
||||||
Skip();
|
Skip();
|
||||||
string numberString = ReadInputUntil(DATA_END_TOKEN);
|
string numberString = ReadInputUntil(DATA_END_TOKEN);
|
||||||
obj = new NSNumber(numberString);
|
obj = new NSNumber(ValuePreprocessor.Preprocess(numberString, ValuePreprocessor.Type.UNDEFINED_NUMBER));
|
||||||
}
|
}
|
||||||
|
|
||||||
//parse data end token
|
//parse data end token
|
||||||
@@ -569,7 +569,7 @@ namespace Claunia.PropertyList
|
|||||||
bytes[i] = (byte)byteValue;
|
bytes[i] = (byte)byteValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = new NSData(bytes);
|
obj = new NSData(ValuePreprocessor.Preprocess(bytes, ValuePreprocessor.Type.DATA));
|
||||||
|
|
||||||
//skip end token
|
//skip end token
|
||||||
Skip();
|
Skip();
|
||||||
@@ -586,18 +586,18 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
if(numericalString.Length <= 4 ||
|
if(numericalString.Length <= 4 ||
|
||||||
numericalString[4] != DATE_DATE_FIELD_DELIMITER)
|
numericalString[4] != DATE_DATE_FIELD_DELIMITER)
|
||||||
return new NSString(numericalString);
|
return new NSString(ValuePreprocessor.Preprocess(numericalString, ValuePreprocessor.Type.STRING));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new NSDate(numericalString);
|
return new NSDate(ValuePreprocessor.Preprocess(numericalString, ValuePreprocessor.Type.DATE));
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch(Exception)
|
||||||
{
|
{
|
||||||
//An exception occurs if the string is not a date but just a string
|
//An exception occurs if the string is not a date but just a string
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NSString(numericalString);
|
return new NSString(ValuePreprocessor.Preprocess(numericalString, ValuePreprocessor.Type.STRING));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace Claunia.PropertyList
|
|||||||
//Read all bytes into a list
|
//Read all bytes into a list
|
||||||
byte[] buf = PropertyListParser.ReadAll(fs);
|
byte[] buf = PropertyListParser.ReadAll(fs);
|
||||||
|
|
||||||
// Don't close the stream - that would be the responisibility of code that class
|
// Don't close the stream - that would be the responsibility of code that class
|
||||||
// Parse
|
// Parse
|
||||||
return Parse(buf);
|
return Parse(buf);
|
||||||
}
|
}
|
||||||
@@ -204,12 +204,12 @@ namespace Claunia.PropertyList
|
|||||||
case 0x8:
|
case 0x8:
|
||||||
{
|
{
|
||||||
//false
|
//false
|
||||||
return new NSNumber(false);
|
return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
}
|
}
|
||||||
case 0x9:
|
case 0x9:
|
||||||
{
|
{
|
||||||
//true
|
//true
|
||||||
return new NSNumber(true);
|
return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
}
|
}
|
||||||
case 0xC:
|
case 0xC:
|
||||||
{
|
{
|
||||||
@@ -243,14 +243,14 @@ namespace Claunia.PropertyList
|
|||||||
//integer
|
//integer
|
||||||
int length = 1 << objInfo;
|
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:
|
case 0x2:
|
||||||
{
|
{
|
||||||
//real
|
//real
|
||||||
int length = 1 << objInfo;
|
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:
|
case 0x3:
|
||||||
{
|
{
|
||||||
@@ -260,21 +260,21 @@ namespace Claunia.PropertyList
|
|||||||
PropertyListFormatException("The given binary property list contains a date object of an unknown type (" +
|
PropertyListFormatException("The given binary property list contains a date object of an unknown type (" +
|
||||||
objInfo + ")");
|
objInfo + ")");
|
||||||
|
|
||||||
return new NSDate(bytes.Slice(offset + 1, 8));
|
return new NSDate(ValuePreprocessor.Preprocess(bytes.Slice(offset + 1, 8).ToArray(), ValuePreprocessor.Type.DATE));
|
||||||
}
|
}
|
||||||
case 0x4:
|
case 0x4:
|
||||||
{
|
{
|
||||||
//Data
|
//Data
|
||||||
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int dataoffset);
|
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int dataoffset);
|
||||||
|
|
||||||
return new NSData(CopyOfRange(bytes, offset + dataoffset, offset + dataoffset + length));
|
return new NSData(ValuePreprocessor.Preprocess(CopyOfRange(bytes, offset + dataoffset, offset + dataoffset + length), ValuePreprocessor.Type.DATA));
|
||||||
}
|
}
|
||||||
case 0x5:
|
case 0x5:
|
||||||
{
|
{
|
||||||
//ASCII String, each character is 1 byte
|
//ASCII String, each character is 1 byte
|
||||||
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);
|
ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);
|
||||||
|
|
||||||
return new NSString(bytes.Slice(offset + stroffset, length), Encoding.ASCII);
|
return new NSString(ValuePreprocessor.Preprocess(bytes.Slice(offset + stroffset, length).ToArray(), ValuePreprocessor.Type.STRING), Encoding.ASCII);
|
||||||
}
|
}
|
||||||
case 0x6:
|
case 0x6:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// plist-cil - An open source library to parse and generate property lists for .NET
|
// plist-cil - An open source library to parse and generate property lists for .NET
|
||||||
// Copyright (C) 2015 Natalia Portillo
|
// Copyright (C) 2015 Natalia Portillo
|
||||||
//
|
//
|
||||||
// This code is based on:
|
// This code is based on:
|
||||||
@@ -147,6 +147,12 @@ namespace Claunia.PropertyList
|
|||||||
return type;
|
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>
|
/// <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>
|
/// <param name="fs">The Stream pointing to the data that should be stored in the array.</param>
|
||||||
internal static byte[] ReadAll(Stream fs)
|
internal static byte[] ReadAll(Stream fs)
|
||||||
|
|||||||
121
plist-cil/ValuePreprocessor.cs
Normal file
121
plist-cil/ValuePreprocessor.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
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>
|
||||||
|
/// Indicates the semantic type of content the preprocessor will work on--independent
|
||||||
|
/// from the underlying data type (which will be string in most cases anyway).
|
||||||
|
/// </summary>
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
BOOL, INTEGER, FLOATING_POINT,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -173,13 +173,13 @@ namespace Claunia.PropertyList
|
|||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
case "true": return new NSNumber(true);
|
case "true": return new NSNumber(ValuePreprocessor.Preprocess(true, ValuePreprocessor.Type.BOOL));
|
||||||
case "false": return new NSNumber(false);
|
case "false": return new NSNumber(ValuePreprocessor.Preprocess(false, ValuePreprocessor.Type.BOOL));
|
||||||
case "integer": return new NSNumber(GetNodeTextContents(n), NSNumber.INTEGER);
|
case "integer": return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.INTEGER), NSNumber.INTEGER);
|
||||||
case "real": return new NSNumber(GetNodeTextContents(n), NSNumber.REAL);
|
case "real": return new NSNumber(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.FLOATING_POINT), NSNumber.REAL);
|
||||||
case "string": return new NSString(GetNodeTextContents(n));
|
case "string": return new NSString(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.STRING));
|
||||||
case "data": return new NSData(GetNodeTextContents(n));
|
case "data": return new NSData(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.DATA));
|
||||||
default: return n.Name.Equals("date") ? new NSDate(GetNodeTextContents(n)) : null;
|
default: return n.Name.Equals("date") ? new NSDate(ValuePreprocessor.Preprocess(GetNodeTextContents(n), ValuePreprocessor.Type.DATE)) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user