670 lines
19 KiB (Stored with Git LFS)
C#
670 lines
19 KiB (Stored with Git LFS)
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Superlazy
|
|
{
|
|
internal abstract class SLContainerBase : SLEntity
|
|
{
|
|
public override string ID => id;
|
|
|
|
protected SLContainerBase parent;
|
|
protected string id;
|
|
}
|
|
|
|
internal class SLContainer : SLContainerBase
|
|
{
|
|
private SLEntity original;
|
|
private Dictionary<string, SLEntity> attributes;
|
|
private HashSet<string> removed;
|
|
private List<SLContainerLink> links;
|
|
private bool dangled;
|
|
|
|
public SLContainer(SLContainerBase original, SLContainerBase parent, string id)
|
|
{
|
|
this.original = original;
|
|
this.parent = parent;
|
|
this.id = id;
|
|
|
|
if (original.IsNullOrFalse() && this.parent is null == false)
|
|
{
|
|
dangled = true;
|
|
}
|
|
}
|
|
|
|
internal override SLEntity ToChild(SLContainerBase parent, string id)
|
|
{
|
|
if (id == null && parent is null) // 삭제시
|
|
{
|
|
dangled = true;
|
|
|
|
attributes?.Clear();
|
|
removed?.Clear();
|
|
original = null;
|
|
|
|
if (links != null)
|
|
{
|
|
foreach (var link in links)
|
|
{
|
|
link.DestroyLink();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dangled == false && this.parent is null == false)
|
|
{
|
|
return Clone().ToChild(parent, id);
|
|
}
|
|
else
|
|
{
|
|
dangled = false;
|
|
this.parent = parent;
|
|
this.id = id;
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public override bool IsNumeric
|
|
{
|
|
get
|
|
{
|
|
if (IsExist())
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool IsValue => false;
|
|
|
|
internal override bool IsExist()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false)) return true;
|
|
|
|
if ((attributes?.Count ?? 0) != 0) return true;
|
|
|
|
if (original.IsNullOrFalse()) return false;
|
|
|
|
if ((removed?.Count ?? 0) == 0) return true;
|
|
if (original.Any(e => removed.Contains(e.ID) == false)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool HasChild(string attributeKey)
|
|
{
|
|
if (attributeKey == "ID") return true;
|
|
if (dangled && (parent?.HasChild(id) ?? false)) return parent[id].HasChild(attributeKey);
|
|
|
|
if (removed?.Contains(attributeKey) ?? false) return false;
|
|
if (attributes?.ContainsKey(attributeKey) ?? false) return true;
|
|
|
|
if (original.IsNullOrFalse() == false)
|
|
{
|
|
return original.HasChild(attributeKey);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override IEnumerator<SLEntity> GetEnumerator()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false))
|
|
{
|
|
foreach (var value in parent[id])
|
|
{
|
|
yield return value;
|
|
}
|
|
|
|
yield break;
|
|
}
|
|
|
|
if (attributes?.Count > 0)
|
|
{
|
|
var keys = new List<string>(attributes.Keys);
|
|
|
|
foreach (var key in keys)
|
|
{
|
|
yield return attributes[key];
|
|
}
|
|
}
|
|
|
|
if (original)
|
|
{
|
|
foreach (var child in original.Where(e => (removed?.Contains(e.ID) ?? false) == false && (attributes?.ContainsKey(e.ID) ?? false) == false).ToList())
|
|
{
|
|
yield return this[child.ID];
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override double GetDouble()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false))
|
|
{
|
|
return parent[id].GetDouble();
|
|
}
|
|
|
|
if (IsExist())
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal override int GetInt()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false))
|
|
{
|
|
return parent[id].GetInt();
|
|
}
|
|
|
|
if (IsExist())
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
internal override string GetString()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false))
|
|
{
|
|
return parent[id].GetString();
|
|
}
|
|
|
|
if (IsExist() == false)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return $"{id}[{attributes?.Count ?? 0}]";
|
|
}
|
|
|
|
internal override object GetObj()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false))
|
|
{
|
|
return parent[id].GetString();
|
|
}
|
|
|
|
SLLog.Error($"this is not value : {id}");
|
|
return false;
|
|
}
|
|
|
|
protected override int GetEntityHashCode()
|
|
{
|
|
if (dangled && (parent?.HasChild(id) ?? false)) return parent[id].GetHashCode();
|
|
if (attributes != null && removed != null) return attributes.GetHashCode() & removed.GetHashCode(); // TODO: 딕셔너리 대신 정밀한 해시코드를 만들어야함(성능이슈 체크)
|
|
if (attributes != null) return attributes.GetHashCode(); // TODO: 딕셔너리 대신 정밀한 해시코드를 만들어야함(성능이슈 체크)
|
|
if (removed != null) return removed.GetHashCode(); // TODO: 딕셔너리 대신 정밀한 해시코드를 만들어야함(성능이슈 체크)
|
|
if (original.IsNullOrFalse() == false) return original.GetHashCode();
|
|
return 0;
|
|
}
|
|
|
|
public override SLEntity Link()
|
|
{
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) return parent[id].Link();
|
|
|
|
SLLog.Error("Can't make empty link");
|
|
return false;
|
|
}
|
|
|
|
if (links == null)
|
|
{
|
|
links = new List<SLContainerLink>();
|
|
}
|
|
|
|
var unusedLink = links.Find(l => l.Unused);
|
|
if (unusedLink is null == false)
|
|
{
|
|
return unusedLink;
|
|
}
|
|
|
|
var newLink = new SLContainerLink(this);
|
|
links.Add(newLink);
|
|
return newLink;
|
|
}
|
|
|
|
public override SLEntity Override()
|
|
{
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) return parent[id].Override();
|
|
|
|
SLLog.Error("Can't make empty override");
|
|
return false;
|
|
}
|
|
|
|
return new SLContainer(this, null, null);
|
|
}
|
|
|
|
public override SLEntity this[string attributeKey]
|
|
{
|
|
get
|
|
{
|
|
if (attributeKey == "ID") return ID;
|
|
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) return parent[id][attributeKey];
|
|
return new SLContainer(null, this, attributeKey); // 댕글
|
|
}
|
|
|
|
if (removed?.Contains(attributeKey) ?? false)
|
|
{
|
|
return new SLContainer(null, this, attributeKey);
|
|
}
|
|
|
|
if (attributes?.ContainsKey(attributeKey) ?? false)
|
|
{
|
|
return attributes[attributeKey];
|
|
}
|
|
|
|
if (original?.HasChild(attributeKey) ?? false)
|
|
{
|
|
var originalValue = original[attributeKey];
|
|
if (originalValue.IsValue)
|
|
{
|
|
return originalValue;
|
|
}
|
|
else
|
|
{
|
|
if (attributes == null) attributes = new Dictionary<string, SLEntity>();
|
|
attributes[attributeKey] = originalValue.Override().ToChild(this, attributeKey);
|
|
return attributes[attributeKey];
|
|
}
|
|
}
|
|
|
|
return new SLContainer(null, this, attributeKey);
|
|
}
|
|
|
|
set
|
|
{
|
|
// 댕글인경우
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) // 다른개체로 교체된 댕글
|
|
{
|
|
parent[id][attributeKey] = value;
|
|
return;
|
|
}
|
|
|
|
if (value.IsNullOrFalse()) // 삭제
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 새로 등록
|
|
attributes = new Dictionary<string, SLEntity>();
|
|
|
|
Modified(attributeKey);
|
|
attributes[attributeKey] = value.ToChild(this, attributeKey);
|
|
|
|
if (parent is null) return;
|
|
|
|
parent[id] = this;
|
|
}
|
|
else
|
|
{
|
|
if (attributes?.ContainsKey(attributeKey) ?? false) // 키가 있는 경우
|
|
{
|
|
if (value.IsNullOrFalse()) // 삭제
|
|
{
|
|
{
|
|
Modified(attributeKey);
|
|
var v = attributes[attributeKey];
|
|
attributes.Remove(attributeKey);
|
|
v.ToChild(null, null);
|
|
}
|
|
|
|
if (removed == null) removed = new HashSet<string>();
|
|
removed.Add(attributeKey);
|
|
|
|
if (IsExist() == false)
|
|
{
|
|
if (parent is null == false)
|
|
{
|
|
parent[id] = null;
|
|
}
|
|
}
|
|
}
|
|
else // 변경
|
|
{
|
|
if (value == attributes[attributeKey]) return;
|
|
|
|
{
|
|
Modified(attributeKey);
|
|
var v = attributes[attributeKey];
|
|
attributes.Remove(attributeKey);
|
|
v.ToChild(null, null);
|
|
}
|
|
|
|
attributes[attributeKey] = value.ToChild(this, attributeKey);
|
|
}
|
|
}
|
|
else // 키가 없는경우
|
|
{
|
|
if (original?.HasChild(attributeKey) ?? false) // 덮어쓰기
|
|
{
|
|
if (value.IsNullOrFalse()) // 삭제추가
|
|
{
|
|
if (removed == null) removed = new HashSet<string>();
|
|
Modified(attributeKey);
|
|
removed.Add(attributeKey);
|
|
|
|
if (IsExist() == false)
|
|
{
|
|
if (parent is null == false)
|
|
{
|
|
parent[id] = null;
|
|
}
|
|
}
|
|
}
|
|
else // 추가
|
|
{
|
|
if (attributes == null) attributes = new Dictionary<string, SLEntity>();
|
|
|
|
if (removed?.Contains(attributeKey) ?? false)
|
|
{
|
|
removed.Remove(attributeKey);
|
|
}
|
|
|
|
{
|
|
Modified(attributeKey);
|
|
attributes[attributeKey] = value.ToChild(this, attributeKey);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 추가
|
|
if (value.IsNullOrFalse()) return;
|
|
|
|
if (attributes == null) attributes = new Dictionary<string, SLEntity>();
|
|
|
|
Modified(attributeKey);
|
|
attributes[attributeKey] = value.ToChild(this, attributeKey);
|
|
if (removed != null && removed.Contains(attributeKey))
|
|
{
|
|
removed.Remove(attributeKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override SLEntity Clone()
|
|
{
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) return parent[id].Clone();
|
|
SLLog.Error($"Can't clone dangle object: {id}");
|
|
return false;
|
|
}
|
|
|
|
var ret = new SLContainer(null, null, null);
|
|
|
|
foreach (var child in this)
|
|
{
|
|
ret[child.ID] = child.Clone();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
internal override void Move(SLEntity parent, string newID)
|
|
{
|
|
if (dangled)
|
|
{
|
|
if (parent?.HasChild(id) ?? false) this.parent[id].Move(parent, newID);
|
|
SLLog.Error($"Can't move dangle object : {id} -> {newID}");
|
|
return;
|
|
}
|
|
|
|
if (id != null) // 이미 루트가 없다면
|
|
{
|
|
var attrTemp = attributes;
|
|
this.parent[id] = false;
|
|
|
|
attributes = attrTemp;
|
|
id = null;
|
|
}
|
|
|
|
parent[id] = this;
|
|
}
|
|
|
|
private HashSet<string> modified;
|
|
|
|
public override bool IsModified(string child)
|
|
{
|
|
var ret = false;
|
|
ret |= original?.IsModified(child) ?? false;
|
|
if (child == null) ret |= (modified?.Count ?? 0) != 0;
|
|
else ret |= modified?.Contains(child) ?? false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
public override void EndModified()
|
|
{
|
|
modified?.Clear();
|
|
}
|
|
|
|
private void Modified(string child)
|
|
{
|
|
if (modified == null) modified = new HashSet<string>();
|
|
modified.Add(child);
|
|
}
|
|
}
|
|
|
|
internal class SLContainerLink : SLContainerBase
|
|
{
|
|
public bool Unused => id == null;
|
|
private SLContainer original;
|
|
|
|
public SLContainerLink(SLContainer original)
|
|
{
|
|
this.original = original;
|
|
}
|
|
|
|
internal override SLEntity ToChild(SLContainerBase parent, string id)
|
|
{
|
|
if (id == null && parent is null) // 삭제시
|
|
{
|
|
this.parent = null;
|
|
this.id = null;
|
|
return this;
|
|
}
|
|
|
|
if (this.parent) return Clone().ToChild(parent, id);
|
|
|
|
this.parent = parent;
|
|
this.id = id;
|
|
return this;
|
|
}
|
|
|
|
internal void DestroyLink()
|
|
{
|
|
if (Unused) return;
|
|
parent[id] = false;
|
|
original = null;
|
|
}
|
|
|
|
public override bool IsNumeric
|
|
{
|
|
get
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool IsValue
|
|
{
|
|
get
|
|
{
|
|
if (original.IsNullOrFalse()) return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal override bool IsExist()
|
|
{
|
|
if (original.IsNullOrFalse()) return false;
|
|
return true;
|
|
}
|
|
|
|
public override bool HasChild(string attributeKey)
|
|
{
|
|
if (original.IsNullOrFalse()) return false;
|
|
return original.HasChild(attributeKey);
|
|
}
|
|
|
|
public override IEnumerator<SLEntity> GetEnumerator()
|
|
{
|
|
if (original.IsNullOrFalse()) return Enumerable.Empty<SLEntity>().GetEnumerator();
|
|
return original.GetEnumerator();
|
|
}
|
|
|
|
internal override double GetDouble()
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
return 0;
|
|
}
|
|
|
|
internal override int GetInt()
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
return 0;
|
|
}
|
|
|
|
internal override object GetObj()
|
|
{
|
|
SLLog.Error($"this is not value : {id}");
|
|
return false;
|
|
}
|
|
|
|
public override SLEntity Link()
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
SLLog.Error($"Destroyed Link, {id}");
|
|
return null;
|
|
}
|
|
|
|
return original.Link();
|
|
}
|
|
|
|
public override SLEntity Override()
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
SLLog.Error($"Destroyed Link, {id}");
|
|
return null;
|
|
}
|
|
|
|
return original.Override();
|
|
}
|
|
|
|
internal override string GetString()
|
|
{
|
|
if (original.IsNullOrFalse()) return string.Empty;
|
|
return $"{id} - {original}";
|
|
}
|
|
|
|
public override SLEntity this[string attributeKey]
|
|
{
|
|
get
|
|
{
|
|
if (attributeKey == "ID") return ID;
|
|
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
if (parent?.HasChild(id) ?? false) return parent[id][attributeKey];
|
|
return false;
|
|
}
|
|
|
|
return original[attributeKey];
|
|
}
|
|
|
|
set
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
if (parent?.HasChild(id) ?? false) parent[id][attributeKey] = value;
|
|
|
|
// 빈객체에 값을 넣어도 이값을 다시참조할 방법이 없음
|
|
SLLog.Error($"Dangle Link. Can't set new Value : {attributeKey}-{value}");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
original[attributeKey] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override SLEntity Clone()
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
SLLog.Error($"Destroyed Link, {id}");
|
|
return null;
|
|
}
|
|
|
|
return original.Clone();
|
|
}
|
|
|
|
internal override void Move(SLEntity parent, string id)
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
SLLog.Error($"Destroyed Link, {id}");
|
|
return;
|
|
}
|
|
|
|
this.parent[id] = null;
|
|
parent[id] = this;
|
|
}
|
|
|
|
protected override int GetEntityHashCode()
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
SLLog.Error($"Destroyed Link, {id}");
|
|
return 0;
|
|
}
|
|
|
|
return original.GetHashCode();
|
|
}
|
|
|
|
public override bool IsModified(string child)
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return original.IsModified(child);
|
|
}
|
|
|
|
public override void EndModified()
|
|
{
|
|
if (original.IsNullOrFalse())
|
|
{
|
|
return;
|
|
}
|
|
|
|
original.EndModified();
|
|
}
|
|
}
|
|
} |