Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 46 additions & 37 deletions src/SolidifyProject.Engine.Infrastructure/Models/PageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ public sealed class PageModel : TextContentModel

public dynamic Model { get; set; }

public override void Parse()
public PageModel()
{
Custom = new ExpandoObject();
Model = new ExpandoObject();

}

public override void Parse()
{
var lines = ContentRaw.Split(END_OF_LINE, StringSplitOptions.None);

var attributeLines = lines
Expand All @@ -62,12 +65,18 @@ public override void Parse()

var contentLines = lines.SkipWhile(x => !SEPARATOR.Equals(x)).Skip(1);
ParseContent(contentLines);

}

public void MapDataToModel(ExpandoObject data)
{
mapDataToPageModel(Model, data);
if (Model is ExpandoObject)
{
MapDataToPageModel(Model, data);
}
else
{
Model = getValueFromDataObject(Model, data);
}
}

private void ParseAttributeLine(string line)
Expand Down Expand Up @@ -110,6 +119,12 @@ private void ParseAttributeLine(string line)
return;
}

if (MODEL_ATTRIBUTE_PREFIX.Any(x => x.Equals(attributeName, StringComparison.OrdinalIgnoreCase)))
{
Model = attributeValue;
return;
}

if (CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR.Any(x => attributeName.Contains(x)))
{
var customAttributeNames = attributeName.Split(CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
Expand All @@ -134,57 +149,50 @@ private void ParseAttributeLine(string line)
return;
}

if (CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR.Any(x => attributeName.Contains(x)))
{
var modelAttributeNames = attributeName.Split(CUSTOM_ATTRIBUTE_PREFIX_SEPARATOR, StringSplitOptions.RemoveEmptyEntries);
if (modelAttributeNames.Length >= 2 && MODEL_ATTRIBUTE_PREFIX.Any(x => x.Equals(modelAttributeNames[0], StringComparison.InvariantCultureIgnoreCase)))
{
ParseCustomAttribute(Model, modelAttributeNames.Skip(1), attributeValue);
}
else
{
throw new ArgumentException($"Unknown name format of custom attribute \"{attributeName}\" at line \"{line}\"");
}

return;
}

throw new ArgumentException($"Unknown attribute \"{attributeName}\" at line \"{line}\"");
}

private void ParseCustomAttribute(ExpandoObject obj, IEnumerable<string> attributeNames, string attributeValue)
{
ICollection<KeyValuePair<string, object>> node = obj;
IDictionary<string, object> node = obj;
var currentSection = attributeNames.First();
object currentValue;


if (attributeNames.Count() > 1)
{
var subNode = new ExpandoObject();
currentValue = subNode;
ParseCustomAttribute(subNode, attributeNames.Skip(1), attributeValue);
ExpandoObject currentNode;
if (node.ContainsKey(currentSection))
{
currentNode = node[currentSection] as ExpandoObject;
}
else
{
currentNode = new ExpandoObject();
node.Add(new KeyValuePair<string, object>(currentSection, currentNode));
}

ParseCustomAttribute(currentNode, attributeNames.Skip(1), attributeValue);
}
else
{
currentValue = attributeValue;
node.Add(new KeyValuePair<string, object>(currentSection, attributeValue));
}

node.Add(new KeyValuePair<string, object>(currentSection, currentValue));
}

private void ParseContent(IEnumerable<string> lines)
{
Content = string.Join("\r\n", lines);
}

