초기 커밋: SheetMe 서식생성기 (P0~P5 완료 상태)
레거시 서식생성기(VB.NET WinForms) 대체용 C#/.NET 10 WPF 디자이너. 기준선: 실DB 활성 디자인 1,271건 왕복 의미론 diff 0 / 예외 0, 단위 테스트 49/49. 이 커밋에 함께 포함된 자격증명 분리: - appsettings.json 을 __HOST__/__PASSWORD__ 플레이스홀더로 전환 - 실접속 정보는 appsettings.Development.json 으로 분리(.gitignore 제외, csproj Debug 조건부 복사라 Release 산출물에 실리지 않음) - ConfigLoader 를 환경변수 > Development > appsettings 순 레이어링으로 변경, 미치환 플레이스홀더는 '미설정'으로 간주 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
using System.Xml;
|
||||
|
||||
namespace SheetMe.Data.Stores;
|
||||
|
||||
/// <summary>E_SctMst 저장 행 1건 — XML 트리 순회 산출물</summary>
|
||||
public sealed class SctRow
|
||||
{
|
||||
/// <summary>컨트롤 이름(SctObjNam)</summary>
|
||||
public string ObjName { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>계층 알파벳 ID(SctObjID) — 15자 우측 공백 패딩</summary>
|
||||
public string ObjId { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>타입 풀네임(SctObjTyp) — AQN 의 첫 콤마 앞</summary>
|
||||
public string ObjType { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>부모 알파벳 ID(SctParObj) — 15자 우측 공백 패딩</summary>
|
||||
public string ParentId { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>표시 순서(SctObjSeq) — DisplaySequence 어트리뷰트</summary>
|
||||
public string ObjSeq { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>텍스트(SctObjTxt) — Text Property 원문</summary>
|
||||
public string ObjText { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 서식 XML → E_SctMst 행 목록 변환 — 레거시 bzSaveSheetDesignNControlInfo.SaveE_SctMst 의 순수 이식.
|
||||
/// 모델이 아닌 '최종 직렬화된 XML'을 순회해 100% 패리티(Spread AsTemplate 특례 포함)를 보장한다.
|
||||
/// </summary>
|
||||
public static class SctMstXmlWalker
|
||||
{
|
||||
#region Methods
|
||||
/// <summary>병합 XML 의 전체 Object 트리를 E_SctMst 행 목록으로 (루트 MDesignerHost 포함 — 레거시 규약)</summary>
|
||||
public static List<SctRow> Walk(string mergedXml)
|
||||
{
|
||||
var rows = new List<SctRow>();
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>알파벳 시퀀스 증가 — A→B…Z→AA→AB…, 전부 Z 면 자릿수 확장(clsCommonLib 이식)</summary>
|
||||
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<SctRow> 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<SctRow> 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
|
||||
}
|
||||
Reference in New Issue
Block a user