Updated formatting to match master

pull/36/head
Justin Hopper 2020-03-30 14:03:19 -05:00
parent 14ed050e05
commit 4091654196
13 changed files with 331 additions and 323 deletions

7
Src/.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*]
charset = utf-8
end_of_line = crlf
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -1,4 +1,4 @@
using JsonDiffPatchDotNet.Formatters.JsonPatch;
using JsonDiffPatchDotNet.Formatters.JsonPatch;
using Newtonsoft.Json.Linq;
using NUnit.Framework;

View File

@ -1,10 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.0
# Visual Studio Version 16
VisualStudioVersion = 16.0.29905.134
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B520C700-7D45-4D82-BFB5-218FA68CF5CF}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
global.json = global.json
NuGet.Config = NuGet.Config
EndProjectSection

View File

@ -2,25 +2,25 @@ using System.Collections.Generic;
namespace JsonDiffPatchDotNet.Formatters
{
internal class ArrayKeyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// This purposefully REVERSED from benjamine/jsondiffpatch,
// In order to match logic found in JsonDiffPatch.ArrayPatch,
// which applies operations in reverse order to avoid shifting floor
return ArrayKeyToSortNumber(y) - ArrayKeyToSortNumber(x);
}
internal class ArrayKeyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// This purposefully REVERSED from benjamine/jsondiffpatch,
// In order to match logic found in JsonDiffPatch.ArrayPatch,
// which applies operations in reverse order to avoid shifting floor
return ArrayKeyToSortNumber(y) - ArrayKeyToSortNumber(x);
}
private static int ArrayKeyToSortNumber(string key)
{
if (key == "_t")
return -1;
private static int ArrayKeyToSortNumber(string key)
{
if (key == "_t")
return -1;
if (key.Length > 1 && key[0] == '_')
return int.Parse(key.Substring(1)) * 10;
if (key.Length > 1 && key[0] == '_')
return int.Parse(key.Substring(1)) * 10;
return (int.Parse(key) * 10) + 1;
}
}
return (int.Parse(key) * 10) + 1;
}
}
}

View File

