Files
cc-1c-skills/.codeassistant/skills/skd-info/scripts/skd-info.ps1
T
2026-07-17 17:43:12 +00:00

1986 lines
65 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# skd-info v1.8 — Analyze 1C DCS structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true)]
[Alias('Path')]
[string]$TemplatePath,
[ValidateSet("overview", "query", "fields", "links", "calculated", "resources", "params", "variant", "trace", "templates", "full")]
[string]$Mode = "overview",
[string]$Name,
[int]$Batch = 0,
[int]$Limit = 150,
[int]$Offset = 0,
[string]$OutFile,
[switch]$Raw
)
$ErrorActionPreference = "Stop"
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# --- Resolve path ---
$originalPath = $TemplatePath
if (-not $TemplatePath.EndsWith(".xml")) {
$candidate = Join-Path (Join-Path $TemplatePath "Ext") "Template.xml"
if (Test-Path $candidate) {
$TemplatePath = $candidate
}
}
# If still not a file, try resolving from object directory (Reports/X, DataProcessors/X)
if (-not (Test-Path $TemplatePath -PathType Leaf)) {
$templatesDir = Join-Path $originalPath "Templates"
if (Test-Path $templatesDir) {
$dcsTemplates = @()
foreach ($metaXml in (Get-ChildItem $templatesDir -Filter "*.xml" -File)) {
[xml]$meta = Get-Content $metaXml.FullName -Encoding UTF8
$tt = $meta.SelectSingleNode("//*[local-name()='TemplateType']")
if ($tt -and $tt.InnerText -eq "DataCompositionSchema") {
$tplName = [System.IO.Path]::GetFileNameWithoutExtension($metaXml.Name)
$tplPath = Join-Path (Join-Path (Join-Path $templatesDir $tplName) "Ext") "Template.xml"
if (Test-Path $tplPath) {
$dcsTemplates += $tplPath
}
}
}
if ($dcsTemplates.Count -eq 1) {
$TemplatePath = $dcsTemplates[0]
$resolvedMsg = (Resolve-Path $TemplatePath).Path
$cwd = (Get-Location).Path
if ($resolvedMsg.StartsWith($cwd)) {
$resolvedMsg = $resolvedMsg.Substring($cwd.Length + 1)
}
Write-Host "[i] Resolved: $resolvedMsg"
} elseif ($dcsTemplates.Count -gt 1) {
Write-Host "Multiple DCS templates found in: $originalPath"
$cwd = (Get-Location).Path
for ($i = 0; $i -lt $dcsTemplates.Count; $i++) {
$p = (Resolve-Path $dcsTemplates[$i]).Path
if ($p.StartsWith($cwd)) { $p = $p.Substring($cwd.Length + 1) }
Write-Host " $($i+1). $p"
}
Write-Host "Specify the template path."
exit 1
} else {
Write-Error "No DCS templates found in: $originalPath"
exit 1
}
}
}
if (-not (Test-Path $TemplatePath -PathType Leaf)) {
Write-Error "File not found: $TemplatePath"
exit 1
}
$resolvedPath = (Resolve-Path $TemplatePath).Path
# --- Load XML ---
$xmlDoc = New-Object System.Xml.XmlDocument
$xmlDoc.PreserveWhitespace = $false
$xmlDoc.Load($resolvedPath)
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("s", "http://v8.1c.ru/8.1/data-composition-system/schema")
$ns.AddNamespace("dcscom", "http://v8.1c.ru/8.1/data-composition-system/common")
$ns.AddNamespace("dcscor", "http://v8.1c.ru/8.1/data-composition-system/core")
$ns.AddNamespace("dcsset", "http://v8.1c.ru/8.1/data-composition-system/settings")
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
$ns.AddNamespace("v8ui", "http://v8.1c.ru/8.1/data/ui")
$ns.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema")
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$ns.AddNamespace("dcsat", "http://v8.1c.ru/8.1/data-composition-system/area-template")
$root = $xmlDoc.DocumentElement
# --- Helpers ---
function Get-MLText($node) {
if (-not $node) { return "" }
$content = $node.SelectSingleNode("v8:item/v8:content", $ns)
if ($content) { return $content.InnerText }
$text = $node.InnerText.Trim()
if ($text) { return $text }
return ""
}
function Unescape-Xml([string]$text) {
if (-not $text) { return $text }
$text = $text.Replace("&", "&")
$text = $text.Replace(">", ">")
$text = $text.Replace("&lt;", "<")
$text = $text.Replace("&quot;", '"')
$text = $text.Replace("&apos;", "'")
return $text
}
function Get-CompactType($valueTypeNode) {
if (-not $valueTypeNode) { return "" }
$types = @()
foreach ($t in $valueTypeNode.SelectNodes("v8:Type", $ns)) {
$raw = $t.InnerText
switch -Wildcard ($raw) {
"xs:string" { $types += "String" }
"xs:decimal" { $types += "Number" }
"xs:boolean" { $types += "Boolean" }
"xs:dateTime" { $types += "DateTime" }
"v8:StandardPeriod" { $types += "StandardPeriod" }
"v8:StandardBeginningDate" { $types += "StandardBeginningDate" }
"v8:AccountType" { $types += "AccountType" }
"v8:Null" { $types += "Null" }
default {
# Strip namespace prefixes like d4p1: cfg:
$clean = $raw -replace '^[a-zA-Z0-9]+:', ''
$types += $clean
}
}
}
if ($types.Count -eq 0) { return "" }
return ($types -join " | ")
}
function Get-DataSetType($dsNode) {
$xsiType = $dsNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($xsiType -like "*DataSetQuery*") { return "Query" }
if ($xsiType -like "*DataSetObject*") { return "Object" }
if ($xsiType -like "*DataSetUnion*") { return "Union" }
return "Unknown"
}
function Get-FieldCount($dsNode) {
return $dsNode.SelectNodes("s:field", $ns).Count
}
function Get-QueryLineCount($dsNode) {
$queryNode = $dsNode.SelectSingleNode("s:query", $ns)
if (-not $queryNode) { return 0 }
$text = $queryNode.InnerText
return ($text -split "`n").Count
}
function Get-StructureItemType($itemNode) {
$xsiType = $itemNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($xsiType -like "*StructureItemGroup*") { return "Group" }
if ($xsiType -like "*StructureItemTable*") { return "Table" }
if ($xsiType -like "*StructureItemChart*") { return "Chart" }
return "Unknown"
}
function Get-GroupFields($itemNode) {
$fields = @()
foreach ($gi in $itemNode.SelectNodes("dcsset:groupItems/dcsset:item", $ns)) {
$fieldNode = $gi.SelectSingleNode("dcsset:field", $ns)
$groupType = $gi.SelectSingleNode("dcsset:groupType", $ns)
if ($fieldNode) {
$f = $fieldNode.InnerText
$gt = if ($groupType) { $groupType.InnerText } else { "" }
if ($gt -and $gt -ne "Items") { $f += "($gt)" }
$fields += $f
}
}
return $fields
}
function Get-SelectionFields($itemNode) {
$fields = @()
foreach ($si in $itemNode.SelectNodes("dcsset:selection/dcsset:item", $ns)) {
$xsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($xsiType -like "*SelectedItemAuto*") {
$fields += "Auto"
} elseif ($xsiType -like "*SelectedItemField*") {
$f = $si.SelectSingleNode("dcsset:field", $ns)
if ($f) { $fields += $f.InnerText }
} elseif ($xsiType -like "*SelectedItemFolder*") {
$fields += "Folder"
}
}
return $fields
}
function Get-FilterSummary($settingsNode) {
$filters = @()
foreach ($fi in $settingsNode.SelectNodes("dcsset:filter/dcsset:item", $ns)) {
$xsiType = $fi.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($xsiType -like "*FilterItemGroup*") {
$groupType = $fi.SelectSingleNode("dcsset:groupType", $ns)
$gt = if ($groupType) { $groupType.InnerText } else { "And" }
$subCount = $fi.SelectNodes("dcsset:item", $ns).Count
$filters += "[Group:$gt $subCount items]"
continue
}
$use = $fi.SelectSingleNode("dcsset:use", $ns)
$isActive = if ($use -and $use.InnerText -eq "false") { "[ ]" } else { "[x]" }
$left = $fi.SelectSingleNode("dcsset:left", $ns)
$comp = $fi.SelectSingleNode("dcsset:comparisonType", $ns)
$right = $fi.SelectSingleNode("dcsset:right", $ns)
$pres = $fi.SelectSingleNode("dcsset:presentation", $ns)
$userSetting = $fi.SelectSingleNode("dcsset:userSettingID", $ns)
$leftStr = if ($left) { $left.InnerText } else { "?" }
$compStr = if ($comp) { $comp.InnerText } else { "?" }
$rightStr = ""
if ($right) {
$rightStr = " $($right.InnerText)"
}
$presStr = ""
if ($pres) {
$pt = Get-MLText $pres
if ($pt) { $presStr = " `"$pt`"" }
}
$userStr = ""
if ($userSetting) { $userStr = " [user]" }
$filters += "$isActive $leftStr $compStr$rightStr$presStr$userStr"
}
return $filters
}
function Build-StructureTree {
param($itemNode, [string]$prefix, [bool]$isLast, [System.Collections.Generic.List[string]]$outLines)
$itemType = Get-StructureItemType $itemNode
$nameNode = $itemNode.SelectSingleNode("dcsset:name", $ns)
$itemName = if ($nameNode) { $nameNode.InnerText } else { "" }
$groupFields = Get-GroupFields $itemNode
$groupStr = if ($groupFields.Count -gt 0) { "[" + ($groupFields -join ", ") + "]" } else { "(detail)" }
$selFields = Get-SelectionFields $itemNode
$selStr = if ($selFields.Count -gt 0) { "Selection: " + ($selFields -join ", ") } else { "" }
$line = ""
switch ($itemType) {
"Group" {
$line = "$itemType $groupStr"
if ($itemName) { $line = "$itemType `"$itemName`" $groupStr" }
}
"Table" {
$line = "Table"
if ($itemName) { $line = "Table `"$itemName`"" }
}
"Chart" {
$line = "Chart"
if ($itemName) { $line = "Chart `"$itemName`"" }
}
}
$outLines.Add("$prefix$line")
if ($selStr -and $itemType -eq "Group") {
$outLines.Add("$prefix $selStr")
}
# For Table, show columns and rows
if ($itemType -eq "Table") {
$columns = $itemNode.SelectNodes("dcsset:column", $ns)
$rows = $itemNode.SelectNodes("dcsset:row", $ns)
foreach ($col in $columns) {
$colGroup = Get-GroupFields $col
$colGroupStr = if ($colGroup.Count -gt 0) { "[" + ($colGroup -join ", ") + "]" } else { "(detail)" }
$colSel = Get-SelectionFields $col
$colSelStr = if ($colSel.Count -gt 0) { "Selection: " + ($colSel -join ", ") } else { "" }
$connC = if ($rows.Count -gt 0) { [string][char]0x251C + [string][char]0x2500 + [string][char]0x2500 } else { [string][char]0x2514 + [string][char]0x2500 + [string][char]0x2500 }
$contC = if ($rows.Count -gt 0) { [string][char]0x2502 + " " } else { " " }
$outLines.Add("$prefix$connC Columns: $colGroupStr")
if ($colSelStr) { $outLines.Add("$prefix$contC $colSelStr") }
}
foreach ($row in $rows) {
$rowGroup = Get-GroupFields $row
$rowGroupStr = if ($rowGroup.Count -gt 0) { "[" + ($rowGroup -join ", ") + "]" } else { "(detail)" }
$rowSel = Get-SelectionFields $row
$rowSelStr = if ($rowSel.Count -gt 0) { "Selection: " + ($rowSel -join ", ") } else { "" }
$outLines.Add("$prefix" + [string][char]0x2514 + [string][char]0x2500 + [string][char]0x2500 + " Rows: $rowGroupStr")
if ($rowSelStr) { $outLines.Add("$prefix $rowSelStr") }
}
}
# Recurse into nested structure items (for Group)
if ($itemType -eq "Group") {
$children = $itemNode.SelectNodes("dcsset:item", $ns)
for ($i = 0; $i -lt $children.Count; $i++) {
$child = $children[$i]
$childType = $child.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($childType -like "*StructureItem*") {
$last = ($i -eq $children.Count - 1)
$connector = if ($last) { [string][char]0x2514 + [string][char]0x2500 + " " } else { [string][char]0x251C + [string][char]0x2500 + " " }
$continuation = if ($last) { " " } else { [string][char]0x2502 + " " }
Build-StructureTree -itemNode $child -prefix "$prefix$continuation" -isLast $last -outLines $outLines
}
}
}
}
# --- Output collector ---
$lines = [System.Collections.Generic.List[string]]::new()
# Determine template name from path
$pathParts = $resolvedPath -split '[/\\]'
$templateName = $resolvedPath
for ($i = $pathParts.Count - 1; $i -ge 0; $i--) {
if ($pathParts[$i] -eq "Ext" -and $i -ge 1) {
$templateName = $pathParts[$i - 1]
break
}
}
$totalXmlLines = (Get-Content $resolvedPath).Count
function Test-ExternalObjectRoot([string]$xmlPath) {
if (-not (Test-Path $xmlPath)) { return $false }
try {
[xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8
$el = $mx.DocumentElement.FirstChild
while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling }
if ($el) { return @('ExternalDataProcessor','ExternalReport') -contains $el.LocalName }
} catch {}
return $false
}
function Get-SupportStatusForPath([string]$targetPath) {
try {
$rp = (Resolve-Path $targetPath).Path
$elemUuid = $null
$binPath = $null
# Reads the uuid of the first metadata element in an .xml file (or $null).
function Get-RootUuid([string]$xmlPath) {
if (-not (Test-Path $xmlPath)) { return $null }
try {
[xml]$mx = Get-Content -Path $xmlPath -Encoding UTF8
$el = $mx.DocumentElement.FirstChild
while ($el -and $el.NodeType -ne 'Element') { $el = $el.NextSibling }
if ($el) { $u = $el.GetAttribute("uuid"); if ($u) { return $u } }
} catch {}
return $null
}
# The target file itself may be the element meta-xml (e.g. Subsystems/X.xml).
$elemUuid = Get-RootUuid $rp
if (Test-ExternalObjectRoot $rp) { return $null }
$d = [System.IO.Path]::GetDirectoryName($rp)
for ($i = 0; $i -lt 12 -and $d; $i++) {
if (Test-ExternalObjectRoot "$d.xml") { return $null }
if (-not $elemUuid) { $elemUuid = Get-RootUuid "$d.xml" }
if (-not $binPath) {
$cand = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin"
if ((Test-Path $cand) -or (Test-Path (Join-Path $d "Configuration.xml"))) { $binPath = $cand }
}
if ($elemUuid -and $binPath) { break }
$parent = [System.IO.Path]::GetDirectoryName($d)
if ($parent -eq $d) { break }
$d = $parent
}
if (-not $binPath -or -not (Test-Path $binPath)) { return "не на поддержке" }
$bytes = [System.IO.File]::ReadAllBytes($binPath)
if ($bytes.Length -le 32) { return "снято с поддержки (правки свободны)" }
$start = 0
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $start = 3 }
$text = [System.Text.Encoding]::UTF8.GetString($bytes, $start, $bytes.Length - $start)
$h = [regex]::Match($text, '^\{6,(\d+),(\d+),')
if (-not $h.Success) { return "не на поддержке" }
$G = [int]$h.Groups[1].Value
$K = [int]$h.Groups[2].Value
if ($K -eq 0) { return "снято с поддержки (правки свободны)" }
if ($G -eq 1) { return "конфигурация read-only (возможность изменения выключена) — правки невозможны без включения" }
if (-not $elemUuid) { return "не на поддержке" }
$u = [regex]::Escape($elemUuid.ToLower())
$best = $null
foreach ($m in [regex]::Matches($text, "([0-2]),0,$u")) {
$f1 = [int]$m.Groups[1].Value
if ($null -eq $best -or $f1 -lt $best) { $best = $f1 }
}
if ($null -eq $best) { return "не на поддержке" }
switch ($best) {
0 { return "на замке — прямая правка сломает обновления; дорабатывай через cfe-* либо включи редактирование объекта" }
1 { return "редактируется с сохранением поддержки" }
2 { return "снято с поддержки (правки свободны)" }
}
return "не на поддержке"
} catch { return "не на поддержке" }
}
function Show-Overview {
$lines.Add("=== DCS: $templateName ($totalXmlLines lines) ===")
$support = Get-SupportStatusForPath $TemplatePath
if ($null -ne $support) { $lines.Add("Поддержка: $support") }
$lines.Add("")
# Sources
$sources = @()
foreach ($ds in $root.SelectNodes("s:dataSource", $ns)) {
$dsName = $ds.SelectSingleNode("s:name", $ns).InnerText
$dsType = $ds.SelectSingleNode("s:dataSourceType", $ns).InnerText
$sources += "$dsName ($dsType)"
}
$lines.Add("Sources: " + ($sources -join ", "))
$lines.Add("")
# Datasets (recursive for Union)
$lines.Add("Datasets:")
foreach ($ds in $root.SelectNodes("s:dataSet", $ns)) {
$dsType = Get-DataSetType $ds
$dsName = $ds.SelectSingleNode("s:name", $ns).InnerText
$fieldCount = Get-FieldCount $ds
switch ($dsType) {
"Query" {
$queryLines = Get-QueryLineCount $ds
$lines.Add(" [Query] $dsName $fieldCount fields, query $queryLines lines")
}
"Object" {
$objName = $ds.SelectSingleNode("s:objectName", $ns)
$objStr = if ($objName) { " objectName=$($objName.InnerText)" } else { "" }
$lines.Add(" [Object] $dsName$objStr $fieldCount fields")
}
"Union" {
$lines.Add(" [Union] $dsName $fieldCount fields")
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
$subType = Get-DataSetType $subDs
$subName = $subDs.SelectSingleNode("s:name", $ns)
$subNameStr = if ($subName) { $subName.InnerText } else { "?" }
$subFields = Get-FieldCount $subDs
switch ($subType) {
"Query" {
$subQueryLines = Get-QueryLineCount $subDs
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [Query] $subNameStr $subFields fields, query $subQueryLines lines")
}
"Object" {
$subObjName = $subDs.SelectSingleNode("s:objectName", $ns)
$subObjStr = if ($subObjName) { " objectName=$($subObjName.InnerText)" } else { "" }
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [Object] $subNameStr$subObjStr $subFields fields")
}
default {
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [$subType] $subNameStr $subFields fields")
}
}
}
}
}
}
# Links — only dataset pairs (not field-level)
$links = $root.SelectNodes("s:dataSetLink", $ns)
if ($links.Count -gt 0) {
$linkPairs = [ordered]@{}
foreach ($lnk in $links) {
$srcDs = $lnk.SelectSingleNode("s:sourceDataSet", $ns).InnerText
$dstDs = $lnk.SelectSingleNode("s:destinationDataSet", $ns).InnerText
$key = "$srcDs -> $dstDs"
if (-not $linkPairs.Contains($key)) { $linkPairs[$key] = 0 }
$linkPairs[$key] = $linkPairs[$key] + 1
}
$linkStrs = @()
foreach ($key in $linkPairs.Keys) {
$cnt = $linkPairs[$key]
if ($cnt -gt 1) { $linkStrs += "$key (${cnt} fields)" }
else { $linkStrs += $key }
}
$lines.Add("Links: " + ($linkStrs -join ", "))
}
# Calculated fields — count only
$calcFields = $root.SelectNodes("s:calculatedField", $ns)
if ($calcFields.Count -gt 0) {
$lines.Add("Calculated: $($calcFields.Count)")
}
# Totals — count + group flag
$totalFields = $root.SelectNodes("s:totalField", $ns)
if ($totalFields.Count -gt 0) {
$hasGrouped = $false
$uniquePaths = @{}
foreach ($tf in $totalFields) {
$tfPath = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
$uniquePaths[$tfPath] = $true
if ($tf.SelectSingleNode("s:group", $ns)) { $hasGrouped = $true }
}
$groupNote = if ($hasGrouped) { ", with group formulas" } else { "" }
if ($uniquePaths.Count -eq $totalFields.Count) {
$lines.Add("Resources: $($totalFields.Count)$groupNote")
} else {
$lines.Add("Resources: $($totalFields.Count) ($($uniquePaths.Count) fields$groupNote)")
}
}
# Templates — count with binding types
$tplDefs = $root.SelectNodes("s:template", $ns)
$fieldTpls = $root.SelectNodes("s:fieldTemplate", $ns)
$groupTpls = $root.SelectNodes("s:groupTemplate", $ns)
$groupHeaderTpls = $root.SelectNodes("s:groupHeaderTemplate", $ns)
$groupFooterTpls = $root.SelectNodes("s:groupFooterTemplate", $ns)
$totalBindings = $fieldTpls.Count + $groupTpls.Count + $groupHeaderTpls.Count + $groupFooterTpls.Count
if ($tplDefs.Count -gt 0) {
$parts = @()
if ($fieldTpls.Count -gt 0) { $parts += "$($fieldTpls.Count) field" }
$grpCount = $groupTpls.Count + $groupHeaderTpls.Count + $groupFooterTpls.Count
if ($grpCount -gt 0) { $parts += "$grpCount group" }
if ($parts.Count -gt 0) {
$lines.Add("Templates: $($tplDefs.Count) defined ($($parts -join ', ') bindings)")
} else {
$lines.Add("Templates: $($tplDefs.Count) defined")
}
}
# Parameters — split visible/hidden
$params = $root.SelectNodes("s:parameter", $ns)
if ($params.Count -gt 0) {
$visibleNames = @()
$hiddenCount = 0
foreach ($p in $params) {
$pName = $p.SelectSingleNode("s:name", $ns).InnerText
$useRestrict = $p.SelectSingleNode("s:useRestriction", $ns)
$isHidden = ($useRestrict -and $useRestrict.InnerText -eq "true")
if ($isHidden) { $hiddenCount++ } else { $visibleNames += $pName }
}
$paramLine = "Params: $($params.Count)"
if ($hiddenCount -gt 0 -and $visibleNames.Count -gt 0) {
$paramLine += " ($($visibleNames.Count) visible, $hiddenCount hidden)"
} elseif ($hiddenCount -eq $params.Count) {
$paramLine += " (all hidden)"
}
if ($visibleNames.Count -gt 0 -and $visibleNames.Count -le 8) {
$paramLine += ": " + ($visibleNames -join ", ")
}
$lines.Add($paramLine)
} else {
$lines.Add("Params: (none)")
}
$lines.Add("")
# Variants
$variants = $root.SelectNodes("s:settingsVariant", $ns)
if ($variants.Count -gt 0) {
$lines.Add("Variants:")
$varIdx = 0
foreach ($v in $variants) {
$varIdx++
$vName = $v.SelectSingleNode("dcsset:name", $ns).InnerText
$vPres = $v.SelectSingleNode("dcsset:presentation", $ns)
$vPresStr = ""
if ($vPres) {
$pt = Get-MLText $vPres
if ($pt) { $vPresStr = " `"$pt`"" }
}
$settings = $v.SelectSingleNode("dcsset:settings", $ns)
$structItems = @()
if ($settings) {
foreach ($si in $settings.SelectNodes("dcsset:item", $ns)) {
$siType = Get-StructureItemType $si
$groupFields = Get-GroupFields $si
$groupStr = if ($groupFields.Count -gt 0) { "(" + ($groupFields -join ",") + ")" } else { "(detail)" }
$structItems += "$siType$groupStr"
}
}
# Compact: if many identical items, show count
if ($structItems.Count -gt 3) {
$grouped = $structItems | Group-Object | Sort-Object Count -Descending
$compactParts = @()
foreach ($g in $grouped) {
if ($g.Count -gt 1) { $compactParts += "$($g.Count)x $($g.Name)" }
else { $compactParts += $g.Name }
}
$structItems = $compactParts
}
$structStr = if ($structItems.Count -gt 0) { " " + ($structItems -join ", ") } else { "" }
$filterCount = 0
if ($settings) {
$filterCount = $settings.SelectNodes("dcsset:filter/dcsset:item", $ns).Count
}
$filterStr = if ($filterCount -gt 0) { " $filterCount filters" } else { "" }
$lines.Add(" [$varIdx] $vName$vPresStr$structStr$filterStr")
}
}
}
function Show-OverviewHints {
# Hints — suggest next commands
$lines.Add("")
$hints = @()
# Collect query dataset names for hint
$queryDsNames = @()
foreach ($ds in $root.SelectNodes("s:dataSet", $ns)) {
$dsType = Get-DataSetType $ds
if ($dsType -eq "Query") {
$queryDsNames += $ds.SelectSingleNode("s:name", $ns).InnerText
} elseif ($dsType -eq "Union") {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
if ((Get-DataSetType $subDs) -eq "Query") {
$sn = $subDs.SelectSingleNode("s:name", $ns)
if ($sn) { $queryDsNames += $sn.InnerText }
}
}
}
}
if ($queryDsNames.Count -eq 1) {
$hints += "-Mode query query text"
} elseif ($queryDsNames.Count -gt 1) {
$hints += "-Mode query -Name <ds> query text ($($queryDsNames -join ', '))"
}
$hints += "-Mode fields field tables by dataset"
$linkCount = $root.SelectNodes("s:dataSetLink", $ns).Count
if ($linkCount -gt 0) {
$hints += "-Mode links dataset connections ($linkCount)"
}
$calcCount = $root.SelectNodes("s:calculatedField", $ns).Count
$totalCount = $root.SelectNodes("s:totalField", $ns).Count
if ($calcCount -gt 0) {
$hints += "-Mode calculated calculated field expressions ($calcCount)"
}
if ($totalCount -gt 0) {
$hints += "-Mode resources resource aggregation ($totalCount)"
}
$params = $root.SelectNodes("s:parameter", $ns)
if ($params.Count -gt 0) {
$hints += "-Mode params parameter details"
}
$variants = $root.SelectNodes("s:settingsVariant", $ns)
if ($variants.Count -eq 1) {
$hints += "-Mode variant variant structure"
} elseif ($variants.Count -gt 1) {
$hints += "-Mode variant -Name <N> variant structure (1..$($variants.Count))"
}
$tplDefs = $root.SelectNodes("s:template", $ns)
if ($tplDefs.Count -gt 0) {
$hints += "-Mode templates template bindings and expressions"
}
$hints += "-Mode trace -Name <f> trace field origin (by name or title)"
$hints += "-Mode full all sections at once"
$lines.Add("Next:")
foreach ($h in $hints) { $lines.Add(" $h") }
}
# ============================================================
# MODE: overview
# ============================================================
if ($Mode -eq "overview") {
Show-Overview
Show-OverviewHints
}
function Show-Query {
# Find dataset
$dataSets = $root.SelectNodes("s:dataSet", $ns)
$targetDs = $null
if ($Name) {
# Search by name: prefer nested Query items over parent Union
# Pass 1: search nested items first
foreach ($ds in $dataSets) {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
$subNameNode = $subDs.SelectSingleNode("s:name", $ns)
if ($subNameNode -and $subNameNode.InnerText -eq $Name) { $targetDs = $subDs; break }
}
if ($targetDs) { break }
}
# Pass 2: search top-level
if (-not $targetDs) {
foreach ($ds in $dataSets) {
$dsNameNode = $ds.SelectSingleNode("s:name", $ns)
if ($dsNameNode -and $dsNameNode.InnerText -eq $Name) { $targetDs = $ds; break }
}
}
if (-not $targetDs) {
Write-Error "Dataset '$Name' not found"
exit 1
}
} else {
# Take first Query dataset
foreach ($ds in $dataSets) {
$dsType = Get-DataSetType $ds
if ($dsType -eq "Query") { $targetDs = $ds; break }
if ($dsType -eq "Union") {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
if ((Get-DataSetType $subDs) -eq "Query") { $targetDs = $subDs; break }
}
if ($targetDs) { break }
}
}
if (-not $targetDs) {
Write-Error "No Query dataset found"
exit 1
}
}
$queryNode = $targetDs.SelectSingleNode("s:query", $ns)
if (-not $queryNode) {
# If this is a Union, list nested query datasets
$dsType = Get-DataSetType $targetDs
if ($dsType -eq "Union") {
$subNames = @()
foreach ($subDs in $targetDs.SelectNodes("s:item", $ns)) {
$sn = $subDs.SelectSingleNode("s:name", $ns)
if ($sn) { $subNames += $sn.InnerText }
}
Write-Error "Dataset '$($targetDs.SelectSingleNode("s:name", $ns).InnerText)' is a Union. Specify nested: $($subNames -join ', ')"
} else {
Write-Error "Dataset has no query element"
}
exit 1
}
$rawQuery = Unescape-Xml $queryNode.InnerText
# Raw mode: emit verbatim query text only (no headers/TOC/batch split) for round-trip
if ($Raw) {
foreach ($ql in ($rawQuery.Trim() -split "`n")) { $lines.Add($ql.TrimEnd()) }
return
}
$dsNameStr = $targetDs.SelectSingleNode("s:name", $ns).InnerText
# Split into batches
$batches = @()
$batchTexts = $rawQuery -split ';\s*\r?\n\s*/{16,}\s*\r?\n'
foreach ($bt in $batchTexts) {
$trimmed = $bt.Trim()
if ($trimmed) { $batches += $trimmed }
}
$totalQueryLines = ($rawQuery -split "`n").Count
if ($batches.Count -le 1) {
# Single query
$lines.Add("=== Query: $dsNameStr ($totalQueryLines lines) ===")
$lines.Add("")
foreach ($ql in ($rawQuery.Trim() -split "`n")) {
$lines.Add($ql.TrimEnd())
}
} else {
$lines.Add("=== Query: $dsNameStr ($totalQueryLines lines, $($batches.Count) batches) ===")
if ($Batch -eq 0) {
# Show TOC
$lineNum = 1
for ($bi = 0; $bi -lt $batches.Count; $bi++) {
$batchLines = ($batches[$bi] -split "`n")
$endLine = $lineNum + $batchLines.Count - 1
# Detect ПОМЕСТИТЬ target
$target = ""
foreach ($bl in $batchLines) {
if ($bl -match '^\s*(?:ПОМЕСТИТЬ|INTO)\s+(\S+)') {
$target = [char]0x2192 + " " + $Matches[1]
break
}
}
$lines.Add(" Batch $($bi + 1): lines $lineNum-$endLine $target")
$lineNum = $endLine + 3 # +separator
}
$lines.Add("")
# Show all batches
for ($bi = 0; $bi -lt $batches.Count; $bi++) {
$lines.Add("--- Batch $($bi + 1) ---")
foreach ($ql in ($batches[$bi] -split "`n")) {
$lines.Add($ql.TrimEnd())
}
$lines.Add("")
}
} else {
# Show specific batch
if ($Batch -gt $batches.Count) {
Write-Error "Batch $Batch not found (total: $($batches.Count))"
exit 1
}
$lines.Add("")
$lines.Add("--- Batch $Batch ---")
foreach ($ql in ($batches[$Batch - 1] -split "`n")) {
$lines.Add($ql.TrimEnd())
}
}
}
}
# ============================================================
# MODE: query
# ============================================================
if ($Mode -eq "query") {
Show-Query
}
function Show-Fields {
$dataSets = $root.SelectNodes("s:dataSet", $ns)
function Show-DataSetFields($dsNode) {
$dsType = Get-DataSetType $dsNode
$dsNameStr = $dsNode.SelectSingleNode("s:name", $ns).InnerText
$fields = $dsNode.SelectNodes("s:field", $ns)
$lines.Add("=== Fields: $dsNameStr [$dsType] ($($fields.Count)) ===")
$lines.Add(" dataPath title role restrict format")
foreach ($f in $fields) {
$dp = $f.SelectSingleNode("s:dataPath", $ns)
$dpStr = if ($dp) { $dp.InnerText } else { "-" }
$titleNode = $f.SelectSingleNode("s:title", $ns)
$titleStr = if ($titleNode) { Get-MLText $titleNode } else { "" }
if (-not $titleStr) { $titleStr = "-" }
# Role
$role = $f.SelectSingleNode("s:role", $ns)
$roleStr = "-"
if ($role) {
$roleParts = @()
foreach ($child in $role.ChildNodes) {
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
$roleParts += $child.LocalName
}
}
if ($roleParts.Count -gt 0) { $roleStr = $roleParts -join "," }
}
# UseRestriction
$restrict = $f.SelectSingleNode("s:useRestriction", $ns)
$restrictStr = "-"
if ($restrict) {
$restrictParts = @()
foreach ($child in $restrict.ChildNodes) {
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
$restrictParts += $child.LocalName.Substring(0, [Math]::Min(4, $child.LocalName.Length))
}
}
if ($restrictParts.Count -gt 0) { $restrictStr = $restrictParts -join "," }
}
# Appearance format
$formatStr = "-"
$appearance = $f.SelectSingleNode("s:appearance", $ns)
if ($appearance) {
foreach ($appItem in $appearance.SelectNodes("dcscor:item", $ns)) {
$paramNode = $appItem.SelectSingleNode("dcscor:parameter", $ns)
$valNode = $appItem.SelectSingleNode("dcscor:value", $ns)
if ($paramNode -and ($paramNode.InnerText -eq "Формат" -or $paramNode.InnerText -eq "Format") -and $valNode) {
$formatStr = $valNode.InnerText
}
}
}
# presentationExpression
$presExpr = $f.SelectSingleNode("s:presentationExpression", $ns)
$presStr = ""
if ($presExpr) { $presStr = " presExpr" }
$dpPad = $dpStr.PadRight(35)
$titlePad = $titleStr.PadRight(22)
$rolePad = $roleStr.PadRight(10)
$restrictPad = $restrictStr.PadRight(12)
$lines.Add(" $dpPad $titlePad $rolePad $restrictPad $formatStr$presStr")
}
}
if ($Name) {
# Detail for specific field by dataPath — search all datasets
$found = $false
$matchedIn = @()
function Collect-FieldInfo($dsNode) {
$dsType = Get-DataSetType $dsNode
$dsNameStr = $dsNode.SelectSingleNode("s:name", $ns).InnerText
foreach ($f in $dsNode.SelectNodes("s:field", $ns)) {
$dp = $f.SelectSingleNode("s:dataPath", $ns)
if (-not $dp -or $dp.InnerText -ne $Name) { continue }
$info = @{ dataset = "$dsNameStr [$dsType]" }
$titleNode = $f.SelectSingleNode("s:title", $ns)
$info.title = if ($titleNode) { Get-MLText $titleNode } else { "" }
# ValueType
$vt = $f.SelectSingleNode("s:valueType", $ns)
$info.type = if ($vt) { Get-CompactType $vt } else { "" }
# Role
$role = $f.SelectSingleNode("s:role", $ns)
$roleParts = @()
if ($role) {
foreach ($child in $role.ChildNodes) {
if ($child.NodeType -ne "Element") { continue }
$txt = $child.InnerText.Trim()
if ($txt -eq "true") {
$roleParts += $child.LocalName
} elseif ($txt -eq "false") {
# skip default-false flags
} else {
$roleParts += "$($child.LocalName)=$txt"
}
}
}
$info.role = $roleParts -join ", "
# UseRestriction
$restrict = $f.SelectSingleNode("s:useRestriction", $ns)
$restrictParts = @()
if ($restrict) {
foreach ($child in $restrict.ChildNodes) {
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
$restrictParts += $child.LocalName
}
}
}
$info.restrict = $restrictParts -join ", "
# Format
$formatStr = ""
$appearance = $f.SelectSingleNode("s:appearance", $ns)
if ($appearance) {
foreach ($appItem in $appearance.SelectNodes("dcscor:item", $ns)) {
$pn = $appItem.SelectSingleNode("dcscor:parameter", $ns)
$vn = $appItem.SelectSingleNode("dcscor:value", $ns)
if ($pn -and ($pn.InnerText -eq "Формат" -or $pn.InnerText -eq "Format") -and $vn) {
$formatStr = $vn.InnerText
}
}
}
$info.format = $formatStr
# PresentationExpression
$presExpr = $f.SelectSingleNode("s:presentationExpression", $ns)
$info.presExpr = if ($presExpr) { $presExpr.InnerText } else { "" }
return $info
}
return $null
}
# Search all datasets and nested items
$fieldInfos = @()
foreach ($ds in $dataSets) {
$info = Collect-FieldInfo $ds
if ($info) { $fieldInfos += $info }
$dsType = Get-DataSetType $ds
if ($dsType -eq "Union") {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
$info = Collect-FieldInfo $subDs
if ($info) { $fieldInfos += $info }
}
}
}
if ($fieldInfos.Count -eq 0) {
Write-Error "Field '$Name' not found in any dataset"
exit 1
}
# Use first match for detail (they usually share the same properties)
$first = $fieldInfos[0]
$titleStr = if ($first.title) { " `"$($first.title)`"" } else { "" }
$lines.Add("=== Field: $Name$titleStr ===")
$lines.Add("")
# Datasets
$dsList = ($fieldInfos | ForEach-Object { $_.dataset }) -join ", "
$lines.Add("Dataset: $dsList")
if ($first.type) { $lines.Add("Type: $($first.type)") }
if ($first.role) { $lines.Add("Role: $($first.role)") }
if ($first.restrict) { $lines.Add("Restrict: $($first.restrict)") }
if ($first.format) { $lines.Add("Format: $($first.format)") }
if ($first.presExpr) {
$lines.Add("PresentationExpression:")
foreach ($el in ($first.presExpr -split "`n")) { $lines.Add(" $($el.TrimEnd())") }
}
} else {
# Compact map: field names per dataset
$lines.Add("=== Fields map ===")
function Show-DataSetFieldMap($dsNode, $indent) {
$dsType = Get-DataSetType $dsNode
$dsNameStr = $dsNode.SelectSingleNode("s:name", $ns).InnerText
$fields = $dsNode.SelectNodes("s:field", $ns)
$fieldNames = @()
foreach ($f in $fields) {
$dp = $f.SelectSingleNode("s:dataPath", $ns)
if ($dp) { $fieldNames += $dp.InnerText }
}
$nameList = $fieldNames -join ", "
if ($nameList.Length -gt 100) {
$nameList = $nameList.Substring(0, 97) + "..."
}
$lines.Add("$indent$dsNameStr [$dsType] ($($fields.Count)): $nameList")
}
foreach ($ds in $dataSets) {
Show-DataSetFieldMap $ds ""
$dsType = Get-DataSetType $ds
if ($dsType -eq "Union") {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
Show-DataSetFieldMap $subDs " "
}
}
}
$lines.Add("")
$lines.Add("Use -Name <field> for details.")
}
}
# ============================================================
# MODE: fields
# ============================================================
if ($Mode -eq "fields") {
Show-Fields
}
# ============================================================
# MODE: links
# ============================================================
elseif ($Mode -eq "links") {
$links = $root.SelectNodes("s:dataSetLink", $ns)
if ($links.Count -eq 0) {
$lines.Add("(no links)")
} else {
$lines.Add("=== Links ($($links.Count)) ===")
$lines.Add("")
# Group by source->dest pair
$currentPair = ""
foreach ($lnk in $links) {
$srcDs = $lnk.SelectSingleNode("s:sourceDataSet", $ns).InnerText
$dstDs = $lnk.SelectSingleNode("s:destinationDataSet", $ns).InnerText
$srcExpr = $lnk.SelectSingleNode("s:sourceExpression", $ns).InnerText
$dstExpr = $lnk.SelectSingleNode("s:destinationExpression", $ns).InnerText
$paramNode = $lnk.SelectSingleNode("s:parameter", $ns)
$paramListNode = $lnk.SelectSingleNode("s:parameterListAllowed", $ns)
$pair = "$srcDs -> $dstDs"
if ($pair -ne $currentPair) {
if ($currentPair) { $lines.Add("") }
$lines.Add("$pair :")
$currentPair = $pair
}
$paramStr = ""
if ($paramNode) { $paramStr = " param=$($paramNode.InnerText)" }
$lines.Add(" $srcExpr -> $dstExpr$paramStr")
}
}
}
# ============================================================
# MODE: calculated
# ============================================================
elseif ($Mode -eq "calculated") {
$calcFields = $root.SelectNodes("s:calculatedField", $ns)
if ($calcFields.Count -eq 0) {
$lines.Add("(no calculated fields)")
} elseif ($Name) {
$found = $false
foreach ($cf in $calcFields) {
$cfPath = $cf.SelectSingleNode("s:dataPath", $ns).InnerText
if ($cfPath -eq $Name) {
$lines.Add("=== Calculated: $cfPath ===")
$lines.Add("")
$cfExpr = $cf.SelectSingleNode("s:expression", $ns).InnerText
$lines.Add("Expression:")
foreach ($el in ($cfExpr -split "`n")) { $lines.Add(" $($el.TrimEnd())") }
$cfTitle = $cf.SelectSingleNode("s:title", $ns)
if ($cfTitle) {
$t = Get-MLText $cfTitle
if ($t) { $lines.Add("Title: $t") }
}
$cfRestrict = $cf.SelectSingleNode("s:useRestriction", $ns)
if ($cfRestrict) {
$parts = @()
foreach ($child in $cfRestrict.ChildNodes) {
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
$parts += $child.LocalName
}
}
if ($parts.Count -gt 0) { $lines.Add("Restrict: $($parts -join ', ')") }
}
$found = $true
break
}
}
if (-not $found) {
Write-Error "Calculated field '$Name' not found"
exit 1
}
} else {
# Map
$lines.Add("=== Calculated fields ($($calcFields.Count)) ===")
foreach ($cf in $calcFields) {
$cfPath = $cf.SelectSingleNode("s:dataPath", $ns).InnerText
$cfTitle = $cf.SelectSingleNode("s:title", $ns)
$titleStr = ""
if ($cfTitle) {
$t = Get-MLText $cfTitle
if ($t -and $t -ne $cfPath) { $titleStr = " `"$t`"" }
}
$lines.Add(" $cfPath$titleStr")
}
$lines.Add("")
$lines.Add("Use -Name <field> for full expression.")
}
}
function Show-Resources {
$totalFields = $root.SelectNodes("s:totalField", $ns)
if ($totalFields.Count -eq 0) {
$lines.Add("(no resources)")
} elseif ($Name) {
$matched = @()
foreach ($tf in $totalFields) {
$tfPath = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
if ($tfPath -eq $Name) { $matched += $tf }
}
if ($matched.Count -eq 0) {
Write-Error "Resource '$Name' not found"
exit 1
}
$lines.Add("=== Resource: $Name ===")
$lines.Add("")
foreach ($tf in $matched) {
$tfExpr = $tf.SelectSingleNode("s:expression", $ns).InnerText
$tfGroup = $tf.SelectSingleNode("s:group", $ns)
$groupStr = "(overall)"
if ($tfGroup) { $groupStr = $tfGroup.InnerText }
$lines.Add(" [$groupStr] $tfExpr")
}
} else {
# Map
$lines.Add("=== Resources ($($totalFields.Count)) ===")
$resMap = [ordered]@{}
foreach ($tf in $totalFields) {
$tfPath = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
$tfGroup = $tf.SelectSingleNode("s:group", $ns)
if (-not $resMap.Contains($tfPath)) {
$resMap[$tfPath] = @{ hasGroup = $false }
}
if ($tfGroup) { $resMap[$tfPath].hasGroup = $true }
}
foreach ($key in $resMap.Keys) {
$groupMark = if ($resMap[$key].hasGroup) { " *" } else { "" }
$lines.Add(" $key$groupMark")
}
$lines.Add("")
$lines.Add(" * = has group-level formulas")
$lines.Add("")
$lines.Add("Use -Name <field> for full formula.")
}
}
# ============================================================
# MODE: resources
# ============================================================
if ($Mode -eq "resources") {
Show-Resources
}
function Show-Params {
$params = $root.SelectNodes("s:parameter", $ns)
$lines.Add("=== Parameters ($($params.Count)) ===")
$lines.Add(" Name Type Default Visible Expression")
foreach ($p in $params) {
$pName = $p.SelectSingleNode("s:name", $ns).InnerText
$pType = Get-CompactType $p.SelectSingleNode("s:valueType", $ns)
if (-not $pType) { $pType = "-" }
# Default value
$valNode = $p.SelectSingleNode("s:value", $ns)
$valStr = "-"
if ($valNode) {
$nilAttr = $valNode.GetAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance")
if ($nilAttr -eq "true") {
$valStr = "null"
} else {
$raw = $valNode.InnerText.Trim()
if ($raw -eq "0001-01-01T00:00:00") {
$valStr = "-"
} elseif ($raw) {
# Check for StandardPeriod variant
$variant = $valNode.SelectSingleNode("v8:variant", $ns)
if ($variant) {
$valStr = $variant.InnerText
} else {
$valStr = $raw
if ($valStr.Length -gt 15) { $valStr = $valStr.Substring(0, 12) + "..." }
}
}
}
}
# Visibility
$useRestrict = $p.SelectSingleNode("s:useRestriction", $ns)
$visStr = "yes"
if ($useRestrict -and $useRestrict.InnerText -eq "true") { $visStr = "hidden" }
if ($useRestrict -and $useRestrict.InnerText -eq "false") { $visStr = "yes" }
# Expression
$exprNode = $p.SelectSingleNode("s:expression", $ns)
$exprStr = "-"
if ($exprNode -and $exprNode.InnerText.Trim()) {
$exprStr = Unescape-Xml $exprNode.InnerText.Trim()
}
# availableAsField
$availField = $p.SelectSingleNode("s:availableAsField", $ns)
$availStr = ""
if ($availField -and $availField.InnerText -eq "false") { $availStr = " [noField]" }
$namePad = $pName.PadRight(33)
$typePad = $pType.PadRight(22)
$valPad = $valStr.PadRight(16)
$visPad = $visStr.PadRight(8)
$lines.Add(" $namePad $typePad $valPad $visPad $exprStr$availStr")
}
}
# ============================================================
# MODE: params
# ============================================================
if ($Mode -eq "params") {
Show-Params
}
function Show-Variant {
$variants = $root.SelectNodes("s:settingsVariant", $ns)
if (-not $Name) {
# --- Variant list (map) ---
if ($variants.Count -eq 0) {
$lines.Add("=== Variants: (none) ===")
} else {
$lines.Add("=== Variants ($($variants.Count)) ===")
$varIdx = 0
foreach ($v in $variants) {
$varIdx++
$vName = $v.SelectSingleNode("dcsset:name", $ns).InnerText
$vPres = $v.SelectSingleNode("dcsset:presentation", $ns)
$vPresStr = ""
if ($vPres) {
$pt = Get-MLText $vPres
if ($pt) { $vPresStr = " `"$pt`"" }
}
$settings = $v.SelectSingleNode("dcsset:settings", $ns)
$structItems = @()
if ($settings) {
foreach ($si in $settings.SelectNodes("dcsset:item", $ns)) {
$siType = Get-StructureItemType $si
$groupFields = Get-GroupFields $si
$groupStr = if ($groupFields.Count -gt 0) { "(" + ($groupFields -join ",") + ")" } else { "(detail)" }
$structItems += "$siType$groupStr"
}
}
if ($structItems.Count -gt 3) {
$grouped = $structItems | Group-Object | Sort-Object Count -Descending
$compactParts = @()
foreach ($g in $grouped) {
if ($g.Count -gt 1) { $compactParts += "$($g.Count)x $($g.Name)" }
else { $compactParts += $g.Name }
}
$structItems = $compactParts
}
$structStr = if ($structItems.Count -gt 0) { " " + ($structItems -join ", ") } else { "" }
$filterCount = 0
if ($settings) {
$filterCount = $settings.SelectNodes("dcsset:filter/dcsset:item", $ns).Count
}
$filterStr = if ($filterCount -gt 0) { " $filterCount filters" } else { "" }
# Selection fields
$selFields = @()
if ($settings) { $selFields = Get-SelectionFields $settings }
$selStr = if ($selFields.Count -gt 0) { " sel: " + ($selFields -join ", ") } else { "" }
$lines.Add(" [$varIdx] $vName$vPresStr$structStr$filterStr")
if ($selStr) { $lines.Add(" $selStr") }
}
}
} else {
# --- Variant detail ---
$targetVariant = $null
$varIdx = 0
foreach ($v in $variants) {
$varIdx++
$vName = $v.SelectSingleNode("dcsset:name", $ns).InnerText
if ($vName -eq $Name -or "$varIdx" -eq $Name) {
$targetVariant = $v
$matchIdx = $varIdx
break
}
}
if (-not $targetVariant) {
Write-Error "Variant '$Name' not found. Use -Mode variant without -Name to see list."
exit 1
}
$vName = $targetVariant.SelectSingleNode("dcsset:name", $ns).InnerText
$vPres = $targetVariant.SelectSingleNode("dcsset:presentation", $ns)
$vPresStr = ""
if ($vPres) {
$pt = Get-MLText $vPres
if ($pt) { $vPresStr = " `"$pt`"" }
}
$lines.Add("=== Variant [$matchIdx]: $vName$vPresStr ===")
$settings = $targetVariant.SelectSingleNode("dcsset:settings", $ns)
if (-not $settings) {
$lines.Add(" (empty settings)")
} else {
# Selection at settings level
$topSel = Get-SelectionFields $settings
if ($topSel.Count -gt 0) {
$lines.Add("")
$lines.Add("Selection: " + ($topSel -join ", "))
}
# Structure
$structItems = $settings.SelectNodes("dcsset:item", $ns)
$hasStruct = $false
foreach ($si in $structItems) {
$siXsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($siXsiType -like "*StructureItem*") { $hasStruct = $true; break }
}
if ($hasStruct) {
$lines.Add("")
$lines.Add("Structure:")
foreach ($si in $structItems) {
$siXsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($siXsiType -like "*StructureItem*") {
Build-StructureTree -itemNode $si -prefix " " -isLast $false -outLines $lines
}
}
}
# Filter
$filters = Get-FilterSummary $settings
if ($filters.Count -gt 0) {
$lines.Add("")
$lines.Add("Filter:")
foreach ($f in $filters) {
$lines.Add(" $f")
}
}
# Data parameters
$dataParams = $settings.SelectNodes("dcsset:dataParameters/dcsset:item", $ns)
if ($dataParams.Count -gt 0) {
$dpStrs = @()
foreach ($dp in $dataParams) {
$dpParam = $dp.SelectSingleNode("dcscor:parameter", $ns)
$dpVal = $dp.SelectSingleNode("dcscor:value", $ns)
if ($dpParam -and $dpVal) {
$dpStrs += "$($dpParam.InnerText)=`"$($dpVal.InnerText)`""
}
}
if ($dpStrs.Count -gt 0) {
$lines.Add("")
$lines.Add("DataParams: " + ($dpStrs -join ", "))
}
}
# Output parameters
$outParams = $settings.SelectNodes("dcsset:outputParameters/dcscor:item", $ns)
if ($outParams.Count -gt 0) {
$opStrs = @()
foreach ($op in $outParams) {
$opParam = $op.SelectSingleNode("dcscor:parameter", $ns)
$opVal = $op.SelectSingleNode("dcscor:value", $ns)
if ($opParam -and $opVal) {
$paramName = $opParam.InnerText
$paramVal = $opVal.InnerText
# Shorten known long names
switch ($paramName) {
"МакетОформления" { $opStrs += "style=$paramVal" }
"РасположениеПолейГруппировки" { $opStrs += "groups=$paramVal" }
"ГоризонтальноеРасположениеОбщихИтогов" { $opStrs += "totalsH=$paramVal" }
"ВертикальноеРасположениеОбщихИтогов" { $opStrs += "totalsV=$paramVal" }
"ВыводитьЗаголовок" { $opStrs += "header=$paramVal" }
"ВыводитьОтбор" { $opStrs += "filter=$paramVal" }
"ВыводитьПараметрыДанных" { $opStrs += "dataParams=$paramVal" }
"РасположениеРеквизитов" { $opStrs += "attrs=$paramVal" }
default { $opStrs += "$paramName=$paramVal" }
}
}
}
if ($opStrs.Count -gt 0) {
$lines.Add("")
$lines.Add("Output: " + ($opStrs -join " "))
}
}
}
} # end else (variant detail)
}
# ============================================================
# MODE: variant
# ============================================================
if ($Mode -eq "variant") {
Show-Variant
}
# ============================================================
# MODE: full
# ============================================================
elseif ($Mode -eq "full") {
Show-Overview
$lines.Add(""); $lines.Add("--- query ---"); $lines.Add("")
$hasQuery = $root.SelectNodes("descendant::s:dataSet[@xsi:type='DataSetQuery']", $ns).Count -gt 0
if ($hasQuery) {
Show-Query
} else {
$objNodes = $root.SelectNodes("descendant::s:dataSet[@xsi:type='DataSetObject']/s:objectName", $ns)
if ($objNodes.Count -gt 0) {
$names = @(); foreach ($n in $objNodes) { $names += $n.InnerText }
$lines.Add("(no query datasets; external datasets: $($names -join ', '))")
} else {
$lines.Add("(no query datasets)")
}
}
$lines.Add(""); $lines.Add("--- fields ---"); $lines.Add("")
Show-Fields
$lines.Add(""); $lines.Add("--- resources ---"); $lines.Add("")
Show-Resources
$lines.Add(""); $lines.Add("--- params ---"); $lines.Add("")
Show-Params
$lines.Add(""); $lines.Add("--- variant ---"); $lines.Add("")
Show-Variant
}
# ============================================================
# MODE: trace
# ============================================================
elseif ($Mode -eq "trace") {
if (-not $Name) {
Write-Error "Trace mode requires -Name <field_name_or_title>"
exit 1
}
# --- Build field index ---
$dsFields = @{} # dataPath -> @{ datasets=@(); title="" }
$calcFields = @{} # dataPath -> @{ expression=""; title="" }
$resFields = @{} # dataPath -> @(@{ expression=""; group="" })
$titleMap = @{} # title -> dataPath
# Scan dataset fields (including nested Union items)
$dataSets = $root.SelectNodes("s:dataSet", $ns)
foreach ($ds in $dataSets) {
$dsName = $ds.SelectSingleNode("s:name", $ns).InnerText
$dsType = Get-DataSetType $ds
foreach ($f in $ds.SelectNodes("s:field", $ns)) {
$dp = $f.SelectSingleNode("s:dataPath", $ns)
if (-not $dp) { continue }
$dpStr = $dp.InnerText
if (-not $dsFields.ContainsKey($dpStr)) {
$dsFields[$dpStr] = @{ datasets = @(); title = "" }
}
$dsFields[$dpStr].datasets += "$dsName [$dsType]"
$titleNode = $f.SelectSingleNode("s:title", $ns)
if ($titleNode) {
$t = Get-MLText $titleNode
if ($t) {
if (-not $dsFields[$dpStr].title) { $dsFields[$dpStr].title = $t }
if (-not $titleMap.ContainsKey($t)) { $titleMap[$t] = $dpStr }
}
}
}
if ($dsType -eq "Union") {
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
$subName = $subDs.SelectSingleNode("s:name", $ns).InnerText
$subType = Get-DataSetType $subDs
foreach ($f in $subDs.SelectNodes("s:field", $ns)) {
$dp = $f.SelectSingleNode("s:dataPath", $ns)
if (-not $dp) { continue }
$dpStr = $dp.InnerText
if (-not $dsFields.ContainsKey($dpStr)) {
$dsFields[$dpStr] = @{ datasets = @(); title = "" }
}
$dsFields[$dpStr].datasets += "$subName [$subType]"
$titleNode = $f.SelectSingleNode("s:title", $ns)
if ($titleNode) {
$t = Get-MLText $titleNode
if ($t) {
if (-not $dsFields[$dpStr].title) { $dsFields[$dpStr].title = $t }
if (-not $titleMap.ContainsKey($t)) { $titleMap[$t] = $dpStr }
}
}
}
}
}
}
# Scan calculated fields
foreach ($cf in $root.SelectNodes("s:calculatedField", $ns)) {
$dpStr = $cf.SelectSingleNode("s:dataPath", $ns).InnerText
$expr = $cf.SelectSingleNode("s:expression", $ns).InnerText
$cfTitle = $cf.SelectSingleNode("s:title", $ns)
$t = ""
if ($cfTitle) { $t = Get-MLText $cfTitle }
$calcFields[$dpStr] = @{ expression = $expr; title = $t }
if ($t -and -not $titleMap.ContainsKey($t)) { $titleMap[$t] = $dpStr }
}
# Scan resources
foreach ($tf in $root.SelectNodes("s:totalField", $ns)) {
$dpStr = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
$expr = $tf.SelectSingleNode("s:expression", $ns).InnerText
$grp = $tf.SelectSingleNode("s:group", $ns)
$groupStr = "(overall)"
if ($grp) { $groupStr = $grp.InnerText }
if (-not $resFields.ContainsKey($dpStr)) { $resFields[$dpStr] = @() }
$resFields[$dpStr] += @{ expression = $expr; group = $groupStr }
}
# --- Resolve name: try dataPath, then exact title, then substring title ---
$targetPath = $Name
$knownPaths = @()
$knownPaths += $dsFields.Keys
$knownPaths += $calcFields.Keys
$knownPaths += $resFields.Keys
$isKnown = $knownPaths -contains $Name
if (-not $isKnown) {
if ($titleMap.ContainsKey($Name)) {
$targetPath = $titleMap[$Name]
} else {
# Substring match in titles
$matchedTitle = $null
foreach ($key in $titleMap.Keys) {
if ($key -like "*$Name*") {
$matchedTitle = $key
break
}
}
if ($matchedTitle) {
$targetPath = $titleMap[$matchedTitle]
} else {
Write-Error "Field '$Name' not found by dataPath or title"
exit 1
}
}
}
# --- Build output ---
$title = ""
if ($calcFields.ContainsKey($targetPath) -and $calcFields[$targetPath].title) {
$title = $calcFields[$targetPath].title
} elseif ($dsFields.ContainsKey($targetPath) -and $dsFields[$targetPath].title) {
$title = $dsFields[$targetPath].title
}
$titleStr = if ($title) { " `"$title`"" } else { "" }
$lines.Add("=== Trace: $targetPath$titleStr ===")
$lines.Add("")
# Dataset origin
if ($dsFields.ContainsKey($targetPath)) {
$uniqueDs = $dsFields[$targetPath].datasets | Select-Object -Unique
$lines.Add("Dataset: $($uniqueDs -join ', ')")
} else {
$lines.Add("Dataset: (schema-level only, not in dataset fields)")
}
# Calculated field
if ($calcFields.ContainsKey($targetPath)) {
$cf = $calcFields[$targetPath]
$lines.Add("")
$lines.Add("Calculated:")
foreach ($el in ($cf.expression -split "`n")) { $lines.Add(" $($el.TrimEnd())") }
# Extract operands: find known field names in expression
$operands = @()
$allKnown = @()
$allKnown += $dsFields.Keys
$allKnown += $calcFields.Keys
$allKnown = $allKnown | Select-Object -Unique | Where-Object { $_ -ne $targetPath }
# Sort by length descending to match longer names first
$allKnown = $allKnown | Sort-Object -Property Length -Descending
foreach ($fieldName in $allKnown) {
$escaped = [regex]::Escape($fieldName)
if ($cf.expression -match "(?<![а-яА-ЯёЁa-zA-Z0-9_.])$escaped(?![а-яА-ЯёЁa-zA-Z0-9_.])") {
$operands += $fieldName
}
}
if ($operands.Count -gt 0) {
$lines.Add(" Operands:")
foreach ($op in $operands) {
if ($calcFields.ContainsKey($op)) {
$lines.Add(" $op -> calculated")
} elseif ($dsFields.ContainsKey($op)) {
$opDs = ($dsFields[$op].datasets | Select-Object -Unique) -join ", "
$lines.Add(" $op -> $opDs")
} else {
$lines.Add(" $op")
}
}
}
}
# Resource
if ($resFields.ContainsKey($targetPath)) {
$lines.Add("")
$lines.Add("Resource:")
foreach ($r in $resFields[$targetPath]) {
$lines.Add(" [$($r.group)] $($r.expression)")
}
}
# Simple dataset field, no calc/resource
if (-not $calcFields.ContainsKey($targetPath) -and -not $resFields.ContainsKey($targetPath)) {
if ($dsFields.ContainsKey($targetPath)) {
$lines.Add("")
$lines.Add("(direct dataset field, no calculated expression or resource)")
}
}
}
# ============================================================
# MODE: templates
# ============================================================
elseif ($Mode -eq "templates") {
# --- Helper: check if expression is trivial ---
function Is-TrivialExpr([string]$paramName, [string]$expr) {
$e = $expr.Trim()
$n = $paramName.Trim()
if ($e -eq $n) { return $true }
if ($e -eq "Представление($n)") { return $true }
return $false
}
# --- Helper: parse template content (rows/cells) ---
function Get-TemplateContent([System.Xml.XmlNode]$tplNode) {
$innerT = $tplNode.SelectSingleNode("s:template", $ns)
if (-not $innerT) { return @{ rows = 0; cells = @(); params = @(); nonTrivial = @() } }
$rows = $innerT.SelectNodes("dcsat:item", $ns)
$rowCount = $rows.Count
$cellData = [System.Collections.ArrayList]::new()
$rowIdx = 0
foreach ($row in $rows) {
$rowIdx++
$rowCells = [System.Collections.ArrayList]::new()
foreach ($cell in $row.SelectNodes("dcsat:tableCell", $ns)) {
$field = $cell.SelectSingleNode("dcsat:item", $ns)
if (-not $field) {
[void]$rowCells.Add("(empty)")
continue
}
$val = $field.SelectSingleNode("dcsat:value", $ns)
if (-not $val) {
[void]$rowCells.Add("(empty)")
continue
}
$xsiType = $val.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
if ($xsiType -like "*LocalStringType*") {
$text = Get-MLText $val
if ($text) { [void]$rowCells.Add("`"$text`"") }
else { [void]$rowCells.Add("(empty)") }
} elseif ($xsiType -like "*Parameter*") {
[void]$rowCells.Add("{$($val.InnerText)}")
} else {
[void]$rowCells.Add("(?)")
}
}
[void]$cellData.Add(@{ row = $rowIdx; cells = $rowCells.ToArray() })
}
# Parameters
$paramNodes = $tplNode.SelectNodes("s:parameter", $ns)
$paramList = [System.Collections.ArrayList]::new()
$nonTrivialList = [System.Collections.ArrayList]::new()
foreach ($p in $paramNodes) {
$pn = $p.SelectSingleNode("dcsat:name", $ns)
$pe = $p.SelectSingleNode("dcsat:expression", $ns)
if ($pn -and $pe) {
$pName = $pn.InnerText
$pExpr = $pe.InnerText
[void]$paramList.Add(@{ name = $pName; expression = $pExpr })
if (-not (Is-TrivialExpr $pName $pExpr)) {
[void]$nonTrivialList.Add(@{ name = $pName; expression = $pExpr })
}
}
}
return @{
rows = $rowCount
cells = $cellData.ToArray()
params = $paramList.ToArray()
nonTrivial = $nonTrivialList.ToArray()
}
}
# --- Build template name -> node index ---
$tplIndex = @{}
foreach ($t in $root.SelectNodes("s:template", $ns)) {
$tn = $t.SelectSingleNode("s:name", $ns)
if ($tn) { $tplIndex[$tn.InnerText] = $t }
}
# --- Parse bindings ---
# Group bindings: groupTemplate + groupHeaderTemplate
$groupBindings = [ordered]@{} # groupName -> @{ bindings = @(@{type; tplName; tplNode}) }
foreach ($gt in $root.SelectNodes("s:groupTemplate", $ns)) {
$gn = $gt.SelectSingleNode("s:groupName", $ns)
$gf = $gt.SelectSingleNode("s:groupField", $ns)
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
$tt = $gt.SelectSingleNode("s:templateType", $ns)
$tn = $gt.SelectSingleNode("s:template", $ns)
$ttStr = if ($tt) { $tt.InnerText } else { "-" }
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
if (-not $groupBindings.Contains($gnStr)) {
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
}
[void]$groupBindings[$gnStr].Add(@{ type = $ttStr; tplName = $tnStr })
}
foreach ($ght in $root.SelectNodes("s:groupHeaderTemplate", $ns)) {
$gn = $ght.SelectSingleNode("s:groupName", $ns)
$gf = $ght.SelectSingleNode("s:groupField", $ns)
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
$tt = $ght.SelectSingleNode("s:templateType", $ns)
$tn = $ght.SelectSingleNode("s:template", $ns)
$ttStr = if ($tt) { "GroupHeader" } else { "GroupHeader" }
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
if (-not $groupBindings.Contains($gnStr)) {
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
}
[void]$groupBindings[$gnStr].Add(@{ type = $ttStr; tplName = $tnStr })
}
foreach ($gft in $root.SelectNodes("s:groupFooterTemplate", $ns)) {
$gn = $gft.SelectSingleNode("s:groupName", $ns)
$gf = $gft.SelectSingleNode("s:groupField", $ns)
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
$tn = $gft.SelectSingleNode("s:template", $ns)
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
if (-not $groupBindings.Contains($gnStr)) {
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
}
[void]$groupBindings[$gnStr].Add(@{ type = "GroupFooter"; tplName = $tnStr })
}
# Field bindings: fieldTemplate
$fieldBindings = [ordered]@{} # fieldName -> tplName
$fieldNonTrivial = [System.Collections.ArrayList]::new()
foreach ($ft in $root.SelectNodes("s:fieldTemplate", $ns)) {
$fn = $ft.SelectSingleNode("s:field", $ns)
$tn = $ft.SelectSingleNode("s:template", $ns)
if ($fn -and $tn) {
$fName = $fn.InnerText
$tName = $tn.InnerText
$fieldBindings[$fName] = $tName
# Check params for non-trivial expressions
if ($tplIndex.ContainsKey($tName)) {
$content = Get-TemplateContent $tplIndex[$tName]
foreach ($nt in $content.nonTrivial) {
[void]$fieldNonTrivial.Add(@{ field = $fName; template = $tName; name = $nt.name; expression = $nt.expression })
}
}
}
}
$totalTpl = $tplIndex.Count
$fieldCount = $fieldBindings.Count
$groupBindCount = 0
foreach ($k in $groupBindings.Keys) { $groupBindCount += $groupBindings[$k].Count }
if (-not $Name) {
# --- MAP mode ---
$lines.Add("=== Templates ($totalTpl defined: $fieldCount field, $groupBindCount group) ===")
# Field bindings
if ($fieldBindings.Count -gt 0) {
$lines.Add("")
if ($fieldNonTrivial.Count -eq 0) {
$fieldNames = @($fieldBindings.Keys)
if ($fieldNames.Count -le 8) {
$lines.Add("Field bindings ($fieldCount): $($fieldNames -join ', ') (all trivial)")
} else {
$lines.Add("Field bindings ($fieldCount): (all trivial)")
$lines.Add(" $($fieldNames[0..7] -join ', '), ...")
}
} else {
$trivialCount = $fieldBindings.Count - ($fieldNonTrivial | Select-Object -ExpandProperty field -Unique).Count
$lines.Add("Field bindings ($fieldCount, $trivialCount trivial):")
foreach ($nt in $fieldNonTrivial) {
$lines.Add(" $($nt.field): $($nt.name) = $($nt.expression)")
}
}
}
# Group bindings
if ($groupBindings.Count -gt 0) {
$lines.Add("")
$lines.Add("Group bindings ($groupBindCount):")
foreach ($gName in $groupBindings.Keys) {
$bindings = $groupBindings[$gName]
$parts = @()
foreach ($b in $bindings) {
$info = "$($b.type) -> $($b.tplName)"
if ($tplIndex.ContainsKey($b.tplName)) {
$content = Get-TemplateContent $tplIndex[$b.tplName]
# Check if any cell has content
$hasContent = $false
foreach ($r in $content.cells) {
foreach ($c in $r.cells) {
if ($c -ne "(empty)") { $hasContent = $true; break }
}
if ($hasContent) { break }
}
$info += " ($($content.rows) rows"
if ($content.params.Count -gt 0) { $info += ", $($content.params.Count) params" }
$info += ")"
if (-not $hasContent -and $content.params.Count -eq 0) {
$info += " spacer"
}
if ($content.nonTrivial.Count -gt 0) {
$ntNames = ($content.nonTrivial | ForEach-Object { $_.name }) -join ', '
$info += " *$ntNames"
}
}
$parts += $info
}
$lines.Add(" $gName")
foreach ($p in $parts) {
$lines.Add(" $p")
}
}
}
if ($fieldBindings.Count -gt 0 -or $groupBindings.Count -gt 0) {
$lines.Add("")
$lines.Add("Use -Name <group|field> for template details.")
}
} else {
# --- DETAIL mode ---
$found = $false
# Check group bindings first
if ($groupBindings.Contains($Name)) {
$found = $true
$bindings = $groupBindings[$Name]
$lines.Add("=== Templates: $Name ===")
foreach ($b in $bindings) {
$lines.Add("")
$tName = $b.tplName
if (-not $tplIndex.ContainsKey($tName)) {
$lines.Add("$($b.type) -> $tName (template not found)")
continue
}
$content = Get-TemplateContent $tplIndex[$tName]
$cellCount = 0
foreach ($r in $content.cells) { $cellCount += $r.cells.Count }
$lines.Add("$($b.type) -> $tName [$($content.rows) rows, $cellCount cells]:")
foreach ($r in $content.cells) {
$cellStr = $r.cells -join " | "
$lines.Add(" Row $($r.row): $cellStr")
}
if ($content.nonTrivial.Count -gt 0) {
$lines.Add(" Params:")
foreach ($nt in $content.nonTrivial) {
$lines.Add(" $($nt.name) = $($nt.expression)")
}
}
}
}
# Check field bindings
if ($fieldBindings.Contains($Name)) {
if ($found) { $lines.Add("") }
$found = $true
$tName = $fieldBindings[$Name]
$lines.Add("=== Field template: $Name -> $tName ===")
if ($tplIndex.ContainsKey($tName)) {
$content = Get-TemplateContent $tplIndex[$tName]
$cellCount = 0
foreach ($r in $content.cells) { $cellCount += $r.cells.Count }
$lines.Add("[$($content.rows) rows, $cellCount cells]")
foreach ($r in $content.cells) {
$cellStr = $r.cells -join " | "
$lines.Add(" Row $($r.row): $cellStr")
}
if ($content.nonTrivial.Count -gt 0) {
$lines.Add(" Non-trivial params:")
foreach ($nt in $content.nonTrivial) {
$lines.Add(" $($nt.name) = $($nt.expression)")
}
} else {
$lines.Add(" (all params trivial)")
}
}
}
if (-not $found) {
Write-Error "Group or field '$Name' not found in template bindings"
exit 1
}
}
}
# --- Output ---
$result = $lines.ToArray()
$totalLines = $result.Count
# OutFile
if ($OutFile) {
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
if ([System.IO.Path]::IsPathRooted($OutFile)) {
$outPath = [System.IO.Path]::GetFullPath($OutFile)
} else {
$outPath = [System.IO.Path]::GetFullPath((Join-Path (Get-Location).Path $OutFile))
}
[System.IO.File]::WriteAllLines($outPath, $result, $utf8Bom)
Write-Host "Written $totalLines lines to $OutFile"
exit 0
}
# Pagination
if ($Offset -gt 0) {
if ($Offset -ge $totalLines) {
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
exit 0
}
$result = $result[$Offset..($totalLines - 1)]
}
if (-not $Raw -and $result.Count -gt $Limit) {
$shown = $result[0..($Limit - 1)]
foreach ($l in $shown) { Write-Host $l }
Write-Host ""
Write-Host "[TRUNCATED] Shown $Limit of $totalLines lines. Use -Offset $($Offset + $Limit) to continue."
} else {
foreach ($l in $result) { Write-Host $l }
}