using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel; // 参照: Microsoft.Office.Interop.Excel
namespace LegacyFormDoc
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// this.button1.Click += button1_Click; // デザイナで結線済みなら不要
}
private void button1_Click(object sender, EventArgs e)
{
try
{
LegacyScanner.Run();
MessageBox.Show("解析完了:出力フォルダを開きます。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Process.Start(LegacyScanner.OutputDir);
}
catch (Exception ex)
{
MessageBox.Show("実行時エラー: " + ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
internal static class LegacyScanner
{
// ★ 固定対象パス(必要に応じて変更)
private const string TARGET_ROOT = @"C:\Repo\LegacyApp";
internal static readonly string OutputDir = Path.Combine(TARGET_ROOT, "_scanout");
private static readonly string[] BaseProps = new[]
{
"Text","TabIndex","Dock","Anchor","Enabled","Visible","ReadOnly","MaxLength","Location","Size","Tag"
};
// ===== 正規表現 =====
private static readonly Regex ReClassHead = new Regex(@"\bclass\s+(\w+)\b",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReInit = new Regex(@"void\s+InitializeComponent\s*\(",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReNew = new Regex(@"(?:this\.)?(\w+)\s*=\s*new\s+([\w\.\<\>]+)\s*\(",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReTextStr = new Regex(@"(?:this\.)?(\w+)\.Text\s*=\s*""([^""]*)""\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
// 右辺にダブルクォートがある行は除外(文字列リテラルと重複させない)
private static readonly Regex ReTextExp = new Regex(
@"(?:this\.)?(\w+)\.Text\s*=\s*(?![^;]*"")([^;]+?)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReTab = new Regex(@"(?:this\.)?(\w+)\.TabIndex\s*=\s*(\d+)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReDock = new Regex(@"(?:this\.)?(\w+)\.Dock\s*=\s*([\w\.]+)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReAnchor = new Regex(@"(?:this\.)?(\w+)\.Anchor\s*=\s*([\w\|\s\.]+)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReBool = new Regex(@"(?:this\.)?(\w+)\.(Enabled|Visible|ReadOnly)\s*=\s*(true|false)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReMaxLen = new Regex(@"(?:this\.)?(\w+)\.MaxLength\s*=\s*(\d+)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReLoc = new Regex(@"(?:this\.)?(\w+)\.Location\s*=\s*new\s+System\.Drawing\.Point\s*\(\s*(-?\d+)\s*,\s*(-?\d+)\s*\)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReSize = new Regex(@"(?:this\.)?(\w+)\.Size\s*=\s*new\s+System\.Drawing\.Size\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReTagStr = new Regex(@"(?:this\.)?(\w+)\.Tag\s*=\s*""([^""]*)""\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex ReTagExp = new Regex(@"(?:this\.)?(\w+)\.Tag\s*=\s*(?![^;]*"")([^;]+?)\s*;",
RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);
// ===== モデル =====
private sealed class ControlRow { public string File; public string Class; public string ControlId; public string Type; public int Line; }
private sealed class PropRow { public string File; public string Class; public string ControlId; public string Prop; public string Value; public int Line; }
private sealed class ClassSpan { public string Name; public int Open1; public int Close1; }
public static void Run()
{
if (!Directory.Exists(TARGET_ROOT))
throw new DirectoryNotFoundException("対象パスが存在しません: " + TARGET_ROOT);
Directory.CreateDirectory(OutputDir);
var controls = new List<ControlRow>();
var props = new List<PropRow>();
var propUniq = new HashSet<string>(StringComparer.Ordinal); // 重複排除
foreach (var path in EnumerateFiles(TARGET_ROOT, "*.cs"))
{
string full = SafeReadAllText(path);
if (string.IsNullOrEmpty(full)) continue;
var classSpans = BuildClassSpans(full);
foreach (Match m in ReInit.Matches(full))
{
int headEnd = m.Index + m.Length;
int blockStart = FindNextOpenBraceStrict(full, headEnd);
if (blockStart <= 0) continue;
int blockEnd = FindMatchingBraceStrict(full, blockStart);
if (blockEnd <= 0) continue;
string block = full.Substring(blockStart - 1, (blockEnd - blockStart + 1));
int baseLine = LineOfPos(full, blockStart);
string cls = ResolveEnclosingClass(classSpans, blockStart, blockEnd);
if (string.IsNullOrEmpty(cls))
{
var mhead = ReClassHead.Match(full);
cls = mhead.Success ? mhead.Groups[1].Value : "";
}
foreach (Match mm in ReNew.Matches(block))
{
controls.Add(new ControlRow
{
File = path,
Class = cls,
ControlId = mm.Groups[1].Value,
Type = mm.Groups[2].Value,
Line = baseLine + EstimateLineFrom0(block, mm.Index) - 1
});
}
AddProp(props, propUniq, ReTextStr, block, path, cls, "Text", baseLine);
AddProp(props, propUniq, ReTextExp, block, path, cls, "Text", baseLine);
AddProp(props, propUniq, ReTab, block, path, cls, "TabIndex", baseLine);
AddProp(props, propUniq, ReDock, block, path, cls, "Dock", baseLine);
AddProp(props, propUniq, ReAnchor, block, path, cls, "Anchor", baseLine);
AddProp2(props,propUniq, ReBool, block, path, cls, baseLine);
AddProp(props, propUniq, ReMaxLen, block, path, cls, "MaxLength", baseLine);
AddPropPoint(props,propUniq, ReLoc, block, path, cls, "Location", baseLine);
AddPropSize(props, propUniq, ReSize,block, path, cls, "Size", baseLine);
AddProp(props, propUniq, ReTagStr, block, path, cls, "Tag", baseLine);
AddProp(props, propUniq, ReTagExp, block, path, cls, "Tag", baseLine);
}
}
// 参考CSV(要件はExcel整形だが従来出力も維持)
WriteControlsCsv(controls);
WritePropsCsv(props);
WriteMatrixCsv_ForReference(controls, props);
// Excel(A3以降:枠線・フィルター・全セル非折返し・AutoFit)
WriteMatrixExcelFormatted(controls, props);
}
// ==== クラススパン解析 ====
private static List<ClassSpan> BuildClassSpans(string full)
{
var spans = new List<ClassSpan>();
var matches = ReClassHead.Matches(full);
foreach (Match m in matches)
{
string name = m.Groups[1].Value;
int braceOpen = FindNextOpenBraceStrict(full, m.Index + m.Length);
if (braceOpen <= 0) continue;
int braceClose = FindMatchingBraceStrict(full, braceOpen);
if (braceClose <= 0) continue;
spans.Add(new ClassSpan { Name = name, Open1 = braceOpen, Close1 = braceClose });
}
spans.Sort((a, b) => a.Open1.CompareTo(b.Open1));
return spans;
}
private static string ResolveEnclosingClass(List<ClassSpan> spans, int methodOpen1, int methodClose1)
{
for (int i = spans.Count - 1; i >= 0; i--)
{
var s = spans[i];
if (s.Open1 <= methodOpen1 && methodClose1 <= s.Close1)
return s.Name;
}
return "";
}
// ==== CSV(参考。全セル ="..." で文字列固定) ====
private static void WriteControlsCsv(List<ControlRow> controls)
{
string path = Path.Combine(OutputDir, "Controls.csv");
using (var sw = new StreamWriter(path, false, Encoding.GetEncoding(932)))
{
sw.WriteLine(string.Join(",", CsvText("File"), CsvText("Class"), CsvText("ControlId"), CsvText("Type"), CsvText("Line")));
foreach (var c in controls)
{
sw.WriteLine(string.Join(",",
CsvText(c.File), CsvText(c.Class), CsvText(c.ControlId), CsvText(c.Type), CsvText(c.Line.ToString())));
}
}
}
private static void WritePropsCsv(List<PropRow> props)
{
string path = Path.Combine(OutputDir, "Props.csv");
using (var sw = new StreamWriter(path, false, Encoding.GetEncoding(932)))
{
sw.WriteLine(string.Join(",", CsvText("File"), CsvText("Class"), CsvText("ControlId"), CsvText("Prop"), CsvText("Value"), CsvText("Line")));
foreach (var p in props)
{
sw.WriteLine(string.Join(",",
CsvText(p.File), CsvText(p.Class), CsvText(p.ControlId), CsvText(p.Prop), CsvText(p.Value), CsvText(p.Line.ToString())));
}
}
}
private static void WriteMatrixCsv_ForReference(List<ControlRow> controls, List<PropRow> props)
{
string path = Path.Combine(OutputDir, "Matrix.csv");
var propSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var b in BaseProps) propSet.Add(b);
foreach (var p in props.Select(x => x.Prop)) if (!string.IsNullOrEmpty(p)) propSet.Add(p);
var propList = propSet.ToList();
var groups = controls
.GroupBy(c => c.File + "|" + c.Class)
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.OrdinalIgnoreCase);
using (var sw = new StreamWriter(path, false, Encoding.GetEncoding(932)))
{
bool firstSection = true;
foreach (var kv in groups)
{
if (!firstSection) sw.WriteLine();
firstSection = false;
var parts = kv.Key.Split('|');
string file = parts.Length > 0 ? parts[0] : "";
string cls = parts.Length > 1 ? parts[1] : "";
sw.WriteLine(string.Join(",", CsvText("FILE"), CsvText(file)));
sw.WriteLine(string.Join(",", CsvText("Class"), CsvText(cls)));
sw.Write(string.Join(",", CsvText("ControlId"), CsvText("Type")));
foreach (var col in propList) sw.Write("," + CsvText(col));
sw.WriteLine();
var rowKeys = new SortedDictionary<string, ControlRow>(StringComparer.OrdinalIgnoreCase);
foreach (var c in kv.Value)
if (!rowKeys.ContainsKey(c.ControlId)) rowKeys.Add(c.ControlId, c);
var propMap = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
foreach (var c in kv.Value)
{
var pick = props.Where(p => p.File == file && p.Class == cls && p.ControlId == c.ControlId);
foreach (var pr in pick)
{
if (!propMap.ContainsKey(c.ControlId)) propMap[c.ControlId] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string cur;
if (propMap[c.ControlId].TryGetValue(pr.Prop, out cur))
{
if (!string.Equals(cur, pr.Value, StringComparison.Ordinal))
propMap[c.ControlId][pr.Prop] = cur + ", " + pr.Value;
}
else
{
propMap[c.ControlId][pr.Prop] = pr.Value ?? "";
}
}
}
foreach (var row in rowKeys)
{
var c = row.Value;
sw.Write(string.Join(",", CsvText(c.ControlId), CsvText(c.Type)));
foreach (var col in propList)
{
string v = (propMap.ContainsKey(c.ControlId) && propMap[c.ControlId].ContainsKey(col))
? propMap[c.ControlId][col] : "";
sw.Write("," + CsvText(v));
}
sw.WriteLine();
}
}
}
}
//==== Excel(A3以降:枠線・フィルター・全セル非折返し・AutoFit・改行除去) ====
private static void WriteMatrixExcelFormatted(List<ControlRow> controls, List<PropRow> props)
{
var propSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var b in BaseProps) propSet.Add(b);
foreach (var p in props.Select(x => x.Prop)) if (!string.IsNullOrEmpty(p)) propSet.Add(p);
var propList = propSet.ToList();
var groups = controls
.GroupBy(c => c.File + "|" + c.Class)
.Select(g => new { Key = g.Key, Items = g.ToList() })
.ToList();
string xlsx = Path.Combine(OutputDir, "Matrix.xlsx");
Excel.Application app = null;
Excel.Workbook wb = null;
try
{
app = new Excel.Application();
app.DisplayAlerts = false;
wb = app.Workbooks.Add();
var usedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (groups.Count == 0)
{
Excel.Worksheet ws0 = (Excel.Worksheet)wb.Worksheets[1];
ws0.Cells.NumberFormat = "@";
ws0.Cells.Clear();
ws0.Name = UniqueSheetName(usedNames, "Matrix");
ws0.Cells[1, 1] = "FILE";
ws0.Cells[1, 2] = "(no data)";
ws0.Cells[2, 1] = "Class";
ws0.Cells[2, 2] = "(no data)";
ws0.Cells[3, 1] = "ControlId";
ws0.Cells[3, 2] = "Type";
Excel.Range header0 = ws0.Range[ws0.Cells[3, 1], ws0.Cells[3, 2]];
header0.Font.Bold = true;
ws0.Cells.WrapText = false; // 非折返し
((Excel.Range)ws0.Columns).ColumnWidth = 8; // 初期
((Excel.Range)ws0.Columns[1]).AutoFit();
((Excel.Range)ws0.Columns[2]).AutoFit();
wb.SaveAs(xlsx);
return;
}
for (int gi = 0; gi < groups.Count; gi++)
{
var grp = groups[gi];
var parts = grp.Key.Split('|');
string file = parts.Length > 0 ? parts[0] : "";
string cls = parts.Length > 1 ? parts[1] : "";
string baseName = string.IsNullOrEmpty(cls) ? Path.GetFileNameWithoutExtension(file) : cls;
string sheetName = UniqueSheetName(usedNames, baseName);
Excel.Worksheet ws;
if (gi == 0)
{
ws = (Excel.Worksheet)wb.Worksheets[1];
ws.Cells.Clear();
}
else
{
ws = (Excel.Worksheet)wb.Worksheets.Add(After: wb.Worksheets[wb.Worksheets.Count]);
}
ws.Name = sheetName;
// 全セル:文字列・非折返し
ws.Cells.NumberFormat = "@";
ws.Cells.WrapText = false;
// A1:FILE, A2:Class(改行を除去して書き込み)
ws.Cells[1, 1] = "FILE";
ws.Cells[1, 2] = Sanitize(file);
ws.Cells[2, 1] = "Class";
ws.Cells[2, 2] = Sanitize(cls);
// A3: ヘッダ
int headerRow = 3;
int col = 1;
ws.Cells[headerRow, col++] = "ControlId";
ws.Cells[headerRow, col++] = "Type";
foreach (var pcol in propList)
ws.Cells[headerRow, col++] = pcol;
// 行キー:ControlId
var rowKeys = new SortedDictionary<string, ControlRow>(StringComparer.OrdinalIgnoreCase);
foreach (var c in grp.Items)
if (!rowKeys.ContainsKey(c.ControlId)) rowKeys.Add(c.ControlId, c);
// props 集計
var propMap = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
foreach (var c in grp.Items)
{
var pick = props.Where(p => p.File == file && p.Class == cls && p.ControlId == c.ControlId);
foreach (var pr in pick)
{
if (!propMap.ContainsKey(c.ControlId)) propMap[c.ControlId] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string cur;
if (propMap[c.ControlId].TryGetValue(pr.Prop, out cur))
{
if (!string.Equals(cur, pr.Value, StringComparison.Ordinal))
propMap[c.ControlId][pr.Prop] = cur + ", " + pr.Value;
}
else
{
propMap[c.ControlId][pr.Prop] = pr.Value ?? "";
}
}
}
// データ書き込み(A4~)※改行・タブを除去
int row = headerRow + 1;
foreach (var k in rowKeys.Keys)
{
var c = rowKeys[k];
col = 1;
ws.Cells[row, col++] = Sanitize(c.ControlId);
ws.Cells[row, col++] = Sanitize(c.Type);
foreach (var pcol in propList)
{
string v = (propMap.ContainsKey(c.ControlId) && propMap[c.ControlId].ContainsKey(pcol))
? propMap[c.ControlId][pcol] : "";
ws.Cells[row, col++] = Sanitize(v);
}
row++;
}
int lastRow = Math.Max(headerRow, row - 1);
int lastCol = 2 + propList.Count;
// 枠線(A3~最終セル)
Excel.Range rng = ws.Range[ws.Cells[headerRow, 1], ws.Cells[lastRow, lastCol]];
rng.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
rng.Borders.Weight = Excel.XlBorderWeight.xlThin;
rng.WrapText = false; // 念押し
// フィルター(A3 見出し行)
Excel.Range header = ws.Range[ws.Cells[headerRow, 1], ws.Cells[headerRow, lastCol]];
header.AutoFilter(1, Type.Missing, Excel.XlAutoFilterOperator.xlAnd, Type.Missing, true);
header.Font.Bold = true;
// 初期幅を小さく→AutoFit(全列)
((Excel.Range)ws.Columns).ColumnWidth = 8;
Excel.Range used = ws.UsedRange;
used.Columns.AutoFit();
used.Rows.AutoFit(); // 非折返しだが行高も最適化
// 先頭列/2列目は最小幅を確保(見やすさ優先)
var col1 = (Excel.Range)ws.Columns[1];
var col2 = (Excel.Range)ws.Columns[2];
col1.ColumnWidth = Math.Max(12, col1.ColumnWidth);
col2.ColumnWidth = Math.Max(18, col2.ColumnWidth);
// A1/A2 見出し太字
((Excel.Range)ws.Cells[1, 1]).Font.Bold = true;
((Excel.Range)ws.Cells[2, 1]).Font.Bold = true;
// A3 を選択
((Excel.Range)ws.Cells[3, 1]).Select();
}
wb.SaveAs(xlsx);
}
finally
{
if (wb != null) { wb.Close(false); System.Runtime.InteropServices.Marshal.ReleaseComObject(wb); }
if (app != null) { app.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(app); }
}
}
// 改行・タブの除去+空白正規化(セルは1行で表示)
private static string Sanitize(string s)
{
if (string.IsNullOrEmpty(s)) return "";
s = s.Replace("\r", " ").Replace("\n", " ").Replace("\t", " ");
// 連続空白を1個に
while (s.Contains(" ")) s = s.Replace(" ", " ");
return s.Trim();
}
// シート名の安全化+重複時の連番付与
private static string UniqueSheetName(HashSet<string> used, string baseName)
{
string name = MakeSafeSheetName(baseName);
if (!used.Add(name))
{
int n = 2;
while (!used.Add(name + "_" + n)) n++;
name = name + "_" + n;
}
return name;
}
private static string MakeSafeSheetName(string name)
{
if (string.IsNullOrEmpty(name)) name = "Sheet";
foreach (var ch in new[] { '\\', '/', '?', '*', '[', ']', ':', '\'' })
name = name.Replace(ch.ToString(), "_");
if (name.Length > 31) name = name.Substring(0, 31);
if (name.EndsWith(".")) name = name.TrimEnd('.');
if (string.IsNullOrEmpty(name)) name = "Sheet";
return name;
}
//==== 文字列として CSV セルに書く(参考出力用)
private static string CsvText(string s)
{
if (s == null) s = "";
string inner = "=\"" + s.Replace("\"", "\"\"") + "\""; // ="..."
string csv = "\"" + inner.Replace("\"", "\"\"") + "\"";
return csv;
}
//==== 解析基盤 ====
private static IEnumerable<string> EnumerateFiles(string root, string pattern)
{
var stack = new Stack<string>();
stack.Push(root);
while (stack.Count > 0)
{
var dir = stack.Pop();
string[] subdirs;
try { subdirs = Directory.GetDirectories(dir); }
catch { continue; }
foreach (var d in subdirs) stack.Push(d);
string[] files;
try { files = Directory.GetFiles(dir, pattern); }
catch { continue; }
foreach (var f in files) yield return f;
}
}
private static string SafeReadAllText(string path)
{
try { return File.ReadAllText(path, DetectEncoding(path)); }
catch { return ""; }
}
private static Encoding DetectEncoding(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var bom = new byte[Math.Min(4, fs.Length)];
fs.Read(bom, 0, bom.Length);
if (bom.Length >= 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) return new UTF8Encoding(true);
if (bom.Length >= 2 && bom[0] == 0xFF && bom[1] == 0xFE) return Encoding.Unicode; // UTF-16LE
}
return Encoding.Default; // CP932 想定
}
private static int LineOfPos(string txt, int pos1Based)
{
if (pos1Based < 1) return 1;
int cnt = 1;
for (int i = 0; i < pos1Based && i < txt.Length; i++)
if (txt[i] == '\n') cnt++;
return cnt;
}
private static int EstimateLineFrom0(string block, int zeroBasedPos)
{
int pos = zeroBasedPos;
if (pos < 0) return 1;
int cnt = 1;
for (int i = 0; i <= pos && i < block.Length; i++)
if (block[i] == '\n') cnt++;
return cnt;
}
//==== ブレース探索(コメント/文字列/逐語的文字列/文字リテラル対応) ====
private static int FindNextOpenBraceStrict(string s, int startPos1Based)
{
bool inSL = false, inML = false, inStr = false, inChar = false, verb = false;
for (int i = startPos1Based - 1; i < s.Length; i++)
{
char c = s[i];
char nxt = (i + 1 < s.Length) ? s[i + 1] : '\0';
if (inSL) { if (c == '\r' || c == '\n') inSL = false; continue; }
if (inML) { if (c == '*' && nxt == '/') { inML = false; i++; } continue; }
if (inStr)
{
if (verb) { if (c == '"' && nxt == '"') { i++; continue; } if (c == '"') { inStr = false; verb = false; } continue; }
else { if (c == '\\') { i++; continue; } if (c == '"') { inStr = false; } continue; }
}
if (inChar) { if (c == '\\') { i++; continue; } if (c == '\'') { inChar = false; } continue; }
if (c == '/' && nxt == '/') { inSL = true; i++; continue; }
if (c == '/' && nxt == '*') { inML = true; i++; continue; }
if (c == '"')
{
char prev = (i > 0) ? s[i - 1] : '\0';
if (prev == '@') { inStr = true; verb = true; } else { inStr = true; verb = false; }
continue;
}
if (c == '\'') { inChar = true; continue; }
if (c == '{') return i + 1; // 1-based
}
return 0;
}
private static int FindMatchingBraceStrict(string s, int posOpen1Based)
{
bool inSL = false, inML = false, inStr = false, inChar = false, verb = false;
int depth = 0;
for (int i = posOpen1Based - 1; i < s.Length; i++)
{
char c = s[i];
char nxt = (i + 1 < s.Length) ? s[i + 1] : '\0';
if (inSL) { if (c == '\r' || c == '\n') inSL = false; continue; }
if (inML) { if (c == '*' && nxt == '/') { inML = false; i++; } continue; }
if (inStr)
{
if (verb) { if (c == '"' && nxt == '"') { i++; continue; } if (c == '"') { inStr = false; verb = false; } continue; }
else { if (c == '\\') { i++; continue; } if (c == '"') { inStr = false; } continue; }
}
if (inChar) { if (c == '\\') { i++; continue; } if (c == '\'') { inChar = false; } continue; }
if (c == '/' && nxt == '/') { inSL = true; i++; continue; }
if (c == '/' && nxt == '*') { inML = true; i++; continue; }
if (c == '"')
{
char prev = (i > 0) ? s[i - 1] : '\0';
if (prev == '@') { inStr = true; verb = true; } else { inStr = true; verb = false; }
continue;
}
if (c == '\'') { inChar = true; continue; }
if (c == '{') depth++;
else if (c == '}')
{
depth--;
if (depth == 0) return i + 1; // 1-based
}
}
return 0;
}
//==== プロパティ抽出(重複排除つき) ====
private static void AddProp(List<PropRow> list, HashSet<string> uniq, Regex re, string block, string file, string cls, string propName, int baseLine)
{
foreach (Match m in re.Matches(block))
{
if (!m.Success || m.Groups.Count < 3) continue;
string id = m.Groups[1].Value ?? "";
string val = (m.Groups[2].Value ?? "").Trim();
int line = baseLine + EstimateLineFrom0(block, m.Index) - 1;
string key = file + "|" + cls + "|" + id + "|" + propName + "|" + val + "|" + line.ToString();
if (!uniq.Add(key)) continue;
list.Add(new PropRow { File = file, Class = cls, ControlId = id, Prop = propName, Value = val, Line = line });
}
}
private static void AddProp2(List<PropRow> list, HashSet<string> uniq, Regex re, string block, string file, string cls, int baseLine)
{
foreach (Match m in re.Matches(block))
{
if (!m.Success || m.Groups.Count < 4) continue;
string id = m.Groups[1].Value ?? "";
string prop = m.Groups[2].Value ?? "";
string val = (m.Groups[3].Value ?? "").ToLowerInvariant().Trim();
int line = baseLine + EstimateLineFrom0(block, m.Index) - 1;
string key = file + "|" + cls + "|" + id + "|" + prop + "|" + val + "|" + line.ToString();
if (!uniq.Add(key)) continue;
list.Add(new PropRow { File = file, Class = cls, ControlId = id, Prop = prop, Value = val, Line = line });
}
}
private static void AddPropPoint(List<PropRow> list, HashSet<string> uniq, Regex re, string block, string file, string cls, string propName, int baseLine)
{
foreach (Match m in re.Matches(block))
{
if (!m.Success || m.Groups.Count < 4) continue;
string id = m.Groups[1].Value ?? "";
string x = (m.Groups[2].Value ?? "").Trim();
string y = (m.Groups[3].Value ?? "").Trim();
string val = x + "," + y;
int line = baseLine + EstimateLineFrom0(block, m.Index) - 1;
string key = file + "|" + cls + "|" + id + "|" + propName + "|" + val + "|" + line.ToString();
if (!uniq.Add(key)) continue;
list.Add(new PropRow { File = file, Class = cls, ControlId = id, Prop = propName, Value = val, Line = line });
}
}
private static void AddPropSize(List<PropRow> list, HashSet<string> uniq, Regex re, string block, string file, string cls, string propName, int baseLine)
{
foreach (Match m in re.Matches(block))
{
if (!m.Success || m.Groups.Count < 4) continue;
string id = m.Groups[1].Value ?? "";
string w = (m.Groups[2].Value ?? "").Trim();
string h = (m.Groups[3].Value ?? "").Trim();
string val = w + "," + h;
int line = baseLine + EstimateLineFrom0(block, m.Index) - 1;
string key = file + "|" + cls + "|" + id + "|" + propName + "|" + val + "|" + line.ToString();
if (!uniq.Add(key)) continue;
list.Add(new PropRow { File = file, Class = cls, ControlId = id, Prop = propName, Value = val, Line = line });
}
}
}
}
最近のコメント