private void mapDataToPageModel(ExpandoObject model, ExpandoObject data)
private void MapDataToPageModel(ExpandoObject model, ExpandoObject data)
{
IDictionary<string, object> modelDict = model;
foreach (var keyValuePair in model)
Dictionary<string, object> objectToIterate = model.ToDictionary(k => k.Key, v => v.Value);
foreach (var keyValuePair in objectToIterate)
{
if (keyValuePair.Value is ExpandoObject expObject)
{
mapDataToPageModel(expObject, data);
MapDataToPageModel(expObject, data);
}
else
{
Expand All @@ -205,7 +213,7 @@ private object getValueFromDataObject(string path, ExpandoObject data)
{
if (attributeNames.Length == 1)
{
return null;
return data;
}

attributeNames = attributeNames.Skip(1).ToArray();
Expand All @@ -214,14 +222,15 @@ private object getValueFromDataObject(string path, ExpandoObject data)
object value = data;
foreach (var attribute in attributeNames)
{
if (value is IDictionary<string,object> dict)
{
value = dict[attribute];
}
else
IDictionary<string, object> dict = value as IDictionary<string, object>;

if (dict == null)
{
return null;
dict = value.GetType().GetProperties()
.ToDictionary(x => x.Name, x => x.GetValue(value, null));
}

value = dict[attribute];
}

return value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using NUnit.Framework;
using System.Collections.Generic;
using System.Dynamic;
using NUnit.Framework;
using SolidifyProject.Engine.Infrastructure.Models;

namespace SolidifyProject.Engine.Test.Unit.Infrastructure.Models
Expand Down Expand Up @@ -49,8 +51,8 @@ public void ParseCustomAttributesTest()
};

var actualModel = new PageModel { ContentRaw = @"
custom.image: logo.png
custom.description: this is description
Custom.image: logo.png
Custom.description: this is description
custom.root.SomeThing: bla-bla-bla
custom.l1.l2.l3.l4: hello
---
Expand All @@ -63,5 +65,167 @@ public void ParseCustomAttributesTest()
Assert.AreEqual(expectedModel.Custom.root.SomeThing, actualModel.Custom.root.SomeThing);
Assert.AreEqual(expectedModel.Custom.l1.l2.l3.l4, actualModel.Custom.l1.l2.l3.l4);
}

[Test]
public void ParseModelSimpleTest()
{
ICollection<KeyValuePair<string, object>> data = new ExpandoObject();
data.Add(new KeyValuePair<string, object>("goods", new List<string>
{
"apple",
"peach",
"mango"
}));
data.Add(new KeyValuePair<string, object>("banner", new
{
url = "http://mydomain.com/banner.png"
}));
data.Add(new KeyValuePair<string, object>("other", new { }));

var actualModel = new PageModel { ContentRaw = @"
Model: Data
---
"};

actualModel.Parse();
actualModel.MapDataToModel((ExpandoObject)data);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.goods.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.goods[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.goods[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.goods[2]);

Assert.AreEqual(((dynamic)data).banner.url, actualModel.Model.banner.url);
}

[Test]
public void ParseModelSingleItemTest()
{
ICollection<KeyValuePair<string, object>> data = new ExpandoObject();
data.Add(new KeyValuePair<string, object>("goods", new List<string>
{
"apple",
"peach",
"mango"
}));
data.Add(new KeyValuePair<string, object>("banner", new
{
url = "http://mydomain.com/banner.png"
}));
data.Add(new KeyValuePair<string, object>("other", new { }));

var actualModel = new PageModel { ContentRaw = @"
Model.goods: Data.goods
---
"};

actualModel.Parse();
actualModel.MapDataToModel((ExpandoObject)data);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.goods.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.goods[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.goods[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.goods[2]);
}

[Test]
public void ParseModelMultipleItemsTest()
{
ICollection<KeyValuePair<string, object>> data = new ExpandoObject();
data.Add(new KeyValuePair<string, object>("goods", new List<string>
{
"apple",
"peach",
"mango"
}));
data.Add(new KeyValuePair<string, object>("banner", new
{
url = "http://mydomain.com/banner.png"
}));
data.Add(new KeyValuePair<string, object>("other", new { }));

var actualModel = new PageModel { ContentRaw = @"
Model.goods: Data.goods
model.bannerUrl: Data.banner.url
---
"};

actualModel.Parse();
actualModel.MapDataToModel((ExpandoObject)data);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.goods.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.goods[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.goods[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.goods[2]);
Assert.AreEqual(((dynamic)data).banner.url, actualModel.Model.bannerUrl);
}

[Test]
public void ParseModelMultipleDuplicateItemsTest()
{
ICollection<KeyValuePair<string, object>> data = new ExpandoObject();
data.Add(new KeyValuePair<string, object>("goods", new List<string>
{
"apple",
"peach",
"mango"
}));
data.Add(new KeyValuePair<string, object>("banner", new
{
url = "http://mydomain.com/banner.png"
}));
data.Add(new KeyValuePair<string, object>("other", new { }));

var actualModel = new PageModel { ContentRaw = @"
Model.goods1: Data.goods
model.goods2: Data.goods
---
"};

actualModel.Parse();
actualModel.MapDataToModel((ExpandoObject)data);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.goods1.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.goods1[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.goods1[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.goods1[2]);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.goods2.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.goods2[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.goods2[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.goods2[2]);
}

[Test]
public void ParseModelComplexMultipleItemsTest()
{
ICollection<KeyValuePair<string, object>> data = new ExpandoObject();
data.Add(new KeyValuePair<string, object>("goods", new List<string>
{
"apple",
"peach",
"mango"
}));
data.Add(new KeyValuePair<string, object>("banner", new
{
url = "http://mydomain.com/banner.png"
}));
data.Add(new KeyValuePair<string, object>("other", new { }));

var actualModel = new PageModel { ContentRaw = @"
Model.category.goods: Data.goods
model.bannerUrl: Data.banner.url
---
"};

actualModel.Parse();
actualModel.MapDataToModel((ExpandoObject)data);

Assert.AreEqual(((dynamic)data).goods.Count, actualModel.Model.category.goods.Count);
Assert.AreEqual(((dynamic)data).goods[0], actualModel.Model.category.goods[0]);
Assert.AreEqual(((dynamic)data).goods[1], actualModel.Model.category.goods[1]);
Assert.AreEqual(((dynamic)data).goods[2], actualModel.Model.category.goods[2]);
Assert.AreEqual(((dynamic)data).banner.url, actualModel.Model.bannerUrl);
}
}
}