using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Collections.ObjectModel; namespace UVC.Data { public class DataObject : Dictionary, IDataObject { // 직접적인 변경이 있었던 키를 저장하는 리스트 protected List changedProperies = new List(); public ReadOnlyCollection ChangedProperies => changedProperies.AsReadOnly(); // 기존에 있던 속성 키를 추적하기 위한 집합 private HashSet existingProperties = new HashSet(); public DataObject() { } public DataObject(JObject other) { // JObject로부터 속성을 복사 foreach (var prop in other.Properties()) { // JToken을 object로 변환 this[prop.Name] = ConvertJTokenToObject(prop.Value); existingProperties.Add(prop.Name); } } // Dictionary로 초기화하는 생성자 추가 public DataObject(Dictionary dictionary) : base(dictionary) { // 생성자에서 초기 속성들을 기존 속성으로 등록 foreach (var key in dictionary.Keys) { existingProperties.Add(key); } } // JToken을 실제 객체로 변환하는 헬퍼 메서드 private object ConvertJTokenToObject(JToken token) { switch (token.Type) { case JTokenType.String: return token.ToString(); case JTokenType.Integer: return token.ToObject(); case JTokenType.Float: return token.ToObject(); case JTokenType.Boolean: return token.ToObject(); case JTokenType.Object: return new DataObject((JObject)token); case JTokenType.Array: JArray array = (JArray)token; return new DataArray(array); default: return token.ToString(); } } /// /// 모든 프로퍼티를 변경된 것으로 표시합니다. /// public void ChangeAll() { changedProperies.Clear(); foreach (var key in this.Keys) { changedProperies.Add(key); } } /// /// 변경된 키 목록을 초기화합니다. /// public void ClearChangedKeys() { changedProperies.Clear(); } /// /// 속성이 변경될 때 호출되는 메서드입니다. /// protected virtual void OnPropertyChanged(string propertyName) { // 기존에 존재하던 속성인 경우에만 변경된 것으로 추적 if (existingProperties.Contains(propertyName)) { if (!changedProperies.Contains(propertyName)) { changedProperies.Add(propertyName); } } else { // 새로 추가된 속성은 기존 속성 목록에 추가 existingProperties.Add(propertyName); } } /// /// 새 속성을 추가할 때 이벤트 연결을 처리합니다. /// public new void Add(string propertyName, object value) { // 추가하기 전에 확인 - 속성이 이미 있는지 확인 bool isExisting = ContainsKey(propertyName); // 기본 구현 호출 base.Add(propertyName, value); // 새로 추가된 속성이면 기존 속성 목록에 추가 if (!isExisting) { existingProperties.Add(propertyName); } } /// /// 인덱서를 통한 속성 설정을 처리합니다. /// public new object this[string key] { get => base[key]; set { // 속성 설정 전에 기존에 있는 속성인지 확인 bool isExisting = ContainsKey(key) || existingProperties.Contains(key); // 기본 구현 호출 base[key] = value; // 기존 속성이었다면 변경된 것으로 처리 if (isExisting) { if (!changedProperies.Contains(key)) { changedProperies.Add(key); } } else { // 새로 추가된 속성은 기존 속성으로 등록 existingProperties.Add(key); } OnPropertyChanged(key); } } public int GetInt(string propertyName, int defaultValue = 0) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is int intValue) return intValue; return Convert.ToInt32(value); } return defaultValue; } public string GetString(string propertyName, string defaultValue = "") { if (TryGetValue(propertyName, out object value) && value != null) { return value.ToString(); } return defaultValue; } public bool GetBool(string propertyName, bool defaultValue = false) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is bool boolValue) return boolValue; return Convert.ToBoolean(value); } return defaultValue; } public float GetFloat(string propertyName, float defaultValue = 0f) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is float floatValue) return floatValue; return Convert.ToSingle(value); } return defaultValue; } public double GetDouble(string propertyName, double defaultValue = 0.0) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is double doubleValue) return doubleValue; return Convert.ToDouble(value); } return defaultValue; } public DateTime? GetDateTime(string propertyName, DateTime? defaultValue = null) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is DateTime dateTime) return dateTime; return Convert.ToDateTime(value); } return defaultValue; } public T GetEnum(string propertyName, T defaultValue = default) where T : Enum { if (TryGetValue(propertyName, out object value) && value != null) { if (value is T enumValue) return enumValue; return (T)Enum.Parse(typeof(T), value.ToString()); } return defaultValue; } public DataArray GetDataArray(string propertyName, DataArray? defaultValue = null) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is DataArray dataArray) return dataArray; if (value is JArray jArray) return new DataArray(jArray); } return defaultValue; } public DataObject GetDataObject(string propertyName, DataObject? defaultValue = null) { if (TryGetValue(propertyName, out object value) && value != null) { if (value is DataObject dataObject) return dataObject; if (value is JObject jObject) return new DataObject(jObject); if (value is Dictionary dict) return new DataObject(dict); } return defaultValue; } /// /// 속성이 제거될 때 기존 속성 목록에서도 제거합니다. /// public new bool Remove(string key) { bool result = base.Remove(key); if (result) { existingProperties.Remove(key); changedProperies.Remove(key); } return result; } /// /// 모든 속성을 제거할 때 기존 속성 목록과 변경된 키 목록도 초기화합니다. /// public void RemoveAll() { base.Clear(); existingProperties.Clear(); changedProperies.Clear(); } /// /// JObject로 변환 /// public JObject ToJObject() { JObject result = new JObject(); foreach (var kvp in this) { result[kvp.Key] = JToken.FromObject(kvp.Value); } return result; } public void SetDifferent(DataObject other) { changedProperies.Clear(); foreach (var keyValue in other) { if(!this.ContainsKey(keyValue.Key) || !this[keyValue.Key].Equals(keyValue.Value)) { this[keyValue.Key] = keyValue.Value; changedProperies.Add(keyValue.Key); } } } } }