# 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("<", "<") $text = $text.Replace(""", '"') $text = $text.Replace("'", "'") 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 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 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 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 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 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 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 " 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 "(? 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 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 } }