holopy3/Assets/UniGLTF/UniJSON/Scripts/Json/JsonNode.cs
Lena Biresch 490ef558ef CleanUp
2021-01-28 13:07:52 +01:00

690 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UniJSON
{
public struct JsonPointer
{
public ArraySegment<String> Path
{
get;
private set;
}
public string this[int index]
{
get
{
return Path.Array[Path.Offset + index];
}
}
public JsonPointer Unshift()
{
return new JsonPointer
{
Path = new ArraySegment<string>(Path.Array, Path.Offset + 1, Path.Count - 1)
};
}
public JsonPointer(JsonNode node)
{
Path = new ArraySegment<string>(node.Path().Skip(1).Select(x => GetKeyFromParent(x)).ToArray());
}
public JsonPointer(string pointer)
{
if (!pointer.StartsWith("/"))
{
throw new ArgumentException();
}
var splited = pointer.Split('/');
Path = new ArraySegment<string>(splited, 1, splited.Length - 1);
}
public override string ToString()
{
if (Path.Count == 0)
{
return "/";
}
var sb = new StringBuilder();
var end = Path.Offset + Path.Count;
for (int i = Path.Offset; i < end; ++i)
{
sb.Append('/');
sb.Append(Path.Array[i]);
}
return sb.ToString();
}
static string GetKeyFromParent(JsonNode json)
{
var parent = json.Parent;
switch (parent.Value.ValueType)
{
case JsonValueType.Array:
{
return parent.IndexOf(json).ToString();
}
case JsonValueType.Object:
{
return parent.KeyOf(json);
}
default:
throw new NotImplementedException();
}
}
}
public enum JsonDiffType
{
KeyAdded,
KeyRemoved,
ValueChanged,
}
public struct JsonDiff
{
public JsonPointer Path;
public JsonDiffType DiffType;
public string Msg;
public JsonDiff(JsonNode node, JsonDiffType diffType, string msg)
{
Path = new JsonPointer(node);
DiffType = diffType;
Msg = msg;
}
public override string ToString()
{
switch (DiffType)
{
case JsonDiffType.KeyAdded:
return string.Format("+ {0}: {1}", Path, Msg);
case JsonDiffType.KeyRemoved:
return string.Format("- {0}: {1}", Path, Msg);
case JsonDiffType.ValueChanged:
return string.Format("= {0}: {1}", Path, Msg);
default:
throw new NotImplementedException();
}
}
}
public struct JsonNode
{
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is JsonNode))
{
return false;
}
var rhs = (JsonNode)obj;
if ((Value.ValueType == JsonValueType.Integer || Value.ValueType == JsonValueType.Null)
&& (rhs.Value.ValueType == JsonValueType.Integer || rhs.Value.ValueType == JsonValueType.Number))
{
// ok
}
else if (Value.ValueType != rhs.Value.ValueType)
{
return false;
}
switch (Value.ValueType)
{
case JsonValueType.Null:
return true;
case JsonValueType.Boolean:
return Value.GetBoolean() == rhs.GetBoolean();
case JsonValueType.Integer:
case JsonValueType.Number:
return Value.GetDouble() == rhs.GetDouble();
case JsonValueType.String:
return Value.GetString() == rhs.GetString();
case JsonValueType.Array:
return ArrayItems.SequenceEqual(rhs.ArrayItems);
case JsonValueType.Object:
{
var l = ObjectItems.ToDictionary(x => x.Key, x => x.Value);
var r = rhs.ObjectItems.ToDictionary(x => x.Key, x => x.Value);
l.Equals(r);
return ObjectItems.OrderBy(x => x.Key).SequenceEqual(rhs.ObjectItems.OrderBy(x => x.Key));
}
}
return false;
}
public IEnumerable<JsonDiff> Diff(JsonNode rhs, JsonPointer path = default(JsonPointer))
{
switch (Value.ValueType)
{
case JsonValueType.Null:
case JsonValueType.Boolean:
case JsonValueType.Number:
case JsonValueType.Integer:
case JsonValueType.String:
if (!Equals(rhs))
{
yield return new JsonDiff(this, JsonDiffType.ValueChanged, string.Format("{0} => {1}", Value, rhs.Value));
}
yield break;
}
if (Value.ValueType != rhs.Value.ValueType)
{
yield return new JsonDiff(this, JsonDiffType.ValueChanged, string.Format("{0} => {1}", Value.ValueType, rhs.Value));
yield break;
}
if (Value.ValueType == JsonValueType.Object)
{
var l = ObjectItems.ToDictionary(x => x.Key, x => x.Value);
var r = rhs.ObjectItems.ToDictionary(x => x.Key, x => x.Value);
foreach (var kv in l)
{
JsonNode x;
if (r.TryGetValue(kv.Key, out x))
{
r.Remove(kv.Key);
// Found
foreach (var y in kv.Value.Diff(x))
{
yield return y;
}
}
else
{
// Removed
yield return new JsonDiff(kv.Value, JsonDiffType.KeyRemoved, kv.Value.Value.ToString());
}
}
foreach (var kv in r)
{
// Addded
yield return new JsonDiff(kv.Value, JsonDiffType.KeyAdded, kv.Value.Value.ToString());
}
}
else if (Value.ValueType == JsonValueType.Array)
{
var ll = ArrayItems.GetEnumerator();
var rr = rhs.ArrayItems.GetEnumerator();
while (true)
{
var lll = ll.MoveNext();
var rrr = rr.MoveNext();
if (lll && rrr)
{
// found
foreach (var y in ll.Current.Diff(rr.Current))
{
yield return y;
}
}
else if (lll)
{
yield return new JsonDiff(ll.Current, JsonDiffType.KeyRemoved, ll.Current.Value.ToString());
}
else if (rrr)
{
yield return new JsonDiff(rr.Current, JsonDiffType.KeyAdded, rr.Current.Value.ToString());
}
else
{
// end
break;
}
}
}
else
{
throw new NotImplementedException();
}
}
public readonly List<JsonValue> Values;
int m_index;
public JsonValue Value
{
get { return Values[m_index]; }
}
public IEnumerable<JsonNode> Children
{
get
{
for (int i = 0; i < Values.Count; ++i)
{
if (Values[i].ParentIndex == m_index)
{
yield return new JsonNode(Values, i);
}
}
}
}
public bool HasParent
{
get
{
return Value.ParentIndex >= 0 && Value.ParentIndex < Values.Count;
}
}
public JsonNode Parent
{
get
{
if (Value.ParentIndex < 0)
{
throw new Exception("no parent");
}
if (Value.ParentIndex >= Values.Count)
{
throw new IndexOutOfRangeException();
}
return new JsonNode(Values, Value.ParentIndex);
}
}
public JsonNode(List<JsonValue> values, int index = 0)
{
Values = values;
m_index = index;
}
#region object interface
public JsonNode this[string key]
{
get
{
foreach (var kv in ObjectItems)
{
if (kv.Key == key)
{
return kv.Value;
}
}
throw new KeyNotFoundException();
}
}
public bool ContainsKey(string key)
{
return ObjectItems.Any(x => x.Key == key);
}
public IEnumerable<KeyValuePair<string, JsonNode>> ObjectItems
{
get
{
if (this.Value.ValueType != JsonValueType.Object) throw new JsonValueException("is not object");
var it = Children.GetEnumerator();
while (it.MoveNext())
{
var key = it.Current.GetString();
it.MoveNext();
yield return new KeyValuePair<string, JsonNode>(key, it.Current);
}
}
}
public string KeyOf(JsonNode node)
{
foreach (var kv in ObjectItems)
{
if (node.m_index == kv.Value.m_index)
{
return kv.Key;
}
}
throw new KeyNotFoundException();
}
#endregion
#region array interface
public JsonNode this[int index]
{
get
{
int i = 0;
foreach (var v in ArrayItems)
{
if (i++ == index)
{
return v;
}
}
throw new KeyNotFoundException();
}
}
public IEnumerable<JsonNode> ArrayItems
{
get
{
if (this.Value.ValueType != JsonValueType.Array) throw new JsonValueException("is not object");
return Children;
}
}
public int IndexOf(JsonNode child)
{
int i = 0;
foreach (var v in ArrayItems)
{
if (v.m_index == child.m_index)
{
return i;
}
++i;
}
throw new KeyNotFoundException();
}
#endregion
public void RemoveKey(string key)
{
if (Value.ValueType != JsonValueType.Object)
{
throw new Exception("is not object");
}
var parentIndex = m_index;
var indices = Values
.Select((value, index) => new { value, index })
.Where(x => x.value.ParentIndex == parentIndex)
.ToArray();
for (int i = 0; i < indices.Length; i += 2)
{
if (indices[i].value.GetString() == key)
{
Values[indices[i].index] = JsonValue.Empty; // remove
Values[indices[i + 1].index] = JsonValue.Empty; // remove
}
}
}
public void AddNode(string key, JsonNode node)
{
if (Value.ValueType != JsonValueType.Object)
{
throw new InvalidOperationException();
}
/*
switch (key)
{
case "title":
case "$schema":
// skip
return;
}
*/
Values.Add(new JsonValue(new StringSegment("\"" + key + "\""), JsonValueType.String, m_index));
AddNode(node);
}
private void AddNode(JsonNode node)
{
var index = Values.Count;
Values.Add(new JsonValue(node.Value.Segment, node.Value.ValueType, m_index));
var parent = new JsonNode(Values, index);
if (node.Value.ValueType == JsonValueType.Array)
{
foreach (var value in node.ArrayItems)
{
parent.AddNode(value);
}
}
else if (node.Value.ValueType == JsonValueType.Object)
{
foreach (var kv in node.ObjectItems)
{
parent.AddNode(kv.Key, kv.Value);
}
}
}
public IEnumerable<JsonNode> GetNodes(JsonPointer jsonPointer)
{
if (jsonPointer.Path.Count == 0)
{
yield return this;
yield break;
}
if (Value.ValueType == JsonValueType.Array)
{
// array
if (jsonPointer[0] == "*")
{
// wildcard
foreach (var child in ArrayItems)
{
foreach (var childChild in child.GetNodes(jsonPointer.Unshift()))
{
yield return childChild;
}
}
}
else
{
int index;
if (!int.TryParse(jsonPointer[0], out index))
{
throw new KeyNotFoundException();
}
var child = this[index];
foreach (var childChild in child.GetNodes(jsonPointer.Unshift()))
{
yield return childChild;
}
}
}
else if (Value.ValueType == JsonValueType.Object)
{
// object
if (jsonPointer[0] == "*")
{
// wildcard
foreach (var kv in ObjectItems)
{
foreach (var childChild in kv.Value.GetNodes(jsonPointer.Unshift()))
{
yield return childChild;
}
}
}
else
{
JsonNode child;
try
{
child = this[jsonPointer[0]];
}
catch (KeyNotFoundException)
{
// key
Values.Add(new JsonValue(new StringSegment(JsonString.Quote(jsonPointer[0])), JsonValueType.String, m_index));
// value
Values.Add(new JsonValue(new StringSegment(), JsonValueType.Object, m_index));
child = this[jsonPointer[0]];
}
foreach (var childChild in child.GetNodes(jsonPointer.Unshift()))
{
yield return childChild;
}
}
}
else
{
throw new NotImplementedException();
}
}
public IEnumerable<JsonNode> GetNodes(string jsonPointer)
{
return GetNodes(new JsonPointer(jsonPointer));
}
void SetValue(JsonPointer jsonPointer, Func<int, JsonValue> createNodeValue)
{
foreach (var node in GetNodes(jsonPointer))
{
Values[node.m_index] = createNodeValue(node.Value.ParentIndex);
}
}
public void SetValue(string jsonPointer, string value)
{
SetValue(new JsonPointer(jsonPointer), parentIndex => new JsonValue
{
ParentIndex = parentIndex,
Segment = new StringSegment(JsonString.Quote(value)),
ValueType = JsonValueType.String
});
}
public void SetValue(string jsonPointer, int value)
{
SetValue(new JsonPointer(jsonPointer), parentIndex => new JsonValue
{
ParentIndex = parentIndex,
Segment = new StringSegment(value.ToString()),
ValueType = JsonValueType.Integer
});
}
public void SetValue(string jsonPointer, float value)
{
SetValue(new JsonPointer(jsonPointer), parentIndex => new JsonValue
{
ParentIndex = parentIndex,
Segment = new StringSegment(value.ToString()),
ValueType = JsonValueType.Integer
});
}
public void SetValue(string jsonPointer, bool value)
{
SetValue(new JsonPointer(jsonPointer), parentIndex => new JsonValue
{
ParentIndex = parentIndex,
Segment = new StringSegment(value.ToString().ToLower()),
ValueType = JsonValueType.Boolean
});
}
public void RemoveValue(string jsonPointer)
{
foreach (var node in GetNodes(new JsonPointer(jsonPointer)))
{
if (node.Parent.Value.ValueType == JsonValueType.Object)
{
Values[node.m_index - 1] = JsonValue.Empty; // remove key
}
Values[node.m_index] = JsonValue.Empty; // remove
}
}
}
public static class JsonNodeExtensions
{
public static Boolean GetBoolean(this JsonNode self)
{
return self.Value.GetBoolean();
}
public static Int32 GetInt32(this JsonNode self)
{
return self.Value.GetInt32();
}
public static Double GetDouble(this JsonNode self)
{
return self.Value.GetDouble();
}
public static string GetString(this JsonNode self)
{
return self.Value.GetString();
}
/*
public static IEnumerable<KeyValuePair<string, JsonNode>> TraverseObjects(this JsonNode self)
{
foreach (var kv in self.ObjectItems)
{
yield return kv;
if (kv.Value.Value.ValueType == JsonValueType.Object)
{
foreach (var _kv in kv.Value.TraverseObjects())
{
yield return _kv;
}
}
}
}
*/
public static IEnumerable<JsonNode> Traverse(this JsonNode self)
{
yield return self;
if (self.Value.ValueType == JsonValueType.Array)
{
foreach (var x in self.ArrayItems)
{
foreach (var y in x.Traverse())
{
yield return y;
}
}
}
else if (self.Value.ValueType == JsonValueType.Object)
{
foreach (var kv in self.ObjectItems)
{
foreach (var y in kv.Value.Traverse())
{
yield return y;
}
}
}
}
public static IEnumerable<JsonNode> Path(this JsonNode self)
{
if (self.HasParent)
{
foreach (var x in self.Parent.Path())
{
yield return x;
}
}
yield return self;
}
}
}