@ -4,164 +4,164 @@ using Newtonsoft.Json.Linq;
namespace JsonDiffPatchDotNet.Formatters
{
public abstract class BaseDeltaFormatter<TContext, TResult>
where TContext : IFormatContext<TResult>, new()
{
public delegate void DeltaKeyIterator(string key, string leftKey, MoveDestination movedFrom, bool isLast);
public abstract class BaseDeltaFormatter<TContext, TResult>
where TContext : IFormatContext<TResult>, new()
{
public delegate void DeltaKeyIterator(string key, string leftKey, MoveDestination movedFrom, bool isLast);
private static readonly IComparer<string> s_arrayKeyComparer = new ArrayKeyComparer();
private static readonly IComparer<string> s_arrayKeyComparer = new ArrayKeyComparer();
public TResult Format(JToken delta)
{
var context = new TContext();
Recurse(context, delta, left: null, key: null, leftKey: null, movedFrom: null, isLast: false);
return context.Result();
}
public TResult Format(JToken delta)
{
var context = new TContext();
Recurse(context, delta, left: null, key: null, leftKey: null, movedFrom: null, isLast: false);
return context.Result();
}
protected abstract bool IncludeMoveDestinations { get; }
protected abstract bool IncludeMoveDestinations { get; }
protected abstract void NodeBegin(TContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast);
protected abstract void NodeBegin(TContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast);
protected abstract void NodeEnd(TContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast);
protected abstract void NodeEnd(TContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast);
protected abstract void RootBegin(TContext context, DeltaType type, NodeType nodeType);
protected abstract void RootBegin(TContext context, DeltaType type, NodeType nodeType);
protected abstract void RootEnd(TContext context, DeltaType type, NodeType nodeType);
protected abstract void RootEnd(TContext context, DeltaType type, NodeType nodeType);
protected abstract void Format(DeltaType type, TContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom);
protected void Recurse(TContext context, JToken delta, JToken left, string key, string leftKey, MoveDestination movedFrom, bool isLast)
{
var useMoveOriginHere = delta != null && movedFrom != null;
var leftValue = useMoveOriginHere ? movedFrom.Value : left;
protected abstract void Format(DeltaType type, TContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom);
if (delta == null && string.IsNullOrEmpty(key))
return;
protected void Recurse(TContext context, JToken delta, JToken left, string key, string leftKey, MoveDestination movedFrom, bool isLast)
{
var useMoveOriginHere = delta != null && movedFrom != null;
var leftValue = useMoveOriginHere ? movedFrom.Value : left;
var type = GetDeltaType(delta, movedFrom);
var nodeType = type == DeltaType.Node ? (delta["_t"]?.Value<string>() == "a" ? NodeType.Array : NodeType.Object) : NodeType.Unknown;
if (delta == null && string.IsNullOrEmpty(key))
return;
if (!string.IsNullOrEmpty(key))
NodeBegin(context, key, leftKey, type, nodeType, isLast);
else
RootBegin(context, type, nodeType);
var type = GetDeltaType(delta, movedFrom);
var nodeType = type == DeltaType.Node ? (delta["_t"]?.Value<string>() == "a" ? NodeType.Array : NodeType.Object) : NodeType.Unknown;
Format(type, context, delta, leftValue, key, leftKey, movedFrom);
if (!string.IsNullOrEmpty(key))
NodeBegin(context, key, leftKey, type, nodeType, isLast);
else
RootBegin(context, type, nodeType);
if (!string.IsNullOrEmpty(key))
NodeEnd(context, key, leftKey, type, nodeType, isLast);
else
RootEnd(context, type, nodeType);
}
Format(type, context, delta, leftValue, key, leftKey, movedFrom);
protected void FormatDeltaChildren(TContext context, JToken delta, JToken left)
{
ForEachDeltaKey(delta, left, Iterator);
if (!string.IsNullOrEmpty(key))
NodeEnd(context, key, leftKey, type, nodeType, isLast);
else
RootEnd(context, type, nodeType);
}
void Iterator(string key, string leftKey, MoveDestination movedFrom, bool isLast)
{
Recurse(context, delta[key], left?[leftKey], key, leftKey, movedFrom, isLast);
}
}
protected void FormatDeltaChildren(TContext context, JToken delta, JToken left)
{
ForEachDeltaKey(delta, left, Iterator);
protected void ForEachDeltaKey(JToken delta, JToken left, DeltaKeyIterator iterator)
{
var keys = new List<string>();
var arrayKeys = false;
var movedDestinations = new Dictionary<string, MoveDestination>();
void Iterator(string key, string leftKey, MoveDestination movedFrom, bool isLast)
{
Recurse(context, delta[key], left?[leftKey], key, leftKey, movedFrom, isLast);
}
}
if (delta is JObject jObject)
{
keys = jObject.Properties().Select(p => p.Name).ToList();
arrayKeys = jObject["_t"]?.Value<string>() == "a";
}
protected void ForEachDeltaKey(JToken delta, JToken left, DeltaKeyIterator iterator)
{
var keys = new List<string>();
var arrayKeys = false;
var movedDestinations = new Dictionary<string, MoveDestination>();
if (left != null && left is JObject leftObject)
{
foreach (var kvp in leftObject)
{
if (delta[kvp.Key] == null && (!arrayKeys || delta["_"+ kvp.Key] == null))
{
keys.Add(kvp.Key);
}
}
}
if (delta is JObject jObject)
{
keys = jObject.Properties().Select(p => p.Name).ToList();
arrayKeys = jObject["_t"]?.Value<string>() == "a";
}
if (delta is JObject deltaObject)
{
foreach (var kvp in deltaObject)
{
var value = kvp.Value;
if (value is JArray valueArray && valueArray.Count == 3)
{
var diffOp = valueArray[2].Value<int>();
if (diffOp == (int)DiffOperation.ArrayMove)
{
var moveKey = valueArray[1].ToString();
movedDestinations[moveKey] = new MoveDestination(kvp.Key, left?[kvp.Key.Substring(1)]);
if (left != null && left is JObject leftObject)
{
foreach (var kvp in leftObject)
{
if (delta[kvp.Key] == null && (!arrayKeys || delta["_" + kvp.Key] == null))
{
keys.Add(kvp.Key);
}
}
}
if (IncludeMoveDestinations && left == null && deltaObject.Property(moveKey) == null)
keys.Add(moveKey);
}
}
}
}
if (delta is JObject deltaObject)
{
foreach (var kvp in deltaObject)
{
var value = kvp.Value;
if (value is JArray valueArray && valueArray.Count == 3)
{
var diffOp = valueArray[2].Value<int>();
if (diffOp == (int)DiffOperation.ArrayMove)
{
var moveKey = valueArray[1].ToString();
movedDestinations[moveKey] = new MoveDestination(kvp.Key, left?[kvp.Key.Substring(1)]);
if (arrayKeys)
keys.Sort(s_arrayKeyComparer);
else
keys.Sort();
if (IncludeMoveDestinations && left == null && deltaObject.Property(moveKey) == null)
keys.Add(moveKey);
}
}
}
}
for (var index = 0; index < keys.Count; index++)
{
var key = keys[index];
if (arrayKeys && key == "_t")
continue;
if (arrayKeys)
keys.Sort(s_arrayKeyComparer);
else
keys.Sort();
var leftKey = arrayKeys
? key.TrimStart('_')
: key;
for (var index = 0; index < keys.Count; index++)
{
var key = keys[index];
if (arrayKeys && key == "_t")
continue;
var isLast = index == keys.Count - 1;
var movedFrom = movedDestinations.ContainsKey(leftKey) ? movedDestinations[leftKey] : null;
iterator(key, leftKey, movedFrom, isLast);
}
}
var leftKey = arrayKeys
? key.TrimStart('_')
: key;
protected static DeltaType GetDeltaType(JToken delta = null, MoveDestination movedFrom = null)
{
if (delta == null)
return movedFrom != null ? DeltaType.MoveDestination : DeltaType.Unchanged;
switch (delta.Type)
{
case JTokenType.Array:
{
var deltaArray = (JArray)delta;
switch (deltaArray.Count)
{
case 1: return DeltaType.Added;
case 2: return DeltaType.Modified;
case 3:
{
switch ((DiffOperation)deltaArray[2].Value<int>())
{
case DiffOperation.Deleted: return DeltaType.Deleted;
case DiffOperation.TextDiff: return DeltaType.TextDiff;
case DiffOperation.ArrayMove: return DeltaType.Moved;
}
break;
}
}
var isLast = index == keys.Count - 1;
var movedFrom = movedDestinations.ContainsKey(leftKey) ? movedDestinations[leftKey] : null;
iterator(key, leftKey, movedFrom, isLast);
}
}
break;
}
protected static DeltaType GetDeltaType(JToken delta = null, MoveDestination movedFrom = null)
{
if (delta == null)
return movedFrom != null ? DeltaType.MoveDestination : DeltaType.Unchanged;
case JTokenType.Object:
return DeltaType.Node;
}
switch (delta.Type)
{
case JTokenType.Array:
{
var deltaArray = (JArray)delta;
switch (deltaArray.Count)
{
case 1: return DeltaType.Added;
case 2: return DeltaType.Modified;
case 3:
{
switch ((DiffOperation)deltaArray[2].Value<int>())
{
case DiffOperation.Deleted: return DeltaType.Deleted;
case DiffOperation.TextDiff: return DeltaType.TextDiff;
case DiffOperation.ArrayMove: return DeltaType.Moved;
}
break;
}
}
return DeltaType.Unknown;
}
}
break;
}
case JTokenType.Object:
return DeltaType.Node;
}
return DeltaType.Unknown;
}
}
}

