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 attributes; private HashSet removed; private List 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 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(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(); } 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(); 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(); 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(); 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(); Modified(attributeKey); removed.Add(attributeKey); if (IsExist() == false) { if (parent is null == false) { parent[id] = null; } } } else // 추가 { if (attributes == null) attributes = new Dictionary(); 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(); 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 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(); 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 GetEnumerator() { if (original.IsNullOrFalse()) return Enumerable.Empty().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(); } } }