Mapping merge driver: continued (#2803)
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
199
Content.Tools/YamlTools.cs
Normal file
199
Content.Tools/YamlTools.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Tools
|
||||
{
|
||||
public static class YamlTools
|
||||
{
|
||||
public static YamlNode CopyYamlNodes(YamlNode other)
|
||||
{
|
||||
switch (other)
|
||||
{
|
||||
case YamlSequenceNode subSequence:
|
||||
YamlSequenceNode tmp1 = new YamlSequenceNode();
|
||||
MergeYamlSequences((YamlSequenceNode) tmp1, new YamlSequenceNode(), (YamlSequenceNode) other, "");
|
||||
return tmp1;
|
||||
case YamlMappingNode subMapping:
|
||||
YamlMappingNode tmp2 = new YamlMappingNode();
|
||||
MergeYamlMappings((YamlMappingNode) tmp2, new YamlMappingNode(), (YamlMappingNode) other, "", new string[] {});
|
||||
return tmp2;
|
||||
case YamlScalarNode subScalar:
|
||||
YamlScalarNode tmp3 = new YamlScalarNode();
|
||||
CopyYamlScalar(tmp3, subScalar);
|
||||
return tmp3;
|
||||
default:
|
||||
throw new ArgumentException($"Unrecognized YAML node type for copy: {other.GetType()}", nameof(other));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TriTypeMatch(YamlNode ours, YamlNode based, YamlNode other)
|
||||
{
|
||||
var refType = other.GetType();
|
||||
if (refType != based.GetType())
|
||||
return false;
|
||||
if (refType != ours.GetType())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void MergeYamlNodes(YamlNode ours, YamlNode based, YamlNode other, string path)
|
||||
{
|
||||
if (!TriTypeMatch(ours, based, other))
|
||||
throw new ArgumentException($"Node type mismatch at {path}");
|
||||
switch (other)
|
||||
{
|
||||
case YamlSequenceNode subSequence:
|
||||
MergeYamlSequences((YamlSequenceNode) ours, (YamlSequenceNode) based, (YamlSequenceNode) other, path);
|
||||
break;
|
||||
case YamlMappingNode subMapping:
|
||||
MergeYamlMappings((YamlMappingNode) ours, (YamlMappingNode) based, (YamlMappingNode) other, path, new string[] {});
|
||||
break;
|
||||
case YamlScalarNode subScalar:
|
||||
// Console.WriteLine(path + " - " + ours + " || " + based + " || " + other);
|
||||
var scalarA = (YamlScalarNode) ours;
|
||||
var scalarB = (YamlScalarNode) based;
|
||||
var scalarC = (YamlScalarNode) other;
|
||||
var aeb = (scalarA.Value == scalarB.Value);
|
||||
var cneb = (scalarC.Value != scalarB.Value);
|
||||
if (aeb || cneb)
|
||||
CopyYamlScalar(scalarA, scalarC);
|
||||
// Console.WriteLine(path + " . " + ours + " || " + based + " || " + other);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unrecognized YAML node type at {path}: {other.GetType()}", nameof(other));
|
||||
}
|
||||
}
|
||||
|
||||
public static void MergeYamlSequences(YamlSequenceNode ours, YamlSequenceNode based, YamlSequenceNode other, string path)
|
||||
{
|
||||
if ((ours.Children.Count == based.Children.Count) && (other.Children.Count == ours.Children.Count))
|
||||
{
|
||||
// this is terrible and doesn't do proper rearrange detection
|
||||
// but it looks as if vectors might be arrays
|
||||
// so rearrange detection might break more stuff...
|
||||
// nope, they aren't, but still good to have
|
||||
for (var i = 0; i < ours.Children.Count; i++)
|
||||
MergeYamlNodes(ours.Children[i], based.Children[i], other.Children[i], path + "/" + i);
|
||||
return;
|
||||
}
|
||||
// for now, just copy other -> ours
|
||||
// I am aware this is terrible
|
||||
ours.Children.Clear();
|
||||
foreach (var c in other.Children)
|
||||
ours.Add(CopyYamlNodes(c));
|
||||
}
|
||||
|
||||
public static void MergeYamlMappings(YamlMappingNode ours, YamlMappingNode based, YamlMappingNode other, string path, string[] ignoreThese)
|
||||
{
|
||||
// Deletions/modifications
|
||||
foreach (var kvp in based)
|
||||
{
|
||||
if (ignoreThese.Contains(kvp.Key.ToString()))
|
||||
continue;
|
||||
|
||||
var localPath = path + "/" + kvp.Key.ToString();
|
||||
var deletedByOurs = !ours.Children.ContainsKey(kvp.Key);
|
||||
var deletedByOther = !other.Children.ContainsKey(kvp.Key);
|
||||
if (deletedByOther && (!deletedByOurs))
|
||||
{
|
||||
// Delete
|
||||
ours.Children.Remove(kvp.Key);
|
||||
}
|
||||
else if (!(deletedByOurs || deletedByOther))
|
||||
{
|
||||
// Modify
|
||||
var a = ours[kvp.Key];
|
||||
var b = kvp.Value; // based[kvp.Key]
|
||||
var c = other[kvp.Key];
|
||||
if (!TriTypeMatch(a, b, c))
|
||||
{
|
||||
Console.WriteLine("Warning: Type mismatch (defaulting to value C) at " + localPath);
|
||||
ours.Children[kvp.Key] = CopyYamlNodes(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
MergeYamlNodes(a, b, c, localPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Additions
|
||||
foreach (var kvp in other)
|
||||
{
|
||||
if (ignoreThese.Contains(kvp.Key.ToString()))
|
||||
continue;
|
||||
|
||||
var localPath = path + "/" + kvp.Key.ToString();
|
||||
if (!based.Children.ContainsKey(kvp.Key))
|
||||
{
|
||||
if (ours.Children.ContainsKey(kvp.Key))
|
||||
{
|
||||
// Both sides added the same key. Try to merge.
|
||||
var a = ours[kvp.Key];
|
||||
var b = based[kvp.Key];
|
||||
var c = kvp.Value; // other[kvp.Key]
|
||||
if (!TriTypeMatch(a, b, c))
|
||||
{
|
||||
Console.WriteLine("Warning: Type mismatch (defaulting to value C) at " + localPath);
|
||||
ours.Children[kvp.Key] = CopyYamlNodes(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
MergeYamlNodes(a, b, c, localPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Well that was easy
|
||||
ours.Children[kvp.Key] = CopyYamlNodes(kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This is a heuristic ONLY! And is also not used at the moment because sequence matching isn't in place.
|
||||
// It could also be massively improved.
|
||||
public static float YamlNodesHeuristic(YamlNode a, YamlNode b)
|
||||
{
|
||||
if (a.GetType() != b.GetType())
|
||||
return 0.0f;
|
||||
switch (a)
|
||||
{
|
||||
case YamlSequenceNode x:
|
||||
return YamlSequencesHeuristic((YamlSequenceNode) a, (YamlSequenceNode) b);
|
||||
case YamlMappingNode y:
|
||||
return YamlMappingsHeuristic((YamlMappingNode) a, (YamlMappingNode) b);
|
||||
case YamlScalarNode z:
|
||||
return (((YamlScalarNode) a).Value == ((YamlScalarNode) b).Value) ? 1.0f : 0.0f;
|
||||
default:
|
||||
throw new ArgumentException($"Unrecognized YAML node type: {a.GetType()}", nameof(a));
|
||||
}
|
||||
}
|
||||
|
||||
public static float YamlSequencesHeuristic(YamlSequenceNode a, YamlSequenceNode b)
|
||||
{
|
||||
if (a.Children.Count != b.Children.Count)
|
||||
return 0.0f;
|
||||
if (a.Children.Count == 0)
|
||||
return 1.0f;
|
||||
var total = 0.0f;
|
||||
for (var i = 0; i < a.Children.Count; i++)
|
||||
total += YamlNodesHeuristic(a.Children[i], b.Children[i]);
|
||||
return total / a.Children.Count;
|
||||
}
|
||||
|
||||
public static float YamlMappingsHeuristic(YamlMappingNode a, YamlMappingNode b)
|
||||
{
|
||||
return (a == b) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
public static void CopyYamlScalar(YamlScalarNode dst, YamlScalarNode src)
|
||||
{
|
||||
dst.Value = src.Value;
|
||||
dst.Style = src.Style;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user