Poprawki w scan-xxxx
This commit is contained in:
@@ -45,15 +45,25 @@ Algorytm:
|
||||
w l.mn. („Dokumenty handlowe"), bo opisują tabelę. Fallback: jeśli klasy `*Table` brak
|
||||
lub nie ma atrybutu, czytane są te same atrybuty z klasy `*Row`. Wartością jest pierwszy
|
||||
parametr `string` konstruktora atrybutu.
|
||||
- `Guided` = `tak`, gdy klasa `*Table` dziedziczy (bezpośrednio lub pośrednio) z `GuidedTable`
|
||||
albo `ExportedTable`. Tabele oznaczone `Guided=tak` są **rootami drzewa obiektów** —
|
||||
stanowią korzeń paczki danych (`Datapack`/`GuidedRow`/`ExportedRow`) i to one są obsługiwane
|
||||
przez mechanizm synchronizacji i eksportu/importu. Tabele bez tej flagi to elementy
|
||||
szczegółowe (subrowy, info-rowy), które są częścią paczki danej tabeli-korzenia, ale nie
|
||||
stanowią samodzielnego rootu.
|
||||
- `Guided` — rozróżnia trzy stany:
|
||||
- `root` — klasa `*Table` dziedziczy (bezpośrednio lub pośrednio) z `GuidedTable`
|
||||
albo `ExportedTable`. Tabele te są **korzeniami drzewa obiektów** — stanowią root
|
||||
paczki danych (`Datapack`/`GuidedRow`/`ExportedRow`) i to one są obsługiwane
|
||||
przez mechanizm synchronizacji i eksportu/importu.
|
||||
- `child: Pole→TypRow` — tabela jest częścią drzewa innego rootu; pole rekordu
|
||||
z atrybutem `[ColumnInfo(GuidedRelation=…)]` wskazuje na tabelę nadrzędną.
|
||||
`Pole` to nazwa pola w `*Record`, `TypRow` to konkretny typ `*Row` odczytany
|
||||
z odpowiadającej property w klasie `*Row` (w `*Record` pole ma zwykle typ `IRow`).
|
||||
- pusta wartość — tabela szczegółowa (subrow, info-row) niewchodząca w skład żadnego
|
||||
drzewa guided.
|
||||
- `Konfig` = `konfig`, gdy `*Table` ma `[TableInfo(IsConfig=true)]`. Tabele konfiguracyjne
|
||||
żyją w osobnej sesji (`ExecuteConfig`) i mają inne reguły zapisu niż tabele operacyjne.
|
||||
- `Interfaces` = lista nazw interfejsów zadeklarowanych w `[TableInfo(Interfaces = new[] { … })]`.
|
||||
Soneta używa ich jako **relacji interfejsowych** — pole typu `IXxx` może referować rekord
|
||||
z dowolnej tabeli deklarującej `IXxx` w swoim `TableInfo`.
|
||||
- Dla samego modułu (`*Module`) Tytuł/Opis czytane są analogicznie z atrybutów na klasie modułu.
|
||||
6. Wypisz markdown: sekcja `##` per moduł (z jego `Caption`/`Description` jeśli są), w każdej
|
||||
sekcji tabela `RowType | TableType | Tytuł | Opis`.
|
||||
sekcji tabela `RowType | TableType | Guided | Konfig | Interfaces | Tytuł | Opis`.
|
||||
|
||||
## Wymagania
|
||||
|
||||
@@ -89,14 +99,14 @@ Znaleziono modułów: 37
|
||||
- Opis: Moduł handlowy obsługujący dokumenty sprzedaży, zakupu, zamówień i innych operacji handlowych...
|
||||
- Tabel: 62
|
||||
|
||||
| RowType | TableType | Guided | Tytuł | Opis |
|
||||
|---------|-----------|--------|-------|------|
|
||||
| DefDokHandlowego | DefDokHandlowych | tak | Definicje dokumentów handlowych | Konfigurowalna definicja (szablon) dokumentu handlowego... |
|
||||
| DefRelacjiHandlowej | DefRelHandlowych | tak | Definicje relacji handlowych | Konfigurowalna definicja relacji między dokumentami handlowymi... |
|
||||
| DokumentHandlowy | DokHandlowe | tak | Dokumenty handlowe | Główna tabela dokumentów handlowych (faktury, paragony, zamówienia, korekty, umowy itp.)... |
|
||||
| DokumentHandlowyKoszt | DokHandloweKoszt | | Koszty dodatkowe | Koszt dodatkowy przypisany do dokumentu handlowego... |
|
||||
| DrukarkaFiskalna | DrukarkiFiskalne | tak | Lista drukarek fiskalnych | Konfiguracja drukarki fiskalnej... |
|
||||
| ... | ... | ... | ... | ... |
|
||||
| RowType | TableType | Guided | Konfig | Interfaces | Tytuł | Opis |
|
||||
|---------|-----------|--------|--------|------------|-------|------|
|
||||
| DefDokHandlowego | DefDokHandlowych | root | konfig | | Definicje dokumentów handlowych | Konfigurowalna definicja (szablon) dokumentu handlowego... |
|
||||
| DefRelacjiHandlowej | DefRelHandlowych | root | konfig | | Definicje relacji handlowych | Konfigurowalna definicja relacji między dokumentami handlowymi... |
|
||||
| DokumentHandlowy | DokHandlowe | root | | IDokument, IKontrahentRef | Dokumenty handlowe | Główna tabela dokumentów handlowych (faktury, paragony, zamówienia, korekty, umowy itp.)... |
|
||||
| DokumentHandlowyKoszt | DokHandloweKoszt | child: Dokument→DokumentHandlowy | | | Koszty dodatkowe | Koszt dodatkowy przypisany do dokumentu handlowego... |
|
||||
| DrukarkaFiskalna | DrukarkiFiskalne | root | konfig | | Lista drukarek fiskalnych | Konfiguracja drukarki fiskalnej... |
|
||||
| ... | ... | ... | ... | ... | ... | ... |
|
||||
|
||||
_Łącznie tabel: 1196_
|
||||
```
|
||||
|
||||
@@ -41,7 +41,24 @@ Algorytm:
|
||||
- znajdź klasę biznesową (`DefinicjaNumeracji`) oraz typ `*Module+DefinicjaNumeracjiRow` (mogą być w innym module — np. `CoreModule`);
|
||||
- powtórz całą procedurę (kroki 5–8) dla tego rekordu, używając prefiksu `Numeracja.` w kluczach wyników (`Numeracja.Pole1`, `Numeracja.Pole2`, …).
|
||||
Rekurencja działa dowolnie głęboko (subrow w subrowie). Pętle (rekord zawierający siebie pośrednio) są zabezpieczone przez zbiór odwiedzonych typów.
|
||||
10. Wypisz tabelę markdown na stdout (kolumny: `Pole | Typ | Rodzaj | Tytuł | Opis`).
|
||||
10. **Metadane tabeli** — dodatkowo do nagłówka trafiają:
|
||||
- `Tabela konfiguracyjna: Tak/Nie` — czytane z `[TableInfo(IsConfig=true)]` na zagnieżdżonej
|
||||
klasie `*Module.*Table` (atrybut siedzi tam, nie na top-levelowym typie zwracanym przez
|
||||
property `Table` w `*Row`).
|
||||
- `Guided: root` — gdy `*Table` dziedziczy z `GuidedTable`/`ExportedTable`.
|
||||
- `Guided: child — nadrzędna przez pole \`X\` → \`Y\`` — gdy w rekordzie istnieje pole
|
||||
z `[ColumnInfo(GuidedRelation=…)]` wskazujące tabelę nadrzędną w drzewie obiektów.
|
||||
- `Implementuje interfejsy: …` — lista interfejsów z `[TableInfo(Interfaces=…)]` tej tabeli.
|
||||
11. **Relacje interfejsowe** — skrypt buduje globalny indeks `interfejs → lista tabel implementujących`
|
||||
(iteracja po wszystkich `*Module.*Table` we wszystkich referencjach). Dla każdego pola, którego
|
||||
typ jest interfejsem występującym w tym indeksie (heurystyka: nazwa zaczyna się od `I` + wielka
|
||||
litera), kolumna `Rodzaj` dostaje znacznik `iface-ref`, a po głównej tabeli pól wypisywana
|
||||
jest sekcja `## Relacje interfejsowe` z listą `Pole | Interfejs | Tabele implementujące`.
|
||||
Pozwala to od razu zobaczyć alternatywy, do których pole może wskazywać.
|
||||
12. **Znacznik `guided-parent`** — pole rekordu z atrybutem `[ColumnInfo(GuidedRelation=…)]`
|
||||
dostaje w kolumnie `Rodzaj` dodatkowy tag `guided-parent`, sygnalizując, że to ono trzyma
|
||||
referencję do rootu drzewa.
|
||||
13. Wypisz tabelę markdown na stdout (kolumny: `Pole | Typ | Rodzaj | Tytuł | Opis`).
|
||||
|
||||
## Wymagania
|
||||
|
||||
@@ -70,6 +87,9 @@ dotnet script ~/.claude/skills/soneta-programming/scripts/scan-props.csx \
|
||||
```markdown
|
||||
# Pola i właściwości klasy biznesowej: `Soneta.Handel.DokumentHandlowy`
|
||||
Nazwa tabeli: `DokHandlowe`
|
||||
Tabela konfiguracyjna: Nie
|
||||
Guided: root
|
||||
Implementuje interfejsy: `IDokument`, `IKontrahentRef`
|
||||
|
||||
- pola bazodanowe: 128
|
||||
- pola kalkulowane (z klas biznesowych): 388
|
||||
@@ -78,14 +98,28 @@ Nazwa tabeli: `DokHandlowe`
|
||||
|------|-----|--------|-------|------|
|
||||
| Brutto | `decimal` | bazodanowe | Brutto | Wartość brutto dokumentu |
|
||||
| DataDokumentu | `System.DateTime` | bazodanowe | Data dokumentu | |
|
||||
| Kontrahent | `Soneta.Kontrahenci.Kontrahent` | bazodanowe | Kontrahent | |
|
||||
| Kontrahent | `Soneta.Kontrahenci.Kontrahent` | bazodanowe, iface-ref | Kontrahent | |
|
||||
| Netto | `decimal` | bazodanowe | Netto | |
|
||||
| Numer | `string` | bazodanowe | Numer | |
|
||||
| SaldoWaluta | `decimal` | | Saldo w walucie | |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
## Relacje interfejsowe
|
||||
|
||||
Pola, których typ jest interfejsem zadeklarowanym w `[TableInfo(Interfaces=...)]` innych tabel.
|
||||
Pole może wskazywać na rekord dowolnej z poniższych tabel.
|
||||
|
||||
| Pole | Interfejs | Tabele implementujące |
|
||||
|------|-----------|------------------------|
|
||||
| Kontrahent | `IKontrahent` | `Kontrahent`, `Pracownik`, `Urzad` |
|
||||
```
|
||||
|
||||
Kolumna `Rodzaj` ma wartość `bazodanowe` dla pól rekordu lub jest pusta dla właściwości kalkulowanych.
|
||||
Kolumna `Rodzaj` jest kombinacją znaczników rozdzielonych przecinkami:
|
||||
- `bazodanowe` — pole rekordu (`*Record`); brak znacznika = property kalkulowana klasy biznesowej.
|
||||
- `guided-parent` — pole z `[ColumnInfo(GuidedRelation=…)]` trzymające referencję do nadrzędnej
|
||||
tabeli w drzewie obiektów guided.
|
||||
- `iface-ref` — typ pola jest interfejsem zadeklarowanym w `[TableInfo(Interfaces=…)]` innej tabeli;
|
||||
konkretne tabele docelowe są wymienione w sekcji `## Relacje interfejsowe` pod tabelą pól.
|
||||
|
||||
## Kody wyjścia
|
||||
|
||||
|
||||
@@ -112,8 +112,8 @@ foreach (var module in modules)
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine("| RowType | TableType | Guided | Tytuł | Opis |");
|
||||
Console.WriteLine("|---------|-----------|--------|-------|------|");
|
||||
Console.WriteLine("| RowType | TableType | Guided | Konfig | Interfaces | Tytuł | Opis |");
|
||||
Console.WriteLine("|---------|-----------|--------|--------|------------|-------|------|");
|
||||
foreach (var row in rowClasses)
|
||||
{
|
||||
var rowType = row.Name.EndsWith("Row")
|
||||
@@ -136,11 +136,19 @@ foreach (var module in modules)
|
||||
if (string.IsNullOrEmpty(description))
|
||||
description = GetAttributeFirstString(row, "DescriptionAttribute");
|
||||
|
||||
var guided = tableCls != null && InheritsFromGuidedOrExportedTable(tableCls)
|
||||
? "tak"
|
||||
: "";
|
||||
var isGuidedRoot = tableCls != null && InheritsFromGuidedOrExportedTable(tableCls);
|
||||
var guided = isGuidedRoot ? "root" : "";
|
||||
if (!isGuidedRoot)
|
||||
{
|
||||
var recordCls = module.GetTypeMembers(rowType + "Record").FirstOrDefault();
|
||||
var parent = FindGuidedParent(recordCls, row);
|
||||
if (!string.IsNullOrEmpty(parent)) guided = "child: " + parent;
|
||||
}
|
||||
|
||||
Console.WriteLine($"| {rowType} | {tableType} | {guided} | {EscapeCell(caption)} | {EscapeCell(description)} |");
|
||||
var konfig = IsConfigTable(tableCls) ? "konfig" : "";
|
||||
var interfaces = string.Join(", ", GetTableInterfaces(tableCls));
|
||||
|
||||
Console.WriteLine($"| {rowType} | {tableType} | {guided} | {konfig} | {EscapeCell(interfaces)} | {EscapeCell(caption)} | {EscapeCell(description)} |");
|
||||
totalRows++;
|
||||
}
|
||||
Console.WriteLine();
|
||||
@@ -185,6 +193,75 @@ static ISymbol FindMemberInherited(INamedTypeSymbol type, string name)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Zwraca opis nadrzędnej tabeli w strukturze guided dla tabel guided-child.
|
||||
// Pole rekordu oznaczone [ColumnInfo(GuidedRelation=RelationGuidedType.GuidedParent)] wskazuje
|
||||
// kierunek relacji; konkretny typ Row pobieramy z property o tej samej nazwie w klasie *Row
|
||||
// (w *Record pole ma zwykle typ IRow, więc bez Row nie da się ustalić konkretu).
|
||||
static string FindGuidedParent(INamedTypeSymbol recordCls, INamedTypeSymbol rowCls)
|
||||
{
|
||||
if (recordCls == null) return "";
|
||||
foreach (var f in recordCls.GetMembers().OfType<IFieldSymbol>())
|
||||
{
|
||||
foreach (var a in f.GetAttributes())
|
||||
{
|
||||
var an = a.AttributeClass?.Name;
|
||||
if (an != "ColumnInfoAttribute" && an != "ColumnInfo") continue;
|
||||
var hasGuided = a.NamedArguments.Any(na => na.Key == "GuidedRelation"
|
||||
&& na.Value.Kind == TypedConstantKind.Enum
|
||||
&& na.Value.Value is int v && v != 0);
|
||||
if (!hasGuided) continue;
|
||||
var propType = "?";
|
||||
if (rowCls != null)
|
||||
{
|
||||
for (var rc = rowCls; rc != null && rc.SpecialType != SpecialType.System_Object; rc = rc.BaseType)
|
||||
{
|
||||
var p = rc.GetMembers(f.Name).OfType<IPropertySymbol>().FirstOrDefault();
|
||||
if (p != null) { propType = p.Type.Name; break; }
|
||||
}
|
||||
}
|
||||
return f.Name + "→" + propType;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Lista interfejsów biznesowych z [TableInfo(Interfaces = new[] { "I1", "I2", ... })].
|
||||
// Soneta używa ich jako "relacji interfejsowych" — pole typu IXxx może referować dowolny
|
||||
// rekord z tabeli, która deklaruje IXxx w swoim TableInfo.
|
||||
static System.Collections.Generic.IEnumerable<string> GetTableInterfaces(INamedTypeSymbol tableCls)
|
||||
{
|
||||
if (tableCls == null) yield break;
|
||||
foreach (var a in tableCls.GetAttributes())
|
||||
{
|
||||
if (a.AttributeClass?.Name != "TableInfoAttribute" && a.AttributeClass?.Name != "TableInfo")
|
||||
continue;
|
||||
foreach (var na in a.NamedArguments)
|
||||
{
|
||||
if (na.Key != "Interfaces" || na.Value.Kind != TypedConstantKind.Array) continue;
|
||||
foreach (var el in na.Value.Values)
|
||||
{
|
||||
if (el.Value is string s && !string.IsNullOrEmpty(s)) yield return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsConfigTable(INamedTypeSymbol tableCls)
|
||||
{
|
||||
if (tableCls == null) return false;
|
||||
foreach (var a in tableCls.GetAttributes())
|
||||
{
|
||||
if (a.AttributeClass?.Name != "TableInfoAttribute" && a.AttributeClass?.Name != "TableInfo")
|
||||
continue;
|
||||
foreach (var na in a.NamedArguments)
|
||||
{
|
||||
if (na.Key == "IsConfig" && na.Value.Value is bool b)
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static string GetAttributeFirstString(ISymbol symbol, string attributeTypeName)
|
||||
{
|
||||
if (symbol == null) return "";
|
||||
|
||||
@@ -105,6 +105,7 @@ topLevelClasses.TryGetValue(recordBaseName, out mainBusinessClass);
|
||||
|
||||
// Nazwa tabeli wyciągana z typu zwracanego przez property `Table` w klasie XxxxRow.
|
||||
string tableTypeName = null;
|
||||
bool isConfigTable = false;
|
||||
var rowClass = enclosing?.GetTypeMembers(recordBaseName + "Row").FirstOrDefault();
|
||||
if (rowClass != null)
|
||||
{
|
||||
@@ -118,6 +119,20 @@ if (rowClass != null)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Atrybut [TableInfo(IsConfig=true)] siedzi na klasie zagnieżdżonej XxxxModule.XxxxTable
|
||||
// (nie na top-level typie tabeli zwracanym przez property `Table` w *Row).
|
||||
var nestedTableCls = enclosing?.GetTypeMembers(recordBaseName + "Table").FirstOrDefault();
|
||||
if (nestedTableCls != null)
|
||||
isConfigTable = IsConfigTable(nestedTableCls);
|
||||
|
||||
// Wyznacz status guided: root (dziedziczy po GuidedTable/ExportedTable) lub child→ParentRow
|
||||
// (pole rekordu z [ColumnInfo(GuidedRelation=...)]). Pole zapamiętujemy też w guidedParentField,
|
||||
// żeby oznaczyć je później w tabeli pól.
|
||||
var isGuidedRoot = nestedTableCls != null && InheritsFromGuidedOrExportedTable(nestedTableCls);
|
||||
string guidedParentField = null;
|
||||
string guidedParentType = null;
|
||||
if (!isGuidedRoot)
|
||||
(guidedParentField, guidedParentType) = FindGuidedParent(foundRecord, rowClass);
|
||||
|
||||
// Klucz: nazwa pola z notacją kropkową dla subrowów; Wartość: (typ, czyBazodanowe, tytuł, opis)
|
||||
var merged = new SortedDictionary<string, (string Type, bool IsDb, string Caption, string Description)>(StringComparer.Ordinal);
|
||||
@@ -138,6 +153,38 @@ else
|
||||
if (!string.IsNullOrEmpty(tableTypeName))
|
||||
{
|
||||
Console.WriteLine($"Nazwa tabeli: `{tableTypeName}`");
|
||||
Console.WriteLine($"Tabela konfiguracyjna: {(isConfigTable ? "Tak" : "Nie")}");
|
||||
if (isGuidedRoot)
|
||||
Console.WriteLine("Guided: root");
|
||||
else if (guidedParentField != null)
|
||||
Console.WriteLine($"Guided: child — nadrzędna przez pole `{guidedParentField}` → `{guidedParentType}`");
|
||||
var thisInterfaces = nestedTableCls != null ? GetTableInterfaces(nestedTableCls).ToList() : new System.Collections.Generic.List<string>();
|
||||
if (thisInterfaces.Count > 0)
|
||||
Console.WriteLine($"Implementuje interfejsy: {string.Join(", ", thisInterfaces.Select(i => "`" + i + "`"))}");
|
||||
}
|
||||
|
||||
// Indeks interfejs → lista tabel implementujących, na potrzeby pokazania alternatyw
|
||||
// dla pól o typie interfejsowym (relacje interfejsowe Soneta). Klasy *Table są zagnieżdżone
|
||||
// w *Module — iterujemy po top-level *Module i pobieramy ich nested types.
|
||||
var interfaceImpls = new SortedDictionary<string, System.Collections.Generic.List<string>>(StringComparer.Ordinal);
|
||||
foreach (var asmRef in compilation.References)
|
||||
{
|
||||
if (compilation.GetAssemblyOrModuleSymbol(asmRef) is not IAssemblySymbol asm) continue;
|
||||
foreach (var top in EnumerateAllTypes(asm.GlobalNamespace))
|
||||
{
|
||||
if (top.ContainingType != null || !top.Name.EndsWith("Module")) continue;
|
||||
foreach (var t in top.GetTypeMembers())
|
||||
{
|
||||
if (!t.Name.EndsWith("Table")) continue;
|
||||
foreach (var iface in GetTableInterfaces(t))
|
||||
{
|
||||
if (!interfaceImpls.TryGetValue(iface, out var list))
|
||||
interfaceImpls[iface] = list = new System.Collections.Generic.List<string>();
|
||||
var rowName = t.Name.Substring(0, t.Name.Length - "Table".Length);
|
||||
list.Add(rowName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine();
|
||||
var dbCount = merged.Values.Count(v => v.IsDb);
|
||||
@@ -147,13 +194,48 @@ Console.WriteLine($"- pola kalkulowane (z klas biznesowych): {calcCount}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("| Pole | Typ | Rodzaj | Tytuł | Opis |");
|
||||
Console.WriteLine("|------|-----|--------|-------|------|");
|
||||
var interfaceFields = new System.Collections.Generic.List<(string Field, string IfaceShort, System.Collections.Generic.List<string> Impls)>();
|
||||
foreach (var kv in merged)
|
||||
{
|
||||
var rodzaj = kv.Value.IsDb ? "bazodanowe" : "";
|
||||
if (guidedParentField != null && kv.Key == guidedParentField)
|
||||
rodzaj = string.IsNullOrEmpty(rodzaj) ? "guided-parent" : rodzaj + ", guided-parent";
|
||||
var shortType = ShortTypeName(kv.Value.Type);
|
||||
if (shortType.StartsWith("I") && shortType.Length > 1 && char.IsUpper(shortType[1])
|
||||
&& interfaceImpls.TryGetValue(shortType, out var impls))
|
||||
{
|
||||
rodzaj = string.IsNullOrEmpty(rodzaj) ? "iface-ref" : rodzaj + ", iface-ref";
|
||||
interfaceFields.Add((kv.Key, shortType, impls));
|
||||
}
|
||||
Console.WriteLine($"| {kv.Key} | `{kv.Value.Type}` | {rodzaj} | {EscapeCell(kv.Value.Caption)} | {EscapeCell(kv.Value.Description)} |");
|
||||
}
|
||||
|
||||
if (interfaceFields.Count > 0)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("## Relacje interfejsowe");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Pola, których typ jest interfejsem zadeklarowanym w `[TableInfo(Interfaces=...)]` innych tabel.");
|
||||
Console.WriteLine("Pole może wskazywać na rekord dowolnej z poniższych tabel.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("| Pole | Interfejs | Tabele implementujące |");
|
||||
Console.WriteLine("|------|-----------|------------------------|");
|
||||
foreach (var f in interfaceFields)
|
||||
{
|
||||
Console.WriteLine($"| {f.Field} | `{f.IfaceShort}` | {string.Join(", ", f.Impls.Select(i => "`" + i + "`"))} |");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
static string ShortTypeName(string fullName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fullName)) return "";
|
||||
var lt = fullName.IndexOf('<');
|
||||
if (lt >= 0) fullName = fullName.Substring(0, lt);
|
||||
var dot = fullName.LastIndexOf('.');
|
||||
return dot >= 0 ? fullName.Substring(dot + 1) : fullName;
|
||||
}
|
||||
|
||||
static void ScanRecord(
|
||||
INamedTypeSymbol record,
|
||||
string prefix,
|
||||
@@ -267,6 +349,77 @@ static IEnumerable<IPropertySymbol> EnumerateInheritedProperties(INamedTypeSymbo
|
||||
}
|
||||
}
|
||||
|
||||
static bool InheritsFromGuidedOrExportedTable(INamedTypeSymbol type)
|
||||
{
|
||||
for (var t = type.BaseType; t != null && t.SpecialType != SpecialType.System_Object; t = t.BaseType)
|
||||
{
|
||||
if (t.Name == "GuidedTable" || t.Name == "ExportedTable") return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static (string field, string parentType) FindGuidedParent(INamedTypeSymbol recordCls, INamedTypeSymbol rowCls)
|
||||
{
|
||||
if (recordCls == null) return (null, null);
|
||||
foreach (var f in recordCls.GetMembers().OfType<IFieldSymbol>())
|
||||
{
|
||||
foreach (var a in f.GetAttributes())
|
||||
{
|
||||
var an = a.AttributeClass?.Name;
|
||||
if (an != "ColumnInfoAttribute" && an != "ColumnInfo") continue;
|
||||
var hasGuided = a.NamedArguments.Any(na => na.Key == "GuidedRelation"
|
||||
&& na.Value.Kind == TypedConstantKind.Enum
|
||||
&& na.Value.Value is int v && v != 0);
|
||||
if (!hasGuided) continue;
|
||||
var propType = "?";
|
||||
if (rowCls != null)
|
||||
{
|
||||
for (var rc = rowCls; rc != null && rc.SpecialType != SpecialType.System_Object; rc = rc.BaseType)
|
||||
{
|
||||
var p = rc.GetMembers(f.Name).OfType<IPropertySymbol>().FirstOrDefault();
|
||||
if (p != null) { propType = p.Type.Name; break; }
|
||||
}
|
||||
}
|
||||
return (f.Name, propType);
|
||||
}
|
||||
}
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
static System.Collections.Generic.IEnumerable<string> GetTableInterfaces(INamedTypeSymbol tableCls)
|
||||
{
|
||||
if (tableCls == null) yield break;
|
||||
foreach (var a in tableCls.GetAttributes())
|
||||
{
|
||||
if (a.AttributeClass?.Name != "TableInfoAttribute" && a.AttributeClass?.Name != "TableInfo")
|
||||
continue;
|
||||
foreach (var na in a.NamedArguments)
|
||||
{
|
||||
if (na.Key != "Interfaces" || na.Value.Kind != TypedConstantKind.Array) continue;
|
||||
foreach (var el in na.Value.Values)
|
||||
{
|
||||
if (el.Value is string s && !string.IsNullOrEmpty(s)) yield return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsConfigTable(INamedTypeSymbol tableCls)
|
||||
{
|
||||
if (tableCls == null) return false;
|
||||
foreach (var a in tableCls.GetAttributes())
|
||||
{
|
||||
if (a.AttributeClass?.Name != "TableInfoAttribute" && a.AttributeClass?.Name != "TableInfo")
|
||||
continue;
|
||||
foreach (var na in a.NamedArguments)
|
||||
{
|
||||
if (na.Key == "IsConfig" && na.Value.Value is bool b)
|
||||
return b;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static string GetAttributeFirstString(ISymbol symbol, string attributeTypeName)
|
||||
{
|
||||
if (symbol == null) return "";
|
||||
|
||||
Reference in New Issue
Block a user