using System.Xml; namespace SheetMe.Data.Stores; /// E_SctMst 저장 행 1건 — XML 트리 순회 산출물 public sealed class SctRow { /// 컨트롤 이름(SctObjNam) public string ObjName { get; init; } = string.Empty; /// 계층 알파벳 ID(SctObjID) — 15자 우측 공백 패딩 public string ObjId { get; init; } = string.Empty; /// 타입 풀네임(SctObjTyp) — AQN 의 첫 콤마 앞 public string ObjType { get; init; } = string.Empty; /// 부모 알파벳 ID(SctParObj) — 15자 우측 공백 패딩 public string ParentId { get; init; } = string.Empty; /// 표시 순서(SctObjSeq) — DisplaySequence 어트리뷰트 public string ObjSeq { get; init; } = string.Empty; /// 텍스트(SctObjTxt) — Text Property 원문 public string ObjText { get; init; } = string.Empty; } /// /// 서식 XML → E_SctMst 행 목록 변환 — 레거시 bzSaveSheetDesignNControlInfo.SaveE_SctMst 의 순수 이식. /// 모델이 아닌 '최종 직렬화된 XML'을 순회해 100% 패리티(Spread AsTemplate 특례 포함)를 보장한다. /// public static class SctMstXmlWalker { #region Methods /// 병합 XML 의 전체 Object 트리를 E_SctMst 행 목록으로 (루트 MDesignerHost 포함 — 레거시 규약) public static List Walk(string mergedXml) { var rows = new List(); var xml = new XmlDocument(); xml.LoadXml(mergedXml); var objId = "A"; foreach (XmlNode node in xml.DocumentElement!.ChildNodes) { if (node.Name.Equals("Object", StringComparison.Ordinal)) { AddRow(rows, (XmlElement)node, objId, string.Empty); WalkChildren(rows, (XmlElement)node, objId); } // 레거시 동작 보존: 시퀀스는 노드 종류와 무관하게 진행(Sheet 자식은 Object 뿐이라 동일) objId = NextAlphabetSequence(objId); } return rows; } /// 알파벳 시퀀스 증가 — A→B…Z→AA→AB…, 전부 Z 면 자릿수 확장(clsCommonLib 이식) public static string NextAlphabetSequence(string current) { var chars = current.ToCharArray(); var index = chars.Length - 1; while (index >= 0 && chars[index] == 'Z') { chars[index] = 'A'; index--; } if (index < 0) { return "A" + new string(chars); } chars[index]++; return new string(chars); } private static void WalkChildren(List rows, XmlElement parent, string parentObjId) { var childId = "AA"; foreach (XmlNode child in parent.ChildNodes) { if (!child.Name.Equals("Object", StringComparison.Ordinal)) { continue; } AddRow(rows, (XmlElement)child, parentObjId + childId, parentObjId); WalkChildren(rows, (XmlElement)child, parentObjId + childId); childId = NextAlphabetSequence(childId); } } private static void AddRow(List rows, XmlElement node, string objId, string parentId) { var name = node.GetAttribute("name"); var typeFullName = node.GetAttribute("type").Split(',')[0]; var displaySequence = node.GetAttribute("DisplaySequence"); var text = string.Empty; string? spreadControlType = null; string? spreadItemList = null; foreach (XmlNode child in node.ChildNodes) { if (!child.Name.Equals("Property", StringComparison.OrdinalIgnoreCase) || child is not XmlElement prop) { continue; } var propName = prop.GetAttribute("name"); if (propName.Equals("Text", StringComparison.OrdinalIgnoreCase)) { text = prop.InnerText; } else if (propName.Equals("ControlType", StringComparison.OrdinalIgnoreCase)) { spreadControlType = prop.InnerText; } else if (propName.Equals("ControlTypeAsTemplate_ItemLIST", StringComparison.OrdinalIgnoreCase)) { spreadItemList = prop.InnerText; } } rows.Add(new SctRow { ObjName = name, ObjId = objId.PadRight(15), ObjType = typeFullName, ParentId = parentId.PadRight(15), ObjSeq = displaySequence, ObjText = text, }); // Spread AsTemplate 특례 — 항목 목록을 개별 행으로(레거시 이식, Chr(5) 구분) if (string.Equals(typeFullName, "M.EMR.UserControl.Spread", StringComparison.OrdinalIgnoreCase) && string.Equals(spreadControlType, "AsTemplate", StringComparison.OrdinalIgnoreCase) && spreadItemList is not null) { var items = spreadItemList.Split((char)5); for (var i = 0; i < items.Length - 1; i++) { rows.Add(new SctRow { ObjName = items[i], ObjId = i.ToString(), ObjType = "ItemOfSpreadAsTemplate", ParentId = string.Empty, ObjSeq = string.Empty, ObjText = i.ToString().PadLeft(3, '0') + "_" + items[i], }); } } } #endregion }