View File

@ -1,15 +1,15 @@
namespace JsonDiffPatchDotNet.Formatters
{
public enum DeltaType
{
Unknown,
Unchanged,
Added,
Moved,
Deleted,
MoveDestination,
Modified,
Node,
TextDiff
}
public enum DeltaType
{
Unknown,
Unchanged,
Added,
Moved,
Deleted,
MoveDestination,
Modified,
Node,
TextDiff
}
}

View File

@ -1,7 +1,7 @@
namespace JsonDiffPatchDotNet.Formatters
namespace JsonDiffPatchDotNet.Formatters
{
public interface IFormatContext<out TResult>
{
TResult Result();
}
public interface IFormatContext<out TResult>
{
TResult Result();
}
}

View File

@ -1,85 +1,85 @@
using System;
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
namespace JsonDiffPatchDotNet.Formatters.JsonPatch
{
public class JsonDeltaFormatter : BaseDeltaFormatter<JsonFormatContext, IList<Operation>>
{
protected override bool IncludeMoveDestinations => true;
public class JsonDeltaFormatter : BaseDeltaFormatter<JsonFormatContext, IList<Operation>>
{
protected override bool IncludeMoveDestinations => true;
protected override void Format(DeltaType type, JsonFormatContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom)
{
switch (type)
{
case DeltaType.Added:
FormatAdded(context, delta);
break;
protected override void Format(DeltaType type, JsonFormatContext context, JToken delta, JToken leftValue, string key, string leftKey, MoveDestination movedFrom)
{
switch (type)
{
case DeltaType.Added:
FormatAdded(context, delta);
break;
case DeltaType.Node:
FormatNode(context, delta, leftValue);
break;
case DeltaType.Node:
FormatNode(context, delta, leftValue);
break;
case DeltaType.Modified:
FormatModified(context, delta);
break;
case DeltaType.Modified:
FormatModified(context, delta);
break;
case DeltaType.Deleted:
FormatDeleted(context);
break;
case DeltaType.Deleted:
FormatDeleted(context);
break;
case DeltaType.Moved:
FormatMoved(context, delta);
break;
case DeltaType.Moved:
FormatMoved(context, delta);
break;
case DeltaType.Unknown:
case DeltaType.Unchanged:
case DeltaType.MoveDestination:
break;
case DeltaType.Unknown:
case DeltaType.Unchanged:
case DeltaType.MoveDestination:
break;
case DeltaType.TextDiff:
throw new InvalidOperationException("JSON RFC 6902 does not support TextDiff.");
}
}
case DeltaType.TextDiff:
throw new InvalidOperationException("JSON RFC 6902 does not support TextDiff.");
}
}
protected override void NodeBegin(JsonFormatContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast)
{
context.Path.Add(leftKey);
}
protected override void NodeBegin(JsonFormatContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast)
{
context.Path.Add(leftKey);
}
protected override void NodeEnd(JsonFormatContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast)
{
if (context.Path.Count > 0)
context.Path.RemoveAt(context.Path.Count - 1);
}
protected override void NodeEnd(JsonFormatContext context, string key, string leftKey, DeltaType type, NodeType nodeType, bool isLast)
{
if (context.Path.Count > 0)
context.Path.RemoveAt(context.Path.Count - 1);
}
protected override void RootBegin(JsonFormatContext context, DeltaType type, NodeType nodeType) { }
protected override void RootBegin(JsonFormatContext context, DeltaType type, NodeType nodeType) { }
protected override void RootEnd(JsonFormatContext context, DeltaType type, NodeType nodeType) { }
protected override void RootEnd(JsonFormatContext context, DeltaType type, NodeType nodeType) { }
private void FormatNode(JsonFormatContext context, JToken delta, JToken left)
{
FormatDeltaChildren(context, delta, left);
}
private void FormatNode(JsonFormatContext context, JToken delta, JToken left)
{
FormatDeltaChildren(context, delta, left);
}
private void FormatAdded(JsonFormatContext context, JToken delta)
{
context.PushCurrentOp(OperationTypes.Add, delta[0]);
}
private void FormatAdded(JsonFormatContext context, JToken delta)
{
context.PushCurrentOp(OperationTypes.Add, delta[0]);
}
private void FormatModified(JsonFormatContext context, JToken delta)
{
context.PushCurrentOp(OperationTypes.Replace, delta[1]);
}
private void FormatModified(JsonFormatContext context, JToken delta)
{
context.PushCurrentOp(OperationTypes.Replace, delta[1]);
}
private void FormatDeleted(JsonFormatContext context)
{
context.PushCurrentOp(OperationTypes.Remove);
}
private void FormatDeleted(JsonFormatContext context)
{
context.PushCurrentOp(OperationTypes.Remove);
}
private void FormatMoved(JsonFormatContext context, JToken delta)
{
context.PushMoveOp(delta[1].ToString());
}
}
private void FormatMoved(JsonFormatContext context, JToken delta)
{
context.PushMoveOp(delta[1].ToString());
}
}
}

