Browse Source

Added CollectionConverter and some derivatives

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
a59a92d895
7 changed files with 163 additions and 3 deletions
  1. +6
    -1
      IPA.Loader/Config/Stores/Attributes.cs
  2. +129
    -0
      IPA.Loader/Config/Stores/CollectionConverter.cs
  3. +19
    -0
      IPA.Loader/Config/Stores/Converters.cs
  4. +2
    -2
      IPA.Loader/Config/Stores/GeneratedStore.cs
  5. +3
    -0
      IPA.Loader/Config/Stores/ValueConverter.cs
  6. +1
    -0
      IPA.Loader/IPA.Loader.csproj
  7. +3
    -0
      IPA.Loader/Loader/DisabledConfig.cs

+ 6
- 1
IPA.Loader/Config/Stores/Attributes.cs View File

@ -23,7 +23,7 @@ namespace IPA.Config.Stores.Attributes
/// <summary> /// <summary>
/// Indicates that a given field or property in an object being wrapped by <see cref="GeneratedExtension.Generated{T}(Config, bool)"/> /// Indicates that a given field or property in an object being wrapped by <see cref="GeneratedExtension.Generated{T}(Config, bool)"/>
/// should be serialized and deserialized using the provided converter instead of the default mechanism.
/// should be serialized and deserialized using the provided converter instead of the default mechanism.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class UseConverterAttribute : Attribute public sealed class UseConverterAttribute : Attribute
@ -40,6 +40,11 @@ namespace IPA.Config.Stores.Attributes
ConverterType.BaseType.GetGenericArguments()[0] : ConverterType.BaseType.GetGenericArguments()[0] :
null; null;
/// <summary>
/// Gets whether or not this converter is a generic <see cref="ValueConverter{T}"/>.
/// </summary>
public bool IsGenericConverter => ConverterTargetType != null;
/// <summary> /// <summary>
/// Creates a new <see cref="UseConverterAttribute"/> with a given <see cref="ConverterType"/>. /// Creates a new <see cref="UseConverterAttribute"/> with a given <see cref="ConverterType"/>.
/// </summary> /// </summary>


