From 17d7b767f20c177da4ef32cfe7867116729910e4 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 2 Nov 2017 09:07:37 +0000 Subject: [PATCH 01/21] Added ArgsList + tests --- Serialization/ArgsList.Serialization.cs | 137 +++++++++ Serialization/ArgsList.Serialization.cs.meta | 3 + Serialization/ArgsList.cs | 133 ++++++++ Serialization/ArgsList.cs.meta | 3 + Serialization/Editor.meta | 3 + Serialization/Editor/ArgsListTests.cs | 307 +++++++++++++++++++ Serialization/Editor/ArgsListTests.cs.meta | 3 + 7 files changed, 589 insertions(+) create mode 100644 Serialization/ArgsList.Serialization.cs create mode 100644 Serialization/ArgsList.Serialization.cs.meta create mode 100644 Serialization/ArgsList.cs create mode 100644 Serialization/ArgsList.cs.meta create mode 100644 Serialization/Editor.meta create mode 100644 Serialization/Editor/ArgsListTests.cs create mode 100644 Serialization/Editor/ArgsListTests.cs.meta diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs new file mode 100644 index 0000000..eef84ca --- /dev/null +++ b/Serialization/ArgsList.Serialization.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using UnityEngine; + +namespace Dubit.DUCK.Serialization +{ + public partial class ArgsList + { + [SerializeField] + private string[] typeOrder; + + [SerializeField] + private string[] stringValues; + + [SerializeField] + private int[] intValues; + + [SerializeField] + private float[] floatValues; + + [SerializeField] + private bool[] boolValues; + + + public void OnBeforeSerialize() + { + var argLists = new Dictionary>(); + var typeOrderList = new List(); + + Func> lazyGetList = (t) => + { + if (argLists.ContainsKey(t)) + { + return argLists[t]; + } + return argLists[t] = new List(); + }; + + for (int i = 0; i < argTypes.Count; i++) + { + var argType = argTypes[i]; + var list = lazyGetList(argType.Name); + list.Add(args[i]); + typeOrderList.Add(argType.Name); + } + + typeOrder = typeOrderList.ToArray(); + + foreach (var supportedType in supportedTypes) + { + supportedType.Value.SetList(this, lazyGetList(supportedType.Key)); + } + } + + public void OnAfterDeserialize() + { + var serializedLists = new Dictionary>(); + args = new List(); + var localArgTypes = new List(); + + // Convert all serialized arrays to lists. + foreach (var supportedType in supportedTypes.Values) + { + var list = supportedType.GetList(this); + serializedLists.Add(supportedType.Type.Name, list); + } + + for (int i = 0; i < typeOrder.Length; i++) + { + var typeName = typeOrder[i]; + if (!supportedTypes.ContainsKey(typeName)) + { + // TODO: How to handle this error (on deserialize we find a type that is not supported) + // for now let's throw, but we may want to handle it more elegantly + throw new Exception("ArgsList cannot deserialize item of type: " + typeName + ", because it's not supported"); + } + + var supportedType = supportedTypes[typeName]; + localArgTypes.Add(supportedType.Type); + + var list = serializedLists[typeName]; + if (list.Count > 0) + { + var arg = list[0]; + list.RemoveAt(0); + args.Add(arg); + } + else + { + // if there was no arg found, we must create default value for it + args.Add(Activator.CreateInstance(supportedType.Type)); + } + } + + argTypes = new ReadOnlyCollection(localArgTypes); + } + + private List GetList(string typeName) + { + return null; + } + + class SupportedType + { + public Type Type { get; private set; } + private readonly Func> getList; + private readonly Action> setList; + + private SupportedType(Type type, Func> getList, Action> setList) + { + Type = type; + this.getList = getList; + this.setList = setList; + } + + public List GetList(ArgsList instance) + { + return getList(instance); + } + + public void SetList(ArgsList instance, List list) + { + setList(instance, list); + } + + public static SupportedType Create(Func getList, Action setList) + { + return new SupportedType( + typeof(T), + i => getList(i).Cast().ToList(), + (i, v) => setList(i, v.Cast().ToArray())); + } + } + } +} \ No newline at end of file diff --git a/Serialization/ArgsList.Serialization.cs.meta b/Serialization/ArgsList.Serialization.cs.meta new file mode 100644 index 0000000..0601062 --- /dev/null +++ b/Serialization/ArgsList.Serialization.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6b65ff7adb674c528dc8e1e3085c9876 +timeCreated: 1509471141 \ No newline at end of file diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs new file mode 100644 index 0000000..49f6532 --- /dev/null +++ b/Serialization/ArgsList.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using UnityEngine; + +namespace Dubit.DUCK.Serialization +{ + [SerializeField] + public partial class ArgsList : ISerializationCallbackReceiver + { + private static readonly Dictionary supportedTypes; + private static readonly Type[] supportedTypesArray; + + static ArgsList() + { + var supportedTypesList = new SupportedType[] + { + SupportedType.Create(i => i.stringValues, (i,v) => i.stringValues = v), + SupportedType.Create(i => i.intValues, (i,v) => i.intValues = v), + SupportedType.Create(i => i.floatValues, (i,v) => i.floatValues = v), + SupportedType.Create(i => i.boolValues, (i,v) => i.boolValues = v), + // GameObject + // Transform + // Vector2 + // Vector3 + // Vector4 + }; + + supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray(); + supportedTypes = supportedTypesList.ToDictionary(t => t.Type.Name, t => t); + } + + public IList ArgTypes + { + get { return argTypes; } + } + + private ReadOnlyCollection argTypes; + private List args; + + public void SetTypes(IList types) + { + if (types == null) throw new ArgumentNullException("types"); + if (!types.Any()) throw new ArgumentException("types must be a list greater than 0"); + + var oldArgs = args; + args = new List(); + + foreach (var argType in types) + { + if (!ValidateArgType(argType)) + { + throw new ArgumentException("Cannot handle arguments of type: " + argType.Name); + } + + var defaultValue = argType.IsValueType ? Activator.CreateInstance(argType) : null; + args.Add(defaultValue); + } + + if (oldArgs != null && argTypes != null) + { + for (int i = 0; i < oldArgs.Count; i++) + { + if (i < types.Count) + { + if (types[i] == argTypes[i]) + { + args[i] = oldArgs[i]; + } + } + } + } + + argTypes = new ReadOnlyCollection(types); + } + + public object this[int index] + { + get + { + if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); + return args[index]; + } + + set + { + var argType = value.GetType(); + if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); + if (!ValidateArgTypeForIndex(argType, index)) + { + throw new ArgumentException(argType.Name + " is not the correct type for index " + index); + } + + args[index] = value; + } + } + + public void Set(int index, T arg) + { + var argType = typeof(T); + if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); + if (!ValidateArgTypeForIndex(argType, index)) + { + throw new ArgumentException(argType.Name + " is not the correct type for index " + index); + } + + args[index] = arg; + } + + public T Get(int index) + { + var argType = typeof(T); + if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); + if (!ValidateArgTypeForIndex(argType, index)) + { + throw new ArgumentException(argType.Name + " is not the correct type for index " + index); + } + + return (T) args[index]; + } + + private bool ValidateArgTypeForIndex(Type type, int index) + { + return argTypes.Count > index && argTypes[index] == type; + } + + private bool ValidateArgType(Type type) + { + return supportedTypesArray.Contains(type); + } + } +} \ No newline at end of file diff --git a/Serialization/ArgsList.cs.meta b/Serialization/ArgsList.cs.meta new file mode 100644 index 0000000..23b59ad --- /dev/null +++ b/Serialization/ArgsList.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7e34e9f2f9dd45ddbdfbbb4d0bf4c535 +timeCreated: 1509456384 \ No newline at end of file diff --git a/Serialization/Editor.meta b/Serialization/Editor.meta new file mode 100644 index 0000000..0c0dfb8 --- /dev/null +++ b/Serialization/Editor.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 467adc14f07f497aba6be6db15424588 +timeCreated: 1509458154 \ No newline at end of file diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs new file mode 100644 index 0000000..98dad9b --- /dev/null +++ b/Serialization/Editor/ArgsListTests.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; + +namespace Dubit.DUCK.Serialization.Editor +{ + [TestFixture] + public class ArgsListTests + { + [Test] + public void ExpectConstructorNotToThrow() + { + Assert.DoesNotThrow(() => { new ArgsList(); }); + } + + [Test] + public void ExpectSetTypesToThrowWithNull() + { + var argsList = new ArgsList(); + Assert.Throws(() => { argsList.SetTypes(null); }); + } + + [Test] + public void ExpectSetTypesToThrowWithEmptyList() + { + var argsList = new ArgsList(); + Assert.Throws(() => { argsList.SetTypes(new List()); }); + } + + [Test] + public void ExpectSetTypesToThrowWithInvalidTypes() + { + var argsList = new ArgsList(); + Assert.Throws(() => { argsList.SetTypes(new List {typeof(ArgsList)}); }); + } + + [Test] + public void ExpectSetTypesNotToThrowWithValidTypes() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + } + + [Test] + public void ExpectSetTypesToSetTheArgTypesProperty() + { + var argsList = new ArgsList(); + var argTypes = new List {typeof(string), typeof(string), typeof(int)}; + argsList.SetTypes(argTypes); + + Assert.AreEqual(argsList.ArgTypes.Count, argTypes.Count); + for (var i = 0; i < argTypes.Count; i++) + { + Assert.AreEqual(argTypes[i], argsList.ArgTypes[i]); + } + } + + [Test] + public void ExpectSetToThrowIfIndexIsOutOfBounds() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(int), typeof(int), typeof(int)}); + + Assert.Throws(() => { argsList.Set(5, 10); }); + Assert.Throws(() => { argsList.Set(-5, 10); }); + } + + [Test] + public void ExpectSetToThrowIfIndexIsForWrongType() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + Assert.Throws(() => { argsList.Set(0, 10); }); + } + + [Test] + public void ExpectGetToThrowIfIndexIsOutOfBounds() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + Assert.Throws(() => { argsList.Get(5); }); + Assert.Throws(() => { argsList.Get(-5); }); + } + + [Test] + public void ExpectGetToThrowIfIndexIsForWrongType() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + Assert.Throws(() => { argsList.Get(0); }); + } + + [Test] + public void ExpectGetToReturnValuePassedIntoSetAtSameIndex() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + var value = "foobar"; + argsList.Set(0, value); + var result = argsList.Get(0); + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectSetIndexerToThrowIfIndexIsOutOfBounds() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(int), typeof(int), typeof(int)}); + + Assert.Throws(() => { argsList[5] = 10; }); + Assert.Throws(() => { argsList[-5] = 10; }); + } + + [Test] + public void ExpectSetIndexerToThrowIfIndexIsForWrongType() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + Assert.Throws(() => { argsList[0] = 10; }); + } + + [Test] + public void ExpectGetIndexerToThrowIfIndexIsOutOfBounds() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + Assert.Throws(() => { Debug.Log(argsList[5]); }); + Assert.Throws(() => { Debug.Log(argsList[-5]); }); + } + + [Test] + public void ExpectGetIndexerToReturnValuePassedIntoSetAtSameIndex() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + var value = "foobar"; + argsList[0] = value; + var result = argsList[0]; + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectGetToReturnDefaultValueIfSetWasNotCalled() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(string), typeof(string), typeof(int)}); + + var value = default(int); + var result = argsList.Get(2); + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectStringSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(string)})); + + // Add some data, serialize and deserialize + var value = "foobar"; + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectIntSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(int)})); + + // Add some data, serialize and deserialize + var value = 42; + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectFloatSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(float)})); + + // Add some data, serialize and deserialize + var value = 9.75f; + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectBoolSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(bool)})); + + // Add some data, serialize and deserialize + var value = true; + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectSerializationOfMultipleTypesToBeSupported() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List + { + typeof(string), + typeof(string), + typeof(int), + typeof(bool), + typeof(float), + typeof(int), + }); + + // Add some data, serialize and deserialize + argsList.Set(0, "foo"); + argsList.Set(1, "bar"); + argsList.Set(2, 10); + argsList.Set(3, true); + argsList.Set(4, -4.2f); + argsList.Set(5, 7); + + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + + // Now test the value is what it should be + Assert.AreEqual(argsList.Get(0), resultArgsList.Get(0)); + Assert.AreEqual(argsList.Get(1), resultArgsList.Get(1)); + Assert.AreEqual(argsList.Get(2), resultArgsList.Get(2)); + Assert.AreEqual(argsList.Get(3), resultArgsList.Get(3)); + Assert.AreEqual(argsList.Get(4), resultArgsList.Get(4)); + Assert.AreEqual(argsList.Get(5), resultArgsList.Get(5)); + } + + [Test(Description = "If SetTypes() is called to change the arg order/types/amount, old data should be invalidated (and replaced with default for the types)")] + public void ExpectSetTypesToInvalidateIndices() + { + var value = 10; + + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(int)}); + argsList.Set(0, value); + argsList.SetTypes(new List {typeof(string)}); + + Assert.AreNotEqual(value, argsList[0]); + Assert.AreEqual(default(string), argsList[0]); + } + + [Test(Description = "If SetTypes() is called to change the arg order/types/amount, for any indices where the type has not changed, the data should be maintained")] + public void ExpectSetTypesToInvalidateIndicesButKeepDataIfTheTypesStillMatch() + { + var value = "foobar"; + + var argsList = new ArgsList(); + + // setup to take (int, string) + argsList.SetTypes(new List {typeof(int), typeof(string)}); + argsList.Set(0, 42); + argsList.Set(1, value); + + // now change to take (string, string) + argsList.SetTypes(new List {typeof(string), typeof(string)}); + + // the second string param should still be vlaid + Assert.AreEqual(value, argsList[1]); + } + } +} \ No newline at end of file diff --git a/Serialization/Editor/ArgsListTests.cs.meta b/Serialization/Editor/ArgsListTests.cs.meta new file mode 100644 index 0000000..67f2dac --- /dev/null +++ b/Serialization/Editor/ArgsListTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 029208ae82ed44e58463d7657d7d9c80 +timeCreated: 1509456890 \ No newline at end of file From f52c860d3f6f4ff734c7ba3d46a959ccb7d06835 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 2 Nov 2017 09:10:36 +0000 Subject: [PATCH 02/21] Added Serialization meta --- Serialization.meta | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Serialization.meta diff --git a/Serialization.meta b/Serialization.meta new file mode 100644 index 0000000..db9c559 --- /dev/null +++ b/Serialization.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b36c8839d075f2348a83ed5ab414973b +folderAsset: yes +timeCreated: 1509613664 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: From 4c613559c2651ec2298ec96d78100daafe1ac5c5 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 2 Nov 2017 16:04:09 +0000 Subject: [PATCH 03/21] ArgsList::OnBeforeSerialize jump out if argTypes are null --- Serialization/ArgsList.Serialization.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index eef84ca..3002288 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -26,6 +26,8 @@ public partial class ArgsList public void OnBeforeSerialize() { + if (argTypes == null) return; + var argLists = new Dictionary>(); var typeOrderList = new List(); From b51dcf12cd3d82a4e5710d548ea84502a4a72b21 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 2 Nov 2017 16:04:52 +0000 Subject: [PATCH 04/21] ArgsList: Ability to set null on indexer but only for reference types. [Tests included] --- Serialization/ArgsList.cs | 40 +++++++++++++++++---------- Serialization/Editor/ArgsListTests.cs | 18 ++++++++++++ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 49f6532..3ec22bb 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -6,7 +6,7 @@ namespace Dubit.DUCK.Serialization { - [SerializeField] + [Serializable] public partial class ArgsList : ISerializationCallbackReceiver { private static readonly Dictionary supportedTypes; @@ -16,21 +16,21 @@ static ArgsList() { var supportedTypesList = new SupportedType[] { - SupportedType.Create(i => i.stringValues, (i,v) => i.stringValues = v), - SupportedType.Create(i => i.intValues, (i,v) => i.intValues = v), - SupportedType.Create(i => i.floatValues, (i,v) => i.floatValues = v), - SupportedType.Create(i => i.boolValues, (i,v) => i.boolValues = v), + SupportedType.Create(i => i.stringValues, (i, v) => i.stringValues = v), + SupportedType.Create(i => i.intValues, (i, v) => i.intValues = v), + SupportedType.Create(i => i.floatValues, (i, v) => i.floatValues = v), + SupportedType.Create(i => i.boolValues, (i, v) => i.boolValues = v), // GameObject // Transform // Vector2 // Vector3 // Vector4 }; - + supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray(); supportedTypes = supportedTypesList.ToDictionary(t => t.Type.Name, t => t); } - + public IList ArgTypes { get { return argTypes; } @@ -71,13 +71,13 @@ public void SetTypes(IList types) } } } - + argTypes = new ReadOnlyCollection(types); } public object this[int index] { - get + get { if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); return args[index]; @@ -85,17 +85,29 @@ public object this[int index] set { - var argType = value.GetType(); if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); - if (!ValidateArgTypeForIndex(argType, index)) + + if (value != null) + { + var argType = value.GetType(); + if (!ValidateArgTypeForIndex(argType, index)) + { + throw new ArgumentException(argType.Name + " is not the correct type for index " + index); + } + } + else { - throw new ArgumentException(argType.Name + " is not the correct type for index " + index); + var argType = argTypes[index]; + if (argType.IsValueType) + { + throw new ArgumentException("Null cannot be set against a value type. (index = " + index + ", type = " + argType.Name); + } } - + args[index] = value; } } - + public void Set(int index, T arg) { var argType = typeof(T); diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index 98dad9b..0a05e85 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -124,6 +124,24 @@ public void ExpectSetIndexerToThrowIfIndexIsForWrongType() Assert.Throws(() => { argsList[0] = 10; }); } + [Test] + public void ExpectSetIndexerToThrowWhenNullIsUsedAgainstValueTypes() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List {typeof(int), typeof(int), typeof(int)}); + + Assert.Throws(() => { argsList[1] = null; }); + } + + [Test] + public void ExpectSetIndexerNotToThrowWhenNullIsUsedAgainstReferenceTypes() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List { typeof(string) }); + + Assert.DoesNotThrow(() => { argsList[0] = null; }); + } + [Test] public void ExpectGetIndexerToThrowIfIndexIsOutOfBounds() { From 762ceb8d5a31d9649362bb42415abad8d65bd7b2 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 2 Nov 2017 16:05:20 +0000 Subject: [PATCH 05/21] Remove Dubit from namespaces --- Serialization/ArgsList.Serialization.cs | 2 +- Serialization/ArgsList.cs | 2 +- Serialization/Editor/ArgsListTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 3002288..81e7257 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -4,7 +4,7 @@ using System.Linq; using UnityEngine; -namespace Dubit.DUCK.Serialization +namespace DUCK.Serialization { public partial class ArgsList { diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 3ec22bb..86a9227 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -4,7 +4,7 @@ using System.Linq; using UnityEngine; -namespace Dubit.DUCK.Serialization +namespace DUCK.Serialization { [Serializable] public partial class ArgsList : ISerializationCallbackReceiver diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index 0a05e85..b215b2f 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using UnityEngine; -namespace Dubit.DUCK.Serialization.Editor +namespace DUCK.Serialization.Editor { [TestFixture] public class ArgsListTests From 20a85fbbc0ae8c2a3533a8350efa7f46302ed5e2 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Fri, 3 Nov 2017 08:53:23 +0000 Subject: [PATCH 06/21] ArgsList: Added support for setting types in overload constructor --- Serialization/ArgsList.cs | 8 ++++++++ Serialization/Editor/ArgsListTests.cs | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 86a9227..23829e7 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -39,6 +39,14 @@ public IList ArgTypes private ReadOnlyCollection argTypes; private List args; + public ArgsList(params Type[] types) + { + if (types.Length > 0) + { + SetTypes(types); + } + } + public void SetTypes(IList types) { if (types == null) throw new ArgumentNullException("types"); diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index b215b2f..878d8b8 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -14,6 +14,18 @@ public void ExpectConstructorNotToThrow() Assert.DoesNotThrow(() => { new ArgsList(); }); } + [Test] + public void ExpectConstructorWithArgsNotToThrowWithValidTypes() + { + Assert.DoesNotThrow(() => { new ArgsList(typeof(string), typeof(int)); }); + } + + [Test] + public void ExpectConstructorWithArgsToThrowWithInvalidTypes() + { + Assert.Throws(() => { new ArgsList(typeof(ArgsList)); }); + } + [Test] public void ExpectSetTypesToThrowWithNull() { From ab5829a94249710ad370c5dd987f47e35fa284e7 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Fri, 3 Nov 2017 08:54:05 +0000 Subject: [PATCH 07/21] ArgsList: format code --- Serialization/ArgsList.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 23829e7..7895ea4 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -46,7 +46,7 @@ public ArgsList(params Type[] types) SetTypes(types); } } - + public void SetTypes(IList types) { if (types == null) throw new ArgumentNullException("types"); @@ -108,7 +108,8 @@ public object this[int index] var argType = argTypes[index]; if (argType.IsValueType) { - throw new ArgumentException("Null cannot be set against a value type. (index = " + index + ", type = " + argType.Name); + throw new ArgumentException("Null cannot be set against a value type. (index = " + index + ", type = " + + argType.Name); } } From 1629621ded2dd201d5c8a56179e0ee300d11ef47 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Mon, 6 Nov 2017 15:35:33 +0000 Subject: [PATCH 08/21] Added support for GameObject and Vectors --- Serialization/ArgsList.Serialization.cs | 11 +++++++++++ Serialization/ArgsList.cs | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 81e7257..cf81991 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -23,6 +23,17 @@ public partial class ArgsList [SerializeField] private bool[] boolValues; + [SerializeField] + private GameObject[] gameObjectArgs; + + [SerializeField] + private Vector2[] vector2Args; + + [SerializeField] + private Vector3[] vector3Args; + + [SerializeField] + private Vector4[] vector4Args; public void OnBeforeSerialize() { diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 7895ea4..b0459cf 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -20,11 +20,11 @@ static ArgsList() SupportedType.Create(i => i.intValues, (i, v) => i.intValues = v), SupportedType.Create(i => i.floatValues, (i, v) => i.floatValues = v), SupportedType.Create(i => i.boolValues, (i, v) => i.boolValues = v), - // GameObject + SupportedType.Create(i => i.gameObjectArgs, (i, v) => i.gameObjectArgs = v), + SupportedType.Create(i => i.vector2Args, (i, v) => i.vector2Args = v), + SupportedType.Create(i => i.vector3Args, (i, v) => i.vector3Args = v), + SupportedType.Create(i => i.vector4Args, (i, v) => i.vector4Args = v), // Transform - // Vector2 - // Vector3 - // Vector4 }; supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray(); From 4163978e16727eb4292f0f2f407d149694090118 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Mon, 6 Nov 2017 15:36:57 +0000 Subject: [PATCH 09/21] ArgsListEditor --- Serialization/Editor/ArgsListEditor.cs | 22 +++++++++++++++++++++ Serialization/Editor/ArgsListEditor.cs.meta | 12 +++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Serialization/Editor/ArgsListEditor.cs create mode 100644 Serialization/Editor/ArgsListEditor.cs.meta diff --git a/Serialization/Editor/ArgsListEditor.cs b/Serialization/Editor/ArgsListEditor.cs new file mode 100644 index 0000000..650ae84 --- /dev/null +++ b/Serialization/Editor/ArgsListEditor.cs @@ -0,0 +1,22 @@ +using DUCK.Utils.Editor.EditorGUIHelpers; + +namespace DUCK.Serialization.Editor +{ + /// + /// Rather than extending editor and actually being an editor, + /// this is just a static function that allows you to draw it from another custom editor + /// + public static class ArgsListEditor + { + public static void Draw(ArgsList argsList) + { + for (var i = 0; i < argsList.ArgTypes.Count; i++) + { + var argType = argsList.ArgTypes[i]; + var argValue = argsList[i]; + var newArg = EditorGUILayoutHelpers.FieldByType(argValue, argType); + argsList[i] = newArg; + } + } + } +} \ No newline at end of file diff --git a/Serialization/Editor/ArgsListEditor.cs.meta b/Serialization/Editor/ArgsListEditor.cs.meta new file mode 100644 index 0000000..57a0083 --- /dev/null +++ b/Serialization/Editor/ArgsListEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3a579a95c32b459fa6c14af35979a06e +timeCreated: 1509959744 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 93c53c0901d416103dfd47497150bd5057202038 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Mon, 6 Nov 2017 15:46:29 +0000 Subject: [PATCH 10/21] ArgsList: added test for GameObject + Vectors --- Serialization/Editor/ArgsListTests.cs | 83 +++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index 878d8b8..c5644e1 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -267,6 +267,89 @@ public void ExpectBoolSerializationToBeSupported() Assert.AreEqual(value, result); } + [Test] + public void ExpectVector2SerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(Vector2)})); + + // Add some data, serialize and deserialize + var value = new Vector2(13.2f, -39.33f); + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectVector3SerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(Vector3)})); + + // Add some data, serialize and deserialize + var value = new Vector3(13.2f, -39.33f, 12.7f); + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectVector4SerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(Vector4)})); + + // Add some data, serialize and deserialize + var value = new Vector4(13.2f, -39.33f, 12.7f, 55.1f); + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + + [Test] + public void ExpectGameObjectSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(GameObject)})); + + // Add some data, serialize and deserialize + var value = new GameObject(); + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + + // Cleanup the object + UnityEditor.Editor.DestroyImmediate(value); + } + [Test] public void ExpectSerializationOfMultipleTypesToBeSupported() { From f51d9de56007012b49393b0f34d2e6a3274a6bf4 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 7 Nov 2017 08:54:41 +0000 Subject: [PATCH 11/21] ArgsList: added support + tests for component serialization --- Serialization/ArgsList.Serialization.cs | 82 +++++++++++++++++-------- Serialization/ArgsList.cs | 16 ++++- Serialization/Editor/ArgsListTests.cs | 40 ++++++++++++ 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index cf81991..10bed42 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -8,6 +8,8 @@ namespace DUCK.Serialization { public partial class ArgsList { + private const string COMPONENT_PREFIX = "c:"; + [SerializeField] private string[] typeOrder; @@ -25,13 +27,13 @@ public partial class ArgsList [SerializeField] private GameObject[] gameObjectArgs; - + [SerializeField] private Vector2[] vector2Args; - + [SerializeField] private Vector3[] vector3Args; - + [SerializeField] private Vector4[] vector4Args; @@ -39,11 +41,15 @@ public void OnBeforeSerialize() { if (argTypes == null) return; - var argLists = new Dictionary>(); + var argLists = new Dictionary>(); var typeOrderList = new List(); - Func> lazyGetList = (t) => + Func> lazyGetList = (t) => { + if (t.IsSubclassOf(typeof(Component))) + { + t = typeof(GameObject); + } if (argLists.ContainsKey(t)) { return argLists[t]; @@ -54,16 +60,24 @@ public void OnBeforeSerialize() for (int i = 0; i < argTypes.Count; i++) { var argType = argTypes[i]; - var list = lazyGetList(argType.Name); - list.Add(args[i]); - typeOrderList.Add(argType.Name); + var list = lazyGetList(argType); + if (argType.IsSubclassOf(typeof(Component))) + { + list.Add(((Component) args[i]).gameObject); + typeOrderList.Add(COMPONENT_PREFIX + argType.Name); + } + else + { + list.Add(args[i]); + typeOrderList.Add(argType.Name); + } } typeOrder = typeOrderList.ToArray(); foreach (var supportedType in supportedTypes) { - supportedType.Value.SetList(this, lazyGetList(supportedType.Key)); + supportedType.Value.SetList(this, lazyGetList(supportedType.Value.Type)); } } @@ -80,41 +94,59 @@ public void OnAfterDeserialize() serializedLists.Add(supportedType.Type.Name, list); } - for (int i = 0; i < typeOrder.Length; i++) + for (var i = 0; i < typeOrder.Length; i++) { var typeName = typeOrder[i]; - if (!supportedTypes.ContainsKey(typeName)) + List list; + Type argType; + Func argConverter = o => o; + + if (typeName.StartsWith(COMPONENT_PREFIX)) { - // TODO: How to handle this error (on deserialize we find a type that is not supported) - // for now let's throw, but we may want to handle it more elegantly - throw new Exception("ArgsList cannot deserialize item of type: " + typeName + ", because it's not supported"); + // strip off prefix + typeName = typeName.Replace(COMPONENT_PREFIX, ""); + if (!componentTypes.ContainsKey(typeName)) + { + // TODO: How to handle this error (found a component type not in the assembly on deserialize) + // for now let's throw, but we may want to handle it more elegantly + throw new Exception("ArgsList cannot deserialize component item of type: " + typeName + ", it was not found in the assemblies"); + } + + argType = componentTypes[typeName]; + localArgTypes.Add(argType); + list = serializedLists[typeof(GameObject).Name]; + argConverter = o => ((GameObject)o).GetComponent(argType); + } + else + { + if (!supportedTypes.ContainsKey(typeName)) + { + // TODO: How to handle this error (on deserialize we find a type that is not supported) + // for now let's throw, but we may want to handle it more elegantly + throw new Exception("ArgsList cannot deserialize item of type: " + typeName + ", because it's not supported"); + } + + argType = supportedTypes[typeName].Type; + localArgTypes.Add(argType); + list = serializedLists[typeName]; } - var supportedType = supportedTypes[typeName]; - localArgTypes.Add(supportedType.Type); - - var list = serializedLists[typeName]; if (list.Count > 0) { var arg = list[0]; list.RemoveAt(0); - args.Add(arg); + args.Add(argConverter(arg)); } else { // if there was no arg found, we must create default value for it - args.Add(Activator.CreateInstance(supportedType.Type)); + args.Add(argType.IsValueType ? Activator.CreateInstance(argType) : null); } } argTypes = new ReadOnlyCollection(localArgTypes); } - private List GetList(string typeName) - { - return null; - } - class SupportedType { public Type Type { get; private set; } diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index b0459cf..b37efc8 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -11,6 +11,7 @@ public partial class ArgsList : ISerializationCallbackReceiver { private static readonly Dictionary supportedTypes; private static readonly Type[] supportedTypesArray; + private static readonly Dictionary componentTypes; static ArgsList() { @@ -24,11 +25,22 @@ static ArgsList() SupportedType.Create(i => i.vector2Args, (i, v) => i.vector2Args = v), SupportedType.Create(i => i.vector3Args, (i, v) => i.vector3Args = v), SupportedType.Create(i => i.vector4Args, (i, v) => i.vector4Args = v), - // Transform }; supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray(); supportedTypes = supportedTypesList.ToDictionary(t => t.Type.Name, t => t); + componentTypes = new Dictionary(); + + // Get every type that extends component + var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes() + .Where(t => t.IsSubclassOf(typeof(Component))) + .Concat(typeof(Component).Assembly.GetTypes() + .Where(t => t.IsSubclassOf(typeof(Component))) + ); + foreach (var type in types) + { + componentTypes.Add(type.Name, type); + } } public IList ArgTypes @@ -148,7 +160,7 @@ private bool ValidateArgTypeForIndex(Type type, int index) private bool ValidateArgType(Type type) { - return supportedTypesArray.Contains(type); + return supportedTypesArray.Contains(type) || type.IsSubclassOf(typeof(Component)); } } } \ No newline at end of file diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index c5644e1..15b5a92 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -350,6 +350,46 @@ public void ExpectGameObjectSerializationToBeSupported() UnityEditor.Editor.DestroyImmediate(value); } + [Test] + public void ExpectComponentSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List + { + typeof(Transform), + typeof(Camera), + typeof(SpriteRenderer), + typeof(GameObject), + })); + + // Add some data, serialize and deserialize + var gameObjectA = new GameObject(); + var gameObjectB = new GameObject(); + var camera = gameObjectA.AddComponent(); + var spriteRenderer = gameObjectB.AddComponent(); + + argsList.Set(0, gameObjectA.transform); + argsList.Set(1, camera); + argsList.Set(2, spriteRenderer); + argsList.Set(3, gameObjectB); + + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + + // Now test the value is what it should be + Assert.AreEqual(gameObjectA.transform, resultArgsList[0]); + Assert.AreEqual(camera, resultArgsList[1]); + Assert.AreEqual(spriteRenderer, resultArgsList[2]); + Assert.AreEqual(gameObjectB, resultArgsList[3]); + + // Cleanup the object + UnityEditor.Editor.DestroyImmediate(gameObjectA); + UnityEditor.Editor.DestroyImmediate(gameObjectB); + } + [Test] public void ExpectSerializationOfMultipleTypesToBeSupported() { From 88a8c28db050dedb5ca775f995906ed428d397e4 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 7 Nov 2017 09:16:57 +0000 Subject: [PATCH 12/21] Added null check for components --- Serialization/ArgsList.Serialization.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 10bed42..7c1a0b9 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -63,7 +63,9 @@ public void OnBeforeSerialize() var list = lazyGetList(argType); if (argType.IsSubclassOf(typeof(Component))) { - list.Add(((Component) args[i]).gameObject); + // TODO: This null checking is needed, and needs test coverage + var arg = args[i] != null ? ((Component) args[i]).gameObject : null; + list.Add(arg); typeOrderList.Add(COMPONENT_PREFIX + argType.Name); } else From 49a8b762d77cb7d4e6364b3252e534731c79c3a0 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 7 Nov 2017 09:17:14 +0000 Subject: [PATCH 13/21] Added the ability to specify arg names when drawing args list --- Serialization/Editor/ArgsListEditor.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Serialization/Editor/ArgsListEditor.cs b/Serialization/Editor/ArgsListEditor.cs index 650ae84..de350da 100644 --- a/Serialization/Editor/ArgsListEditor.cs +++ b/Serialization/Editor/ArgsListEditor.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using DUCK.Utils.Editor.EditorGUIHelpers; namespace DUCK.Serialization.Editor @@ -8,13 +9,14 @@ namespace DUCK.Serialization.Editor /// public static class ArgsListEditor { - public static void Draw(ArgsList argsList) + public static void Draw(ArgsList argsList, IList argNames = null) { for (var i = 0; i < argsList.ArgTypes.Count; i++) { var argType = argsList.ArgTypes[i]; var argValue = argsList[i]; - var newArg = EditorGUILayoutHelpers.FieldByType(argValue, argType); + var label = argNames != null && argNames.Count > i ? argNames[i] : argType.Name; + var newArg = EditorGUILayoutHelpers.FieldByType(label, argValue, argType); argsList[i] = newArg; } } From 2383f665abcd5328451f8401253c3b22f44dbaa5 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 7 Nov 2017 13:20:03 +0000 Subject: [PATCH 14/21] ArgsList: Added tests + support for color args --- Serialization/ArgsList.Serialization.cs | 3 ++ Serialization/ArgsList.cs | 1 + Serialization/Editor/ArgsListTests.cs | 50 ++++++++++++++++++------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 7c1a0b9..4f011dd 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -37,6 +37,9 @@ public partial class ArgsList [SerializeField] private Vector4[] vector4Args; + [SerializeField] + private Color[] colorArgs; + public void OnBeforeSerialize() { if (argTypes == null) return; diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index b37efc8..b3cb51f 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -25,6 +25,7 @@ static ArgsList() SupportedType.Create(i => i.vector2Args, (i, v) => i.vector2Args = v), SupportedType.Create(i => i.vector3Args, (i, v) => i.vector3Args = v), SupportedType.Create(i => i.vector4Args, (i, v) => i.vector4Args = v), + SupportedType.Create(i => i.colorArgs, (i, v) => i.colorArgs = v), }; supportedTypesArray = supportedTypesList.Select(t => t.Type).ToArray(); diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index 15b5a92..dc63e7e 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -25,7 +25,7 @@ public void ExpectConstructorWithArgsToThrowWithInvalidTypes() { Assert.Throws(() => { new ArgsList(typeof(ArgsList)); }); } - + [Test] public void ExpectSetTypesToThrowWithNull() { @@ -144,12 +144,12 @@ public void ExpectSetIndexerToThrowWhenNullIsUsedAgainstValueTypes() Assert.Throws(() => { argsList[1] = null; }); } - + [Test] public void ExpectSetIndexerNotToThrowWhenNullIsUsedAgainstReferenceTypes() { var argsList = new ArgsList(); - argsList.SetTypes(new List { typeof(string) }); + argsList.SetTypes(new List {typeof(string)}); Assert.DoesNotThrow(() => { argsList[0] = null; }); } @@ -286,7 +286,7 @@ public void ExpectVector2SerializationToBeSupported() // Now test the value is what it should be Assert.AreEqual(value, result); } - + [Test] public void ExpectVector3SerializationToBeSupported() { @@ -306,7 +306,7 @@ public void ExpectVector3SerializationToBeSupported() // Now test the value is what it should be Assert.AreEqual(value, result); } - + [Test] public void ExpectVector4SerializationToBeSupported() { @@ -327,6 +327,26 @@ public void ExpectVector4SerializationToBeSupported() Assert.AreEqual(value, result); } + [Test] + public void ExpectColorSerializationToBeSupported() + { + var argsList = new ArgsList(); + + // test that it doesn't throw when specifying the type + + Assert.DoesNotThrow(() => argsList.SetTypes(new List {typeof(Color)})); + + // Add some data, serialize and deserialize + var value = new Color(1f, 0.5f, 0f, 0f); + argsList.Set(0, value); + var json = JsonUtility.ToJson(argsList); + var resultArgsList = JsonUtility.FromJson(json); + var result = resultArgsList.Get(0); + + // Now test the value is what it should be + Assert.AreEqual(value, result); + } + [Test] public void ExpectGameObjectSerializationToBeSupported() { @@ -375,7 +395,7 @@ public void ExpectComponentSerializationToBeSupported() argsList.Set(1, camera); argsList.Set(2, spriteRenderer); argsList.Set(3, gameObjectB); - + var json = JsonUtility.ToJson(argsList); var resultArgsList = JsonUtility.FromJson(json); @@ -424,35 +444,37 @@ public void ExpectSerializationOfMultipleTypesToBeSupported() Assert.AreEqual(argsList.Get(5), resultArgsList.Get(5)); } - [Test(Description = "If SetTypes() is called to change the arg order/types/amount, old data should be invalidated (and replaced with default for the types)")] + [Test(Description = + "If SetTypes() is called to change the arg order/types/amount, old data should be invalidated (and replaced with default for the types)")] public void ExpectSetTypesToInvalidateIndices() { var value = 10; - + var argsList = new ArgsList(); argsList.SetTypes(new List {typeof(int)}); argsList.Set(0, value); argsList.SetTypes(new List {typeof(string)}); - + Assert.AreNotEqual(value, argsList[0]); Assert.AreEqual(default(string), argsList[0]); } - [Test(Description = "If SetTypes() is called to change the arg order/types/amount, for any indices where the type has not changed, the data should be maintained")] + [Test(Description = + "If SetTypes() is called to change the arg order/types/amount, for any indices where the type has not changed, the data should be maintained")] public void ExpectSetTypesToInvalidateIndicesButKeepDataIfTheTypesStillMatch() { var value = "foobar"; - + var argsList = new ArgsList(); - + // setup to take (int, string) argsList.SetTypes(new List {typeof(int), typeof(string)}); argsList.Set(0, 42); argsList.Set(1, value); - + // now change to take (string, string) argsList.SetTypes(new List {typeof(string), typeof(string)}); - + // the second string param should still be vlaid Assert.AreEqual(value, argsList[1]); } From d15360df30008b1475ea1ed9a8dfc0fabea5d1fa Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 7 Nov 2017 13:20:58 +0000 Subject: [PATCH 15/21] ArgsList: Added property to retrieve all args as a list --- Serialization/ArgsList.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index b3cb51f..268c62d 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -36,8 +36,9 @@ static ArgsList() var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes() .Where(t => t.IsSubclassOf(typeof(Component))) .Concat(typeof(Component).Assembly.GetTypes() - .Where(t => t.IsSubclassOf(typeof(Component))) - ); + .Where(t => t.IsSubclassOf(typeof(Component))) + ); + foreach (var type in types) { componentTypes.Add(type.Name, type); @@ -49,6 +50,11 @@ public IList ArgTypes get { return argTypes; } } + public IList AllArgs + { + get { return args; } + } + private ReadOnlyCollection argTypes; private List args; From 07306ac902e7a0e6cb1c2cefcaf69384e683c688 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Wed, 8 Nov 2017 09:20:17 +0000 Subject: [PATCH 16/21] Allows ability to specify custom draw function for args at specific indices --- Serialization/Editor/ArgsListEditor.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Serialization/Editor/ArgsListEditor.cs b/Serialization/Editor/ArgsListEditor.cs index de350da..1ffe0f4 100644 --- a/Serialization/Editor/ArgsListEditor.cs +++ b/Serialization/Editor/ArgsListEditor.cs @@ -9,15 +9,24 @@ namespace DUCK.Serialization.Editor /// public static class ArgsListEditor { - public static void Draw(ArgsList argsList, IList argNames = null) + public delegate object CustomArgDrawFunc(string label, object currentValue); + + public static void Draw(ArgsList argsList, IList argNames = null, Dictionary customDrawFunctions = null) { for (var i = 0; i < argsList.ArgTypes.Count; i++) { var argType = argsList.ArgTypes[i]; var argValue = argsList[i]; var label = argNames != null && argNames.Count > i ? argNames[i] : argType.Name; - var newArg = EditorGUILayoutHelpers.FieldByType(label, argValue, argType); - argsList[i] = newArg; + if (customDrawFunctions != null && customDrawFunctions.ContainsKey(i)) + { + argValue = customDrawFunctions[i](label, argValue); + } + else + { + argValue = EditorGUILayoutHelpers.FieldByType(label, argValue, argType); + } + argsList[i] = argValue; } } } From 3008e9523861442340356b9fa523a14d8ff39193 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 9 Nov 2017 11:26:43 +0000 Subject: [PATCH 17/21] ArgsList: Changed the way we retrieve components --- Serialization/ArgsList.Serialization.cs | 4 +--- Serialization/ArgsList.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 4f011dd..df15db5 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -104,7 +104,6 @@ public void OnAfterDeserialize() var typeName = typeOrder[i]; List list; Type argType; - Func argConverter = o => o; if (typeName.StartsWith(COMPONENT_PREFIX)) { @@ -120,7 +119,6 @@ public void OnAfterDeserialize() argType = componentTypes[typeName]; localArgTypes.Add(argType); list = serializedLists[typeof(GameObject).Name]; - argConverter = o => ((GameObject)o).GetComponent(argType); } else { @@ -140,7 +138,7 @@ public void OnAfterDeserialize() { var arg = list[0]; list.RemoveAt(0); - args.Add(argConverter(arg)); + args.Add(arg); } else { diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 268c62d..f37baaa 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -107,6 +107,10 @@ public object this[int index] get { if (index >= argTypes.Count || index < 0) throw new ArgumentOutOfRangeException("index"); + if (argTypes[index].IsSubclassOf(typeof(Component)) && args[index] != null) + { + return ((GameObject) args[index]).GetComponent(argTypes[index]); + } return args[index]; } @@ -157,6 +161,11 @@ public T Get(int index) throw new ArgumentException(argType.Name + " is not the correct type for index " + index); } + if (argType.IsSubclassOf(typeof(Component)) && args[index] != null) + { + return ((GameObject) args[index]).GetComponent(); + } + return (T) args[index]; } From 27b7127f7fb91009ea323f8a42955bde58cd83c6 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Thu, 9 Nov 2017 13:13:55 +0000 Subject: [PATCH 18/21] ArgsList: Changed the way we retrieve components --- Serialization/ArgsList.Serialization.cs | 3 +-- Serialization/ArgsList.cs | 31 ++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index df15db5..993f22e 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -66,8 +66,7 @@ public void OnBeforeSerialize() var list = lazyGetList(argType); if (argType.IsSubclassOf(typeof(Component))) { - // TODO: This null checking is needed, and needs test coverage - var arg = args[i] != null ? ((Component) args[i]).gameObject : null; + var arg = args[i]; list.Add(arg); typeOrderList.Add(COMPONENT_PREFIX + argType.Name); } diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index f37baaa..a2dd19c 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -50,9 +50,20 @@ public IList ArgTypes get { return argTypes; } } + // TODO: test coverage for this prop public IList AllArgs { - get { return args; } + get + { + return args.Select((a,i) => + { + if (a != null && argTypes[i].IsSubclassOf(typeof(Component))) + { + return ((GameObject) a).GetComponent(argTypes[i]); + } + return a; + }).ToList(); + } } private ReadOnlyCollection argTypes; @@ -136,7 +147,14 @@ public object this[int index] } } - args[index] = value; + if (value != null && value.GetType().IsSubclassOf(typeof(Component))) + { + args[index] = ((Component)value).gameObject; + } + else + { + args[index] = value; + } } } @@ -149,7 +167,14 @@ public void Set(int index, T arg) throw new ArgumentException(argType.Name + " is not the correct type for index " + index); } - args[index] = arg; + if (argType.IsSubclassOf(typeof(Component))) + { + args[index] = ((Component)(object)arg).gameObject; + } + else + { + args[index] = arg; + } } public T Get(int index) From a1512e83e075d39ba4b25f8771113a945a14de52 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 14 Nov 2017 09:12:11 +0000 Subject: [PATCH 19/21] ArgsList: Added test coverage for AllArgs --- Serialization/ArgsList.cs | 1 - Serialization/Editor/ArgsListTests.cs | 46 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index a2dd19c..045c942 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -50,7 +50,6 @@ public IList ArgTypes get { return argTypes; } } - // TODO: test coverage for this prop public IList AllArgs { get diff --git a/Serialization/Editor/ArgsListTests.cs b/Serialization/Editor/ArgsListTests.cs index dc63e7e..2aee6d2 100644 --- a/Serialization/Editor/ArgsListTests.cs +++ b/Serialization/Editor/ArgsListTests.cs @@ -478,5 +478,51 @@ public void ExpectSetTypesToInvalidateIndicesButKeepDataIfTheTypesStillMatch() // the second string param should still be vlaid Assert.AreEqual(value, argsList[1]); } + + [Test] + public void ExpectAllArgsToReturnAllArgs() + { + var argsList = new ArgsList(); + argsList.SetTypes(new List + { + typeof(string), + typeof(int), + typeof(bool), + typeof(float), + typeof(GameObject), + typeof(Camera), + typeof(SpriteRenderer), + }); + + // Add some data, serialize and deserialize + var gameObjectA = new GameObject(); + var gameObjectB = new GameObject(); + var camera = gameObjectA.AddComponent(); + var spriteRenderer = gameObjectB.AddComponent(); + + // Add some data, serialize and deserialize + argsList.Set(0, "foo"); + argsList.Set(1, 10); + argsList.Set(2, true); + argsList.Set(3, -4.2f); + argsList.Set(4, gameObjectA); + argsList.Set(5, camera); + argsList.Set(6, spriteRenderer); + + var allArgs = argsList.AllArgs; + + // Now test the value is what it should be + Assert.AreEqual(argsList.Get(0), allArgs[0]); + Assert.AreEqual(argsList.Get(1), allArgs[1]); + Assert.AreEqual(argsList.Get(2), allArgs[2]); + Assert.AreEqual(argsList.Get(3), allArgs[3]); + Assert.AreEqual(argsList.Get(4), allArgs[4]); + Assert.AreEqual(argsList.Get(5), allArgs[5]); + Assert.AreEqual(argsList.Get(6), allArgs[6]); + + // Cleanup the object + UnityEditor.Editor.DestroyImmediate(gameObjectA); + UnityEditor.Editor.DestroyImmediate(gameObjectB); + } } } \ No newline at end of file From 8af16bca24122ce51dbe85897d6a1a99ccaa186e Mon Sep 17 00:00:00 2001 From: kkjamie Date: Tue, 14 Nov 2017 09:15:32 +0000 Subject: [PATCH 20/21] Removed TODO and tweaked wording --- Serialization/ArgsList.Serialization.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Serialization/ArgsList.Serialization.cs b/Serialization/ArgsList.Serialization.cs index 993f22e..b409ad6 100644 --- a/Serialization/ArgsList.Serialization.cs +++ b/Serialization/ArgsList.Serialization.cs @@ -110,7 +110,7 @@ public void OnAfterDeserialize() typeName = typeName.Replace(COMPONENT_PREFIX, ""); if (!componentTypes.ContainsKey(typeName)) { - // TODO: How to handle this error (found a component type not in the assembly on deserialize) + // TODO: How to handle this error (found a component type not in the assemblies on deserialize) // for now let's throw, but we may want to handle it more elegantly throw new Exception("ArgsList cannot deserialize component item of type: " + typeName + ", it was not found in the assemblies"); } @@ -123,8 +123,6 @@ public void OnAfterDeserialize() { if (!supportedTypes.ContainsKey(typeName)) { - // TODO: How to handle this error (on deserialize we find a type that is not supported) - // for now let's throw, but we may want to handle it more elegantly throw new Exception("ArgsList cannot deserialize item of type: " + typeName + ", because it's not supported"); } From ab8c3e093f632da9838c18224f7e53dceb88ec67 Mon Sep 17 00:00:00 2001 From: kkjamie Date: Fri, 17 Nov 2017 15:48:41 +0000 Subject: [PATCH 21/21] merged double if check --- Serialization/ArgsList.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Serialization/ArgsList.cs b/Serialization/ArgsList.cs index 045c942..2b8d960 100644 --- a/Serialization/ArgsList.cs +++ b/Serialization/ArgsList.cs @@ -99,12 +99,9 @@ public void SetTypes(IList types) { for (int i = 0; i < oldArgs.Count; i++) { - if (i < types.Count) + if (i < types.Count && types[i] == argTypes[i]) { - if (types[i] == argTypes[i]) - { - args[i] = oldArgs[i]; - } + args[i] = oldArgs[i]; } } }