View File

@ -1,50 +1,50 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace JsonDiffPatchDotNet.Formatters.JsonPatch
{
public class JsonFormatContext : IFormatContext<IList<Operation>>
{
public JsonFormatContext()
{
Operations = new List<Operation>();
Path = new List<string>();
}
public class JsonFormatContext : IFormatContext<IList<Operation>>
{
public JsonFormatContext()
{
Operations = new List<Operation>();
Path = new List<string>();
}
public IList<Operation> Operations { get; }
public IList<Operation> Operations { get; }
public IList<string> Path { get; }
public IList<string> Path { get; }
public IList<Operation> Result()
{
return Operations;
}
public IList<Operation> Result()
{
return Operations;
}
public void PushCurrentOp(string op)
{
Operations.Add(new Operation(op, CurrentPath(), null));
}
public void PushCurrentOp(string op)
{
Operations.Add(new Operation(op, CurrentPath(), null));
}
public void PushCurrentOp(string op, object value)
{
Operations.Add(new Operation(op, CurrentPath(), null, value));
}
public void PushCurrentOp(string op, object value)
{
Operations.Add(new Operation(op, CurrentPath(), null, value));
}
public void PushMoveOp(string to)
{
Operations.Add(new Operation(OperationTypes.Move, ToPath(to), CurrentPath()));
}
public void PushMoveOp(string to)
{
Operations.Add(new Operation(OperationTypes.Move, ToPath(to), CurrentPath()));
}
private string CurrentPath()
{
return $"/{string.Join("/", Path)}";
}
private string CurrentPath()
{
return $"/{string.Join("/", Path)}";
}
private string ToPath(string toPath)
{
var to = Path.ToList();
to[to.Count - 1] = toPath;
return $"/{string.Join("/", to)}";
}
}
private string ToPath(string toPath)
{
var to = Path.ToList();
to[to.Count - 1] = toPath;
return $"/{string.Join("/", to)}";
}
}
}