+ 129
- 0
IPA.Loader/Config/Stores/CollectionConverter.cs View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IPA.Config.Data;
namespace IPA.Config.Stores.Converters
{
/// <summary>
/// A base class for all <see cref="ICollection{T}"/> type converters, providing most of the functionality.
/// </summary>
/// <typeparam name="T">the type of the items in the collection</typeparam>
/// <typeparam name="TCollection">the instantiated type of collection</typeparam>
public class CollectionConverter<T, TCollection> : ValueConverter<TCollection>
where TCollection : ICollection<T>
{
/// <summary>
/// Creates a <see cref="CollectionConverter{T, TCollection}"/> using the default converter for the
/// element type. Equivalent to calling <see cref="CollectionConverter{T, TCollection}.CollectionConverter(ValueConverter{T})"/>
/// with <see cref="Converter{T}.Default"/>.
/// </summary>
/// <seealso cref="CollectionConverter{T, TCollection}.CollectionConverter(ValueConverter{T})"/>
public CollectionConverter() : this(Converter<T>.Default) { }
/// <summary>
/// Creates a <see cref="CollectionConverter{T, TCollection}"/> using the specified underlying converter.
/// </summary>
/// <param name="underlying">the <see cref="ValueConverter{T}"/> to use to convert the values</param>
public CollectionConverter(ValueConverter<T> underlying)
=> BaseConverter = underlying;
/// <summary>
/// Gets the converter for the collection's value type.
/// </summary>
protected ValueConverter<T> BaseConverter { get; }
/// <summary>
/// Creates a collection of type <typeparamref name="TCollection"/> using the <paramref name="size"/> and
/// <paramref name="parent"/>.
/// </summary>
/// <param name="size">the initial size of the collecion</param>
/// <param name="parent">the object that will own the new collection</param>
/// <returns>a new instance of <typeparamref name="TCollection"/></returns>
/// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/>
protected virtual TCollection Create(int size, object parent)
=> Activator.CreateInstance<TCollection>();
/// <summary>
/// Populates the colleciton <paramref name="col"/> with the deserialized values from <paramref name="list"/>
/// with the parent <paramref name="parent"/>.
/// </summary>
/// <param name="col">the collection to populate</param>
/// <param name="list">the values to populate it with</param>
/// <param name="parent">the object that will own the new objects</param>
/// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/>
protected void PopulateFromValue(TCollection col, List list, object parent)
{
foreach (var it in list)
col.Add(BaseConverter.FromValue(it, parent));
}
/// <summary>
/// Deserializes a <see cref="List"/> in <paramref name="value"/> into a new <typeparamref name="TCollection"/>
/// owned by <paramref name="parent"/>.
/// </summary>
/// <param name="value">the <see cref="List"/> to convert to a <typeparamref name="TCollection"/></param>
/// <param name="parent">the object that will own the resulting <typeparamref name="TCollection"/></param>
/// <returns>a new <typeparamref name="TCollection"/> holding the deserialized content of <paramref name="value"/></returns>
/// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/>
public override TCollection FromValue(Value value, object parent)
{
if (!(value is List list)) throw new ArgumentException("Argument not a List", nameof(value));
var col = Create(list.Count, parent);
PopulateFromValue(col, list, parent);
return col;
}
/// <summary>
/// Serializes a <typeparamref name="TCollection"/> into a <see cref="List"/>.
/// </summary>
/// <param name="obj">the <typeparamref name="TCollection"/> to serialize</param>
/// <param name="parent">the object owning <paramref name="obj"/></param>
/// <returns>the <see cref="List"/> that <paramref name="obj"/> was serialized into</returns>
/// <seealso cref="ValueConverter{T}.ToValue(T, object)"/>
public override Value ToValue(TCollection obj, object parent)
=> Value.From(obj.Select(t => BaseConverter.ToValue(t, parent)));
}
/// <summary>
/// A <see cref="CollectionConverter{T, TCollection}"/> which default constructs a converter for use as the value converter.
/// </summary>
/// <typeparam name="T">the value type of the collection</typeparam>
/// <typeparam name="TCollection">the type of the colleciton</typeparam>
/// <typeparam name="TConverter">the type of the converter to use for <typeparamref name="T"/></typeparam>
/// <seealso cref="CollectionConverter{T, TCollection}"/>
public class CollectionConverter<T, TCollection, TConverter> : CollectionConverter<T, TCollection>
where TCollection : ICollection<T>
where TConverter : ValueConverter<T>, new()
{
/// <summary>
/// Creates a <see cref="CollectionConverter{T, TCollection}"/> using the default converter for the
/// element type. Equivalent to calling <see cref="CollectionConverter{T, TCollection}.CollectionConverter(ValueConverter{T})"/>
/// with a default-constructed <typeparamref name="TConverter"/>.
/// </summary>
/// <seealso cref="CollectionConverter{T, TCollection}.CollectionConverter(ValueConverter{T})"/>
public CollectionConverter() : base(new TConverter()) { }
}
public class ISetConverter<T> : CollectionConverter<T, ISet<T>>
{
public ISetConverter() : base() { }
public ISetConverter(ValueConverter<T> underlying) : base(underlying) { }
protected override ISet<T> Create(int size, object parent)
=> new HashSet<T>();
}
public class ListConverter<T> : CollectionConverter<T, List<T>>
{
public ListConverter() : base() { }
public ListConverter(ValueConverter<T> underlying) : base(underlying) { }
protected override List<T> Create(int size, object parent)
=> new List<T>(size);
}
public class IListConverter<T> : CollectionConverter<T, IList<T>>
{
public IListConverter() : base() { }
public IListConverter(ValueConverter<T> underlying) : base(underlying) { }
protected override IList<T> Create(int size, object parent)
=> new List<T>(size);
}
}

+ 19
- 0
IPA.Loader/Config/Stores/Converters.cs View File

