Skip to content

Commit

Permalink
Add LoadUtils, with the abstracted parenting code.
Browse files Browse the repository at this point in the history
  • Loading branch information
rdw committed Feb 25, 2017
1 parent 1330498 commit a9d9f67
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
91 changes: 91 additions & 0 deletions Assets/DarkConfig/LoadUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Collections.Generic;
using DarkConfig;
using System;

namespace DarkConfig {
public class LoadUtils {
public static void SetParentDefaults<K, V>(
ref Dictionary<K, V> container,
DocNode doc,
Func<V, K> getBasedOn,
string[] unparentableFieldNames = null) {
// clear existing values before the reify; because later we bake them
var fields = typeof(V).GetFields();
if(container != null) {
foreach(var kv in container) {
foreach(var field in fields) {
field.SetValue(kv.Value, GetDefault(field.FieldType));
}
}
}

Config.Reify(ref container, doc);

var parentRelationships = new Dictionary<V, V>();

// hook up parent references
foreach (var kv in container) {
var val = kv.Value;
var basedOn = getBasedOn(val);
if(basedOn == null) continue;
if (!container.ContainsKey(basedOn)) {
Config.Log(LogVerbosity.Error,
string.Format("In file {0}, {1} is based on {2}, which doesn't exist",
doc.SourceInformation, val, basedOn));
continue;
}

parentRelationships[val] = container[basedOn];
}

// set fields from the parents
foreach (var kv in container) {
var val = kv.Value;
foreach (var field in fields) {
if (field.IsSpecialName) continue;
if(unparentableFieldNames != null) {
bool shouldNotParentThisField = false;
for(int i = 0; i < unparentableFieldNames.Length; i++) {
if(field.Name == unparentableFieldNames[i]) {
shouldNotParentThisField = true;
break;
}
}
if(shouldNotParentThisField) continue;
}

var fieldValue = GetParentedFieldValue(field, val, parentRelationships, 0);
field.SetValue(val, fieldValue);
}
}
}

static object GetParentedFieldValue<V>(
System.Reflection.FieldInfo field,
V conf,
Dictionary<V, V> parentRelationships,
int recursionDepth) {

var fieldValue = field.GetValue(conf);
V parent;
if(!parentRelationships.TryGetValue(conf, out parent)) return fieldValue;
if(parent == null) return fieldValue;
if (recursionDepth > 100) {
Config.Log(LogVerbosity.Error,
string.Format("Might be a loop in the basedOn references at: {0}, parent {1}", conf, parent));
return fieldValue;
}
if (fieldValue == null) {
// need to get the default from the parent
return GetParentedFieldValue(field, parent, parentRelationships, recursionDepth + 1);
} else {
return fieldValue;
}
}

static object GetDefault(System.Type type) {
if(type.IsValueType) return System.Activator.CreateInstance(type);
return null;
}
}
}
12 changes: 12 additions & 0 deletions Assets/DarkConfig/LoadUtils.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

143 changes: 143 additions & 0 deletions Assets/Editor/Tests/LoadUtilsFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System.Collections.Generic;
using DarkConfig;
using NUnit.Framework;

[TestFixture]
class LoadUtilsFacts {
class TestClass {
public int? intKey = null;
public string stringKey = null;

public string basedOn = null;

public static string getBasedOn(TestClass c) {
return c.basedOn;
}
}

const string c_filename = "LoadUtilsFacts_TestFileName";

DocNode FromString(string s) {
return Config.LoadDocFromString(s, c_filename);
}

[Test]
public void LoadUtils_SetParentDefaults_LoadSimple() {
var doc = FromString(@"
basic:
intKey: 1
stringKey: 2
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn);
Assert.AreEqual(1, d.Count);
Assert.AreEqual(1, d["basic"].intKey.Value);
Assert.AreEqual("2", d["basic"].stringKey);
}

[Test]
public void LoadUtils_SetParentDefaults_LoadParented() {
var doc = FromString(@"
default:
intKey: 0
stringKey: a
basic:
basedOn: default
intKey: 1
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn);
Assert.AreEqual(2, d.Count);
Assert.AreEqual(1, d["basic"].intKey.Value);
Assert.AreEqual("a", d["basic"].stringKey);
}

[Test]
public void LoadUtils_SetParentDefaults_LoadParentedRecursive() {
var doc = FromString(@"
default:
intKey: 0
stringKey: a
basic:
basedOn: default
intKey: 1
deeper:
basedOn: basic
intKey: 2
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn);
Assert.AreEqual(3, d.Count);
Assert.AreEqual(2, d["deeper"].intKey.Value);
Assert.AreEqual("a", d["deeper"].stringKey);
}


[Test]
public void LoadUtils_SetParentDefaults_LoadNullableRecursive() {
var doc = FromString(@"
default:
intKey: 0
stringKey: a
basic:
basedOn: default
stringKey: basicString
deeper:
basedOn: basic
stringKey: deeperString
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn);
Assert.AreEqual(3, d.Count);
Assert.AreEqual(0, d["deeper"].intKey.Value);
Assert.AreEqual("deeperString", d["deeper"].stringKey);
}

[Test]
public void LoadUtils_SetParentDefaults_LoadParentedTwice() {
var doc = FromString(@"
default:
intKey: 0
stringKey: a
basic:
basedOn: default
intKey: 1
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn);
var doc2 = FromString(@"
default:
intKey: 1
stringKey: b
basic:
basedOn: default
");
LoadUtils.SetParentDefaults(ref d, doc2, TestClass.getBasedOn);
Assert.AreEqual(2, d.Count);
Assert.AreEqual(1, d["basic"].intKey.Value);
Assert.AreEqual("b", d["basic"].stringKey);
}


[Test]
public void LoadUtils_SetParentDefaults_IgnoreFields() {
var doc = FromString(@"
default:
intKey: 0
stringKey: a
basic:
basedOn: default
");

Dictionary<string, TestClass> d = null;
LoadUtils.SetParentDefaults(ref d, doc, TestClass.getBasedOn,
new string[] {"basedOn", "intKey"});
Assert.AreEqual(null, d["basic"].intKey);
Assert.AreEqual("a", d["basic"].stringKey);
}
}
12 changes: 12 additions & 0 deletions Assets/Editor/Tests/LoadUtilsFacts.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Sln/Unity/DarkConfigUnityDLL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
<Compile Include="..\..\Assets\DarkConfig\DocNodeExtensions.cs" />
<Compile Include="..\..\Assets\DarkConfig\ComposedDocNode.cs" />
<Compile Include="..\..\Assets\DarkConfig\MurmurHash3.cs" />
<Compile Include="..\..\Assets\DarkConfig\LoadUtils.cs" />
<Compile Include="..\..\Assets\DarkConfig\Attributes.cs" />
<Compile Include="..\..\Assets\DarkConfig\ConfigOptions.cs" />
<Compile Include="..\..\Assets\DarkConfig\ReflectionCache.cs" />
Expand Down

0 comments on commit a9d9f67

Please sign in to comment.