View File

@ -1,36 +1,36 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
namespace JsonDiffPatchDotNet.Formatters.JsonPatch
{
public class Operation
{
public Operation() { }
public class Operation
{
public Operation() { }
public Operation(string op, string path, string from)
{
Op = op;
Path = path;
From = from;
}
public Operation(string op, string path, string from)
{
Op = op;
Path = path;
From = from;
}
public Operation(string op, string path, string from, object value)
{
Op = op;
Path = path;
From = from;
Value = value;
}
public Operation(string op, string path, string from, object value)
{
Op = op;
Path = path;
From = from;
Value = value;
}
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("op")]
public string Op { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("from")]
public string From { get; set; }
[JsonProperty("op")]
public string Op { get; set; }
[JsonProperty("value")]
public object Value { get; set; }
}
[JsonProperty("from")]
public string From { get; set; }
[JsonProperty("value")]
public object Value { get; set; }
}
}

View File

@ -1,13 +1,13 @@
namespace JsonDiffPatchDotNet.Formatters.JsonPatch
namespace JsonDiffPatchDotNet.Formatters.JsonPatch
{
public static class OperationTypes
{
public const string Add = "add";
public static class OperationTypes
{
public const string Add = "add";
public const string Replace = "replace";
public const string Replace = "replace";
public const string Move = "move";
public const string Move = "move";
public const string Remove = "remove";
}
public const string Remove = "remove";
}
}

View File

@ -1,17 +1,17 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
namespace JsonDiffPatchDotNet.Formatters
{
public class MoveDestination
{
public MoveDestination(string key, JToken value)
{
Key = key;
Value = value;
}
public class MoveDestination
{
public MoveDestination(string key, JToken value)
{
Key = key;
Value = value;
}
public string Key { get; }
public string Key { get; }
public JToken Value { get; }
}
public JToken Value { get; }
}
}

View File

@ -1,9 +1,9 @@
namespace JsonDiffPatchDotNet.Formatters
namespace JsonDiffPatchDotNet.Formatters
{
public enum NodeType
{
Unknown,
Object,
Array
}
public enum NodeType
{
Unknown,
Object,
Array
}
}