@ -1,4 +1,5 @@
using IPA.Config.Data; using IPA.Config.Data;
using IPA.Config.Stores.Attributes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -171,6 +172,24 @@ namespace IPA.Config.Stores.Converters
=> obj == null ? null : baseConverter.ToValue(obj.Value, parent); => obj == null ? null : baseConverter.ToValue(obj.Value, parent);
} }
/// <summary>
/// A converter for a <see cref="Nullable{T}"/> that default-constructs a converter of type <typeparamref name="TConverter"/>
/// to use as the underlying converter. Use this in the <see cref="UseConverterAttribute"/>.
/// </summary>
/// <typeparam name="T">the underlying type of the <see cref="Nullable{T}"/></typeparam>
/// <typeparam name="TConverter">the type to use as an underlying converter</typeparam>
/// <seealso cref="NullableConverter{T}"/>
public class NullableConverter<T, TConverter> : NullableConverter<T>
where T : struct
where TConverter : ValueConverter<T>, new()
{
/// <summary>
/// Creates a converter with a new <typeparamref name="TConverter"/> as the underlying converter.
/// </summary>
/// <seealso cref="NullableConverter{T}.NullableConverter(ValueConverter{T})"/>
public NullableConverter() : base(new TConverter()) { }
}
internal class StringConverter : ValueConverter<string> internal class StringConverter : ValueConverter<string>
{ {
public override string FromValue(Value value, object parent) public override string FromValue(Value value, object parent)


+ 2
- 2
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -325,10 +325,10 @@ namespace IPA.Config.Stores
var converterAttr = attrs.Select(o => o as UseConverterAttribute).NonNull().FirstOrDefault(); var converterAttr = attrs.Select(o => o as UseConverterAttribute).NonNull().FirstOrDefault();
if (converterAttr != null) if (converterAttr != null)
{
{ // TODO: figure out how to represent chaining
member.Converter = converterAttr.ConverterType; member.Converter = converterAttr.ConverterType;
member.ConverterTarget = converterAttr.ConverterTargetType; member.ConverterTarget = converterAttr.ConverterTargetType;
member.IsGenericConverter = member.ConverterTarget != null;
member.IsGenericConverter = converterAttr.IsGenericConverter;
} }
return true; return true;


+ 3
- 0
IPA.Loader/Config/Stores/ValueConverter.cs View File

@ -19,6 +19,9 @@ namespace IPA.Config.Stores
/// be (ideally) the the top of the serialization tree, or some other generated object in that tree, rather than some arbitrary /// be (ideally) the the top of the serialization tree, or some other generated object in that tree, rather than some arbitrary
/// object in the middle that is not managed by the generatd config system. /// object in the middle that is not managed by the generatd config system.
/// </para> /// </para>
/// <para>
/// Converters do <i>not</i> need to perform null checks, as the serializer and deserializer will do that automatically.
/// </para>
/// </remarks> /// </remarks>
public interface IValueConverter public interface IValueConverter
{ {


+ 1
- 0
IPA.Loader/IPA.Loader.csproj View File

@ -97,6 +97,7 @@
<Compile Include="Config\Stores\Attributes.cs" /> <Compile Include="Config\Stores\Attributes.cs" />
<Compile Include="Config\Stores\Converters.cs" /> <Compile Include="Config\Stores\Converters.cs" />
<Compile Include="Config\Stores\CustomObjectConverter.cs" /> <Compile Include="Config\Stores\CustomObjectConverter.cs" />
<Compile Include="Config\Stores\CollectionConverter.cs" />
<Compile Include="Config\Stores\GeneratedStore.cs" /> <Compile Include="Config\Stores\GeneratedStore.cs" />
<Compile Include="Config\Stores\GeneratedStoreCollections.cs" /> <Compile Include="Config\Stores\GeneratedStoreCollections.cs" />
<Compile Include="Config\Stores\ValueConverter.cs" /> <Compile Include="Config\Stores\ValueConverter.cs" />


+ 3
- 0
IPA.Loader/Loader/DisabledConfig.cs View File

@ -1,6 +1,7 @@
using IPA.Config; using IPA.Config;
using IPA.Config.Stores; using IPA.Config.Stores;
using IPA.Config.Stores.Attributes; using IPA.Config.Stores.Attributes;
using IPA.Config.Stores.Converters;
using IPA.Utilities; using IPA.Utilities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -24,6 +25,8 @@ namespace IPA.Loader
public virtual bool Reset { get; set; } = true; public virtual bool Reset { get; set; } = true;
[NonNullable]
[UseConverter(typeof(CollectionConverter<string, HashSet<string>>))]
public virtual HashSet<string> DisabledModIds { get; set; } = new HashSet<string>(); public virtual HashSet<string> DisabledModIds { get; set; } = new HashSet<string>();
protected virtual void OnReload() protected virtual void OnReload()


Loading…
Cancel
Save