2025-06-05 20:09:28 +09:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
|
|
namespace UVC.Data
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// DataObject 객체 컬렉션의 변경사항을 추적하는 데이터 배열 클래스
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class DataArray : List<DataObject>, IDataObject
|
|
|
|
|
|
{
|
|
|
|
|
|
// 추가 된 항목 목록
|
|
|
|
|
|
protected List<DataObject> addedList = new List<DataObject>();
|
|
|
|
|
|
// 제거 된 항목 목록
|
|
|
|
|
|
protected List<DataObject> removedList = new List<DataObject>();
|
|
|
|
|
|
// 수정 된 항목 목록
|
|
|
|
|
|
protected List<DataObject> modifiedList = new List<DataObject>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 추가된 항목에 접근할 수 있는 읽기 전용 컬렉션
|
|
|
|
|
|
public ReadOnlyCollection<DataObject> AddedItems => addedList.AsReadOnly();
|
|
|
|
|
|
|
|
|
|
|
|
// 제거된 항목에 접근할 수 있는 읽기 전용 컬렉션
|
|
|
|
|
|
public ReadOnlyCollection<DataObject> RemovedItems => removedList.AsReadOnly();
|
|
|
|
|
|
|
|
|
|
|
|
// 제거된 항목에 접근할 수 있는 읽기 전용 컬렉션
|
|
|
|
|
|
public ReadOnlyCollection<DataObject> ModifiedList => modifiedList.AsReadOnly();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 변경 추적 활성화 여부
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsChangeTracking { get; set; } = false;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 기본 생성자
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public DataArray() : base()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 초기 용량을 지정하는 생성자
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="capacity">초기 용량</param>
|
|
|
|
|
|
public DataArray(int capacity) : base(capacity)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 기존 컬렉션으로부터 생성하는 생성자
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="collection">초기 항목을 포함하는 컬렉션</param>
|
|
|
|
|
|
public DataArray(IEnumerable<DataObject> collection) : base(collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public DataArray(string jsonString) : base()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!string.IsNullOrEmpty(jsonString))
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
JArray jArray = JArray.Parse(jsonString);
|
|
|
|
|
|
foreach (var item in jArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
Add(ConvertToDataObject(item));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new ArgumentException("Invalid JSON string format.", nameof(jsonString), ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// JArray로부터 DataArray를 생성하는 생성자
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="jArray">JSON 배열</param>
|
|
|
|
|
|
public DataArray(JArray jArray) : base()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (jArray != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var item in jArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
Add(ConvertToDataObject(item));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// JToken을 DataObject로 변환합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private DataObject ConvertToDataObject(JToken token)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (token.Type == JTokenType.Object)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new DataObject((JObject)token);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// JObject가 아닌 경우, 새 DataObject를 만들고 값을 넣어줍니다
|
|
|
|
|
|
var dataObject = new DataObject();
|
|
|
|
|
|
dataObject.Add("value", ConvertJTokenToObject(token));
|
|
|
|
|
|
return dataObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private object ConvertJTokenToObject(JToken token)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (token.Type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case JTokenType.String:
|
|
|
|
|
|
return token.ToString();
|
|
|
|
|
|
case JTokenType.Integer:
|
|
|
|
|
|
return token.ToObject<int>();
|
|
|
|
|
|
case JTokenType.Float:
|
|
|
|
|
|
return token.ToObject<float>();
|
|
|
|
|
|
case JTokenType.Boolean:
|
|
|
|
|
|
return token.ToObject<bool>();
|
|
|
|
|
|
case JTokenType.Object:
|
|
|
|
|
|
return new DataObject((JObject)token);
|
|
|
|
|
|
case JTokenType.Array:
|
|
|
|
|
|
JArray array = (JArray)token;
|
|
|
|
|
|
return new DataArray(array);
|
|
|
|
|
|
default:
|
|
|
|
|
|
return token.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-06 02:17:54 +09:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 모든 아이템이 추가 된것으로 표시합니다.
|
|
|
|
|
|
/// 전체 데이터가 갱신되었을 때 사용합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void InitData()
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Clear();
|
|
|
|
|
|
addedList.AddRange(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 다른 DataObject와 현재 객체를 비교하여 다른 부분만 설정합니다.
|
|
|
|
|
|
/// 변경된 키는 자동으로 추적됩니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="other">비교할 DataObject</param>
|
|
|
|
|
|
public void UpdateDifferent(IDataObject other)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (other == null) return;
|
|
|
|
|
|
if (other is DataArray otherArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Clear();
|
|
|
|
|
|
removedList.Clear();
|
|
|
|
|
|
modifiedList.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
// 현재 DataArray와 비교하여 변경된 항목을 추적
|
|
|
|
|
|
for (int i = 0; i < Math.Max(this.Count, otherArray.Count); i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i < this.Count && i < otherArray.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!this[i].ToString().Equals(otherArray[i].ToString()))
|
|
|
|
|
|
{
|
|
|
|
|
|
modifiedList.Add(this[i]);
|
|
|
|
|
|
this[i].UpdateDifferent(otherArray[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (i < this.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
removedList.Add(this[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (i < otherArray.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Add(otherArray[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 업데이트 된 객체를 반환합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public IDataObject GetUpdatedObject()
|
|
|
|
|
|
{
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-06-05 20:09:28 +09:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 항목을 추가합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="item">추가할 항목</param>
|
|
|
|
|
|
public new void Add(DataObject item)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.Add(item);
|
|
|
|
|
|
|
|
|
|
|
|
// 변경 사항을 추적하고 이벤트 발생
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
int index = Count - 1;
|
|
|
|
|
|
addedList.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 항목을 제거합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="item">제거할 항목</param>
|
|
|
|
|
|
/// <returns>제거 성공 여부</returns>
|
|
|
|
|
|
public new bool Remove(DataObject item)
|
|
|
|
|
|
{
|
|
|
|
|
|
int index = IndexOf(item);
|
|
|
|
|
|
if (index >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveAt(index);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 지정된 인덱스의 항목을 제거합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="index">제거할 항목의 인덱스</param>
|
|
|
|
|
|
public new void RemoveAt(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (index < 0 || index >= Count)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
|
|
|
|
|
|
|
|
DataObject removedItem = this[index];
|
|
|
|
|
|
base.RemoveAt(index);
|
|
|
|
|
|
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
removedList.Add(removedItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 컬렉션의 모든 항목을 제거합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public new void Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var oldItems = new List<DataObject>(this);
|
|
|
|
|
|
base.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 모든 항목을 제거 목록에 추가
|
|
|
|
|
|
removedList.AddRange(oldItems);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 지정된 인덱스에 항목을 삽입합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="index">삽입할 위치</param>
|
|
|
|
|
|
/// <param name="item">삽입할 항목</param>
|
|
|
|
|
|
public new void Insert(int index, DataObject item)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.Insert(index, item);
|
|
|
|
|
|
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 지정된 인덱스의 항목을 교체합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="index">교체할 위치</param>
|
|
|
|
|
|
/// <param name="item">새 항목</param>
|
|
|
|
|
|
public new DataObject this[int index]
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return base[index]; }
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (index < 0 || index >= Count)
|
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
|
|
|
|
|
|
|
|
DataObject oldItem = base[index];
|
|
|
|
|
|
base[index] = value;
|
|
|
|
|
|
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Add(value);
|
|
|
|
|
|
removedList.Add(oldItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 범위의 항목을 추가합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="collection">추가할 항목의 컬렉션</param>
|
|
|
|
|
|
public new void AddRange(IEnumerable<DataObject> collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (collection == null)
|
|
|
|
|
|
throw new ArgumentNullException(nameof(collection));
|
|
|
|
|
|
|
|
|
|
|
|
int startIndex = Count;
|
|
|
|
|
|
base.AddRange(collection);
|
|
|
|
|
|
|
|
|
|
|
|
if (IsChangeTracking)
|
|
|
|
|
|
{
|
|
|
|
|
|
var items = collection.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < items.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Add(items[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 변경된 인덱스 목록을 초기화합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ResetChangedIndices()
|
|
|
|
|
|
{
|
|
|
|
|
|
addedList.Clear();
|
|
|
|
|
|
removedList.Clear();
|
|
|
|
|
|
modifiedList.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 변경 내역을 저장하지 않고 항목을 추가합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="item">추가할 항목</param>
|
|
|
|
|
|
public void AddWithoutTracking(DataObject item)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool oldTracking = IsChangeTracking;
|
|
|
|
|
|
IsChangeTracking = false;
|
|
|
|
|
|
Add(item);
|
|
|
|
|
|
IsChangeTracking = oldTracking;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// DataArray를 JArray로 변환합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public JArray ToJArray()
|
|
|
|
|
|
{
|
|
|
|
|
|
JArray array = new JArray();
|
|
|
|
|
|
foreach (var item in this)
|
|
|
|
|
|
{
|
|
|
|
|
|
array.Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
return array;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// DataArray를 JArray로 암시적 변환합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static implicit operator JArray(DataArray dataArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dataArray.ToJArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// JArray를 DataArray로 암시적 변환합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static implicit operator DataArray(JArray jArray)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new DataArray(jArray);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|