Rewrite yaml merger.
This commit is contained in:
@@ -79,6 +79,11 @@ namespace OpenRA
|
||||
{
|
||||
return "{{YamlNode: {0} @ {1}}}".F(Key, Location);
|
||||
}
|
||||
|
||||
public MiniYamlNode Clone()
|
||||
{
|
||||
return new MiniYamlNode(Key, Value.Clone());
|
||||
}
|
||||
}
|
||||
|
||||
public class MiniYaml
|
||||
@@ -89,6 +94,11 @@ namespace OpenRA
|
||||
public string Value;
|
||||
public List<MiniYamlNode> Nodes;
|
||||
|
||||
public MiniYaml Clone()
|
||||
{
|
||||
return new MiniYaml(Value, Nodes.Select(n => n.Clone()).ToList());
|
||||
}
|
||||
|
||||
public Dictionary<string, MiniYaml> ToDictionary()
|
||||
{
|
||||
return ToDictionary(MiniYamlIdentity);
|
||||
@@ -245,89 +255,117 @@ namespace OpenRA
|
||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries), fileName);
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> Merge(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
||||
public static List<MiniYamlNode> Merge(IEnumerable<List<MiniYamlNode>> sources)
|
||||
{
|
||||
return ApplyRemovals(MergePartial(a, b));
|
||||
if (!sources.Any())
|
||||
return new List<MiniYamlNode>();
|
||||
|
||||
var tree = sources.Where(s => s != null).Aggregate(MergePartial)
|
||||
.ToDictionary(n => n.Key, n => n.Value);
|
||||
|
||||
var resolved = new Dictionary<string, MiniYaml>();
|
||||
foreach (var kv in tree)
|
||||
{
|
||||
var inherited = new Dictionary<string, MiniYamlNode.SourceLocation>();
|
||||
inherited.Add(kv.Key, new MiniYamlNode.SourceLocation());
|
||||
|
||||
var children = ResolveInherits(kv.Key, kv.Value, tree, inherited);
|
||||
resolved.Add(kv.Key, new MiniYaml(kv.Value.Value, children));
|
||||
}
|
||||
|
||||
return resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList();
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> MergePartial(List<MiniYamlNode> a, List<MiniYamlNode> b)
|
||||
static void MergeIntoResolved(MiniYamlNode overrideNode, List<MiniYamlNode> existingNodes,
|
||||
Dictionary<string, MiniYaml> tree, Dictionary<string, MiniYamlNode.SourceLocation> inherited)
|
||||
{
|
||||
if (a.Count == 0)
|
||||
return b;
|
||||
var existingNode = existingNodes.FirstOrDefault(n => n.Key == overrideNode.Key);
|
||||
if (existingNode != null)
|
||||
{
|
||||
existingNode.Value = MiniYaml.MergePartial(existingNode.Value, overrideNode.Value);
|
||||
existingNode.Value.Nodes = ResolveInherits(existingNode.Key, existingNode.Value, tree, inherited);
|
||||
}
|
||||
else
|
||||
existingNodes.Add(overrideNode.Clone());
|
||||
}
|
||||
|
||||
if (b.Count == 0)
|
||||
return a;
|
||||
static List<MiniYamlNode> ResolveInherits(string key, MiniYaml node, Dictionary<string, MiniYaml> tree, Dictionary<string, MiniYamlNode.SourceLocation> inherited)
|
||||
{
|
||||
var resolved = new List<MiniYamlNode>();
|
||||
|
||||
// Inheritance is tracked from parent->child, but not from child->parentsiblings.
|
||||
inherited = new Dictionary<string, MiniYamlNode.SourceLocation>(inherited);
|
||||
|
||||
foreach (var n in node.Nodes)
|
||||
{
|
||||
if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
|
||||
{
|
||||
MiniYaml parent;
|
||||
if (!tree.TryGetValue(n.Value.Value, out parent))
|
||||
throw new YamlException(
|
||||
"{0}: Parent type `{1}` not found".F(n.Location, n.Value.Value));
|
||||
|
||||
if (inherited.ContainsKey(n.Value.Value))
|
||||
throw new YamlException("{0}: Parent type `{1}` was already inherited by this yaml tree at {2} (note: may be from a derived tree)"
|
||||
.F(n.Location, n.Value.Value, inherited[n.Value.Value]));
|
||||
|
||||
inherited.Add(n.Value.Value, n.Location);
|
||||
foreach (var r in ResolveInherits(n.Key, parent, tree, inherited))
|
||||
MergeIntoResolved(r, resolved, tree, inherited);
|
||||
}
|
||||
else if (n.Key.StartsWith("-"))
|
||||
{
|
||||
var removed = n.Key.Substring(1);
|
||||
if (resolved.RemoveAll(r => r.Key == removed) == 0)
|
||||
throw new YamlException("{0}: There are no elements with key `{1}` to remove".F(n.Location, removed));
|
||||
}
|
||||
else
|
||||
MergeIntoResolved(n, resolved, tree, inherited);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static MiniYaml MergePartial(MiniYaml existingNodes, MiniYaml overrideNodes)
|
||||
{
|
||||
if (existingNodes == null)
|
||||
return overrideNodes;
|
||||
|
||||
if (overrideNodes == null)
|
||||
return existingNodes;
|
||||
|
||||
return new MiniYaml(overrideNodes.Value ?? existingNodes.Value, MergePartial(existingNodes.Nodes, overrideNodes.Nodes));
|
||||
}
|
||||
|
||||
static List<MiniYamlNode> MergePartial(List<MiniYamlNode> existingNodes, List<MiniYamlNode> overrideNodes)
|
||||
{
|
||||
if (existingNodes.Count == 0)
|
||||
return overrideNodes;
|
||||
|
||||
if (overrideNodes.Count == 0)
|
||||
return existingNodes;
|
||||
|
||||
var ret = new List<MiniYamlNode>();
|
||||
|
||||
var dictA = a.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||
var dictB = b.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||
var allKeys = dictA.Keys.Union(dictB.Keys);
|
||||
var existingDict = existingNodes.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||
var overrideDict = overrideNodes.ToDictionaryWithConflictLog(x => x.Key, "MiniYaml.Merge", null, x => "{0} (at {1})".F(x.Key, x.Location));
|
||||
var allKeys = existingDict.Keys.Union(overrideDict.Keys);
|
||||
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
MiniYamlNode aa, bb;
|
||||
dictA.TryGetValue(key, out aa);
|
||||
dictB.TryGetValue(key, out bb);
|
||||
MiniYamlNode existingNode, overrideNode;
|
||||
existingDict.TryGetValue(key, out existingNode);
|
||||
overrideDict.TryGetValue(key, out overrideNode);
|
||||
|
||||
var loc = aa == null ? default(MiniYamlNode.SourceLocation) : aa.Location;
|
||||
var merged = (aa == null || bb == null) ? aa ?? bb : new MiniYamlNode(key, MergePartial(aa.Value, bb.Value), loc);
|
||||
var loc = overrideNode == null ? default(MiniYamlNode.SourceLocation) : overrideNode.Location;
|
||||
var merged = (existingNode == null || overrideNode == null) ? overrideNode ?? existingNode :
|
||||
new MiniYamlNode(key, MergePartial(existingNode.Value, overrideNode.Value), loc);
|
||||
ret.Add(merged);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> ApplyRemovals(List<MiniYamlNode> a)
|
||||
{
|
||||
var removeKeys = a.Select(x => x.Key)
|
||||
.Where(x => x.Length > 0 && x[0] == '-')
|
||||
.Select(k => k.Substring(1))
|
||||
.ToHashSet();
|
||||
|
||||
var ret = new List<MiniYamlNode>();
|
||||
foreach (var x in a)
|
||||
{
|
||||
if (x.Key[0] == '-')
|
||||
continue;
|
||||
|
||||
if (removeKeys.Contains(x.Key))
|
||||
removeKeys.Remove(x.Key);
|
||||
else
|
||||
{
|
||||
x.Value.Nodes = ApplyRemovals(x.Value.Nodes);
|
||||
ret.Add(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (removeKeys.Any())
|
||||
throw new YamlException("Bogus yaml removals: {0}".F(removeKeys.JoinWith(", ")));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static MiniYaml MergePartial(MiniYaml a, MiniYaml b)
|
||||
{
|
||||
if (a == null)
|
||||
return b;
|
||||
|
||||
if (b == null)
|
||||
return a;
|
||||
|
||||
return new MiniYaml(a.Value ?? b.Value, MergePartial(a.Nodes, b.Nodes));
|
||||
}
|
||||
|
||||
public static MiniYaml Merge(MiniYaml a, MiniYaml b)
|
||||
{
|
||||
if (a == null)
|
||||
return b;
|
||||
|
||||
if (b == null)
|
||||
return a;
|
||||
|
||||
return new MiniYaml(a.Value ?? b.Value, Merge(a.Nodes, b.Nodes));
|
||||
}
|
||||
|
||||
public IEnumerable<string> ToLines(string name)
|
||||
{
|
||||
yield return name + ": " + Value;
|
||||
|
||||
Reference in New Issue
Block a user