Auto-build: codeassistant (powershell) from 2067778

This commit is contained in:
github-actions[bot]
2026-08-01 14:45:21 +00:00
commit 9868e5e4a7
321 changed files with 163418 additions and 0 deletions
@@ -0,0 +1,957 @@
# xdto-compile v1.1 — Build a 1C XDTO package from an XML Schema (XSD)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true, ParameterSetName='File')]
[Alias('Path')]
[string]$XsdPath,
[Parameter(Mandatory=$true, ParameterSetName='Inline')]
[string]$Xsd,
[Parameter(Mandatory=$true)]
[string]$OutputDir,
[string]$Name,
[object]$Synonym,
[string]$Comment,
[switch]$Force
)
$ErrorActionPreference = "Stop"
# Эти пространства имён предоставляет сама платформа — пакетов в конфигурации
# для них нет и быть не должно (выведено по корпусу: импортируются, но
# targetNamespace с таким значением ни у одного пакета нет)
$PLATFORM_NS = @(
"http://v8.1c.ru/8.1/data/core",
"http://v8.1c.ru/8.1/data/enterprise",
"http://v8.1c.ru/8.1/data/enterprise/current-config",
"http://v8.1c.ru/8.1/data-composition-system/settings",
"http://v8.1c.ru/8.3/data/ext",
"http://www.w3.org/2001/XMLSchema"
)
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$XDTO_NS = "http://v8.1c.ru/8.1/xdto"
$XS_NS = "http://www.w3.org/2001/XMLSchema"
$XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"
$MD_NS = "http://v8.1c.ru/8.3/MDClasses"
$V8_NS = "http://v8.1c.ru/8.1/data/core"
# --- Support guard (Ext/ParentConfigurations.bin) ---
# See docs/1c-support-state-spec.md. Blocks edits of vendor objects "на замке" /
# read-only configs unless allowed. Trigger = bin present; reaction from
# .v8-project.json editingAllowedCheck (deny|warn|off, default deny). Never
# throws — guard errors degrade to allow.
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
}
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.get_LocalName() }
} catch {}
return $false
}
function Find-V8Project([string]$startDir) {
$d = $startDir
for ($i = 0; $i -lt 20 -and $d; $i++) {
$pj = Join-Path $d ".v8-project.json"
if (Test-Path $pj) { return $pj }
$parent = [System.IO.Path]::GetDirectoryName($d)
if ($parent -eq $d) { break }
$d = $parent
}
return $null
}
function Get-EditMode([string]$cfgDir) {
$mode = "deny"
try {
$pj = Find-V8Project $cfgDir
if ($pj) {
$cfg = Get-Content -Path $pj -Raw -Encoding UTF8 | ConvertFrom-Json
if ($cfg.PSObject.Properties.Name -contains 'editingAllowedCheck' -and $cfg.editingAllowedCheck) {
$mode = [string]$cfg.editingAllowedCheck
}
}
} catch {}
return $mode
}
function Assert-EditAllowed([string]$targetPath) {
try {
$mode = $null
$d = $targetPath
for ($i = 0; $i -lt 20 -and $d; $i++) {
$cfgXml = Join-Path $d "Configuration.xml"
$supportBin = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin"
# Автономный объект (внешняя обработка/отчёт) — граница климба
foreach ($x in @(Get-ChildItem -Path $d -Filter "*.xml" -File -ErrorAction SilentlyContinue)) {
if (Test-ExternalObjectRoot $x.FullName) { return }
}
if (Test-Path $cfgXml) {
if (Test-Path $supportBin) {
$mode = Get-EditMode $d
if ($mode -eq "off") { return }
$msg = "Конфигурация находится на поддержке (Ext/ParentConfigurations.bin). Правка может быть запрещена."
if ($mode -eq "warn") { Write-Warning $msg; return }
throw "$msg Снимите с поддержки (/support-edit) или задайте editingAllowedCheck в .v8-project.json."
}
return
}
$parent = [System.IO.Path]::GetDirectoryName($d)
if ($parent -eq $d) { break }
$d = $parent
}
} catch [System.Management.Automation.RuntimeException] {
throw
} catch {}
}
# --- Load the schema ---
if ($PSCmdlet.ParameterSetName -eq 'Inline') {
$xsdText = $Xsd
$defaultName = "Package"
} else {
if (-not (Test-Path $XsdPath -PathType Leaf)) { throw "Файл XSD не найден: $XsdPath" }
$xsdText = [System.IO.File]::ReadAllText($XsdPath)
$defaultName = [System.IO.Path]::GetFileNameWithoutExtension($XsdPath)
}
$xdoc = New-Object System.Xml.XmlDocument
$xdoc.PreserveWhitespace = $false
try { $xdoc.LoadXml($xsdText) } catch { throw "Не удалось разобрать XSD: $($_.Exception.Message)" }
$schema = $xdoc.DocumentElement
if ($schema.get_LocalName() -ne "schema" -or $schema.NamespaceURI -ne $XS_NS) {
throw "Ожидался корневой <xs:schema> в пространстве имён $XS_NS"
}
$targetNs = $schema.GetAttribute("targetNamespace")
# --- Emit-tree primitives -----------------------------------------------------
# A node carries attributes in canonical order; QName values keep their namespace
# so prefixes can be assigned per depth at serialization time (the dNpN scheme).
function New-Node([string]$tag) {
return [pscustomobject]@{ Tag = $tag; Attrs = (New-Object System.Collections.ArrayList); Children = (New-Object System.Collections.ArrayList); Text = $null; Prefix = $null; DeclareNs = $null }
}
function Add-Attr($node, [string]$name, $value) {
# $value НЕ типизируем: [string]$null коэрсится в "" и атрибут ложно появляется
if ($null -eq $value) { return }
[void]$node.Attrs.Add([pscustomobject]@{ Name = $name; Value = [string]$value; Ns = $null; Local = $null })
}
function Add-QAttr($node, [string]$name, $ns, $local) {
if ($null -eq $local) { return }
[void]$node.Attrs.Add([pscustomobject]@{ Name = $name; Value = $null; Ns = [string]$ns; Local = [string]$local })
}
function Add-QListAttr($node, [string]$name, $pairs, [bool]$clark) {
if (-not $pairs -or $pairs.Count -eq 0) { return }
[void]$node.Attrs.Add([pscustomobject]@{ Name = $name; Value = $null; Ns = $null; Local = $null; List = $pairs; Clark = $clark })
}
function Add-Child($node, $child) { if ($child) { [void]$node.Children.Add($child) } }
# Canonical attribute order per element — derived by topological sort over the
# whole 8.3.24 corpus (acc + erp, 760 packages), see docs/1c-xdto-spec.md.
$ATTR_ORDER = @{
"package" = @("targetNamespace", "elementFormQualified", "attributeFormQualified")
"import" = @("namespace")
"objectType" = @("name", "base", "open", "abstract", "mixed", "ordered", "sequenced")
"property" = @("name", "ref", "type", "lowerBound", "upperBound", "nillable", "fixed", "default", "form", "localName", "qualified")
"valueType" = @("name", "base", "variety", "itemType", "length", "memberTypes", "minExclusive", "maxExclusive", "minInclusive", "maxInclusive", "minLength", "maxLength", "totalDigits", "fractionDigits", "whiteSpace")
"typeDef" = @("xsi:type", "base", "mixed", "open", "ordered", "sequenced", "variety", "itemType", "length", "memberTypes", "minExclusive", "maxExclusive", "minInclusive", "maxInclusive", "minLength", "maxLength", "totalDigits", "fractionDigits", "whiteSpace")
"enumeration" = @("xsi:type")
}
function Sort-Attrs($node) {
$order = $ATTR_ORDER[$node.Tag]
if (-not $order) { return $node.Attrs }
$sorted = New-Object System.Collections.ArrayList
foreach ($n in $order) {
foreach ($a in $node.Attrs) { if ($a.Name -eq $n) { [void]$sorted.Add($a) } }
}
foreach ($a in $node.Attrs) { if ($order -notcontains $a.Name) { [void]$sorted.Add($a) } }
return $sorted
}
function Esc([string]$s) {
if ($null -eq $s) { return "" }
return $s.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace('"', "&quot;")
}
function EscText([string]$s) {
if ($null -eq $s) { return "" }
return $s.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
}
# --- Serializer with the dNpN prefix scheme ---
$out = New-Object System.Text.StringBuilder
function Serialize-Node($node, [int]$depth, $inherited) {
$indent = "`t" * ($depth - 1)
$attrs = Sort-Attrs $node
# Namespaces needing a NEW declaration here: те, что ещё не в области видимости.
# Сериализатор платформы объявляет префикс на первом узле, где он нужен, а
# потомки его переиспользуют — отсюда d2p1 у property внутри objectType.
$localNs = New-Object System.Collections.ArrayList
function Need-Prefix([string]$ns) {
if (-not $ns -or $ns -eq $XS_NS -or $ns -eq $XSI_NS) { return }
if ($inherited.ContainsKey($ns)) { return }
if (-not $localNs.Contains($ns)) { [void]$localNs.Add($ns) }
}
foreach ($a in $attrs) {
if ($a.PSObject.Properties.Name -contains 'List' -and $a.List) {
# Нотация Кларка несёт ns в значении и префикса не требует; редкие
# случаи, где платформа его всё же объявила, приходят зеркалом declareNs
if (-not $a.Clark) { foreach ($p in $a.List) { Need-Prefix $p.Ns } }
} elseif ($a.Ns) { Need-Prefix $a.Ns }
}
# Свойство с qualified платформа сериализует с явным префиксом пространства
# имён XDTO — и в имени тега, и в имени самого атрибута
$hasQualified = $false
foreach ($a in $attrs) { if ($a.Name -eq "qualified") { $hasQualified = $true } }
if ($hasQualified) { Need-Prefix $XDTO_NS }
if ($node.DeclareNs) { Need-Prefix $node.DeclareNs }
$prefixOf = @{}
foreach ($k in $inherited.Keys) { $prefixOf[$k] = $inherited[$k] }
$nsDecls = ""
for ($i = 0; $i -lt $localNs.Count; $i++) {
# Осмысленный префикс из исходника (зеркало xdto:prefix) имеет приоритет
$px = if ($i -eq 0 -and $node.Prefix) { $node.Prefix } else { "d${depth}p$($i + 1)" }
$prefixOf[$localNs[$i]] = $px
$nsDecls += " xmlns:$px=`"$(Esc $localNs[$i])`""
}
function QVal([string]$ns, [string]$local) {
if (-not $ns) { return $local }
if ($ns -eq $XS_NS) { return "xs:$local" }
if ($ns -eq $XSI_NS) { return "xsi:$local" }
return "$($prefixOf[$ns]):$local"
}
$attrText = ""
foreach ($a in $attrs) {
if ($a.PSObject.Properties.Name -contains 'List' -and $a.List) {
$vals = @()
foreach ($p in $a.List) {
if ($a.Clark) { $vals += $(if ($p.Ns) { "{$($p.Ns)}$($p.Local)" } else { $p.Local }) }
else { $vals += (QVal $p.Ns $p.Local) }
}
$attrText += " $($a.Name)=`"$(Esc ($vals -join ' '))`""
} elseif ($a.Ns -or $a.Local) {
$attrText += " $($a.Name)=`"$(Esc (QVal $a.Ns $a.Local))`""
} elseif ($a.Name -eq "qualified") {
$attrText += " $($prefixOf[$XDTO_NS]):qualified=`"$(Esc $a.Value)`""
} else {
$attrText += " $($a.Name)=`"$(Esc $a.Value)`""
}
}
$tagName = $node.Tag
if ($hasQualified) { $tagName = "$($prefixOf[$XDTO_NS]):$($node.Tag)" }
$hasChildren = $node.Children.Count -gt 0
# Пустое значение пишется самозакрывающимся тегом: <enumeration/>, а не <enumeration></enumeration>
$hasText = ($null -ne $node.Text -and $node.Text -ne "")
if (-not $hasChildren -and -not $hasText) {
[void]$out.Append("$indent<$tagName$nsDecls$attrText/>`r`n")
return
}
if ($hasText -and -not $hasChildren) {
[void]$out.Append("$indent<$tagName$nsDecls$attrText>$(EscText $node.Text)</$tagName>`r`n")
return
}
[void]$out.Append("$indent<$tagName$nsDecls$attrText>`r`n")
foreach ($c in $node.Children) { Serialize-Node $c ($depth + 1) $prefixOf }
[void]$out.Append("$indent</$tagName>`r`n")
}
# --- XSD reading helpers ---
# Предупреждения о том, что XSD выражает, а модель XDTO — нет. Молча ронять
# такие конструкции нельзя: пакет соберётся, а половина свойств исчезнет.
$script:warnings = New-Object System.Collections.ArrayList
function Warn([string]$msg) {
if (-not $script:warnings.Contains($msg)) { [void]$script:warnings.Add($msg) }
}
function XA([System.Xml.XmlElement]$el, [string]$name) {
if ($el.HasAttribute($name)) { return $el.GetAttribute($name) }
return $null
}
function MA([System.Xml.XmlElement]$el, [string]$name) {
# xdto: mirror attribute — the literal value to write into Package.bin.
# Префикс не фиксируем: ищем по namespace, а не по строке "xdto:".
$a = $el.Attributes.GetNamedItem($name, $XDTO_NS)
if ($null -eq $a) { return $null }
return $a.Value
}
function XChildren([System.Xml.XmlElement]$el, [string]$local) {
$res = New-Object System.Collections.ArrayList
foreach ($c in $el.ChildNodes) {
if ($c.NodeType -eq [System.Xml.XmlNodeType]::Element -and $c.NamespaceURI -eq $XS_NS -and $c.get_LocalName() -eq $local) { [void]$res.Add($c) }
}
# ArrayList, а не @(): PowerShell разворачивает массив из одного элемента при return
return ,$res
}
function XFirst([System.Xml.XmlElement]$el, [string]$local) {
$r = XChildren $el $local
if ($r.Count -gt 0) { return $r[0] }
return $null
}
# Split a QName from the XSD into (ns, local) using that element's prefix scope
function Split-QName([System.Xml.XmlElement]$el, [string]$qname) {
if ($null -eq $qname -or $qname -eq "") { return $null }
$parts = $qname.Split(":")
if ($parts.Count -eq 2) {
$ns = $el.GetNamespaceOfPrefix($parts[0])
$local = $parts[1]
} else {
# Прощающий ввод: голое имя типа трактуем как тип целевого пространства
$ns = $el.GetNamespaceOfPrefix("")
if (-not $ns) { $ns = $targetNs }
$local = $parts[0]
}
return [pscustomobject]@{ Ns = $ns; Local = $local }
}
function Split-QNameList([System.Xml.XmlElement]$el, [string]$list) {
if (-not $list) { return @() }
$res = @()
foreach ($q in ($list -split "\s+")) { if ($q) { $res += (Split-QName $el $q) } }
return $res
}
$FACETS = @("length", "minLength", "maxLength", "totalDigits", "fractionDigits",
"minInclusive", "maxInclusive", "minExclusive", "maxExclusive", "whiteSpace")
# --- simpleType -> valueType / typeDef(ValueType) ---
function Fill-SimpleType($node, [System.Xml.XmlElement]$st) {
$restriction = XFirst $st "restriction"
$list = XFirst $st "list"
$union = XFirst $st "union"
if ($list) {
$it = Split-QName $list (XA $list "itemType")
$mv = MA $list "variety"
Add-Attr $node "variety" $(if ($null -ne $mv) { $mv } else { "List" })
if ($it) { Add-QAttr $node "itemType" $it.Ns $it.Local }
return
}
if ($union) {
$mv = MA $union "variety"
Set-AttrValue $node "variety" $(if ($null -ne $mv) { $mv } else { "Union" })
$members = @(Split-QNameList $union (XA $union "memberTypes"))
# По умолчанию нотация Кларка — так записано 125 из 135 memberTypes корпуса
$useClark = ((MA $union "memberTypesForm") -ne "prefixed")
if ($members.Count -gt 0) { Add-QListAttr $node "memberTypes" $members $useClark }
$node.DeclareNs = MA $union "declareNs"
foreach ($anon in (XChildren $union "simpleType")) {
# typeDef в контексте простого типа xsi:type не несёт (40 узлов корпуса)
$td = New-Node "typeDef"
Fill-SimpleType $td $anon
Add-Child $node $td
}
return
}
if ($restriction) {
$b = Split-QName $restriction (XA $restriction "base")
if ($b) { Add-QAttr $node "base" $b.Ns $b.Local }
$mv = MA $restriction "variety"
if ($null -ne $mv) { Add-Attr $node "variety" $mv }
# Анонимный базовый тип внутри xs:restriction — typeDef без xsi:type
$anonBase = XFirst $restriction "simpleType"
if ($anonBase) {
$td = New-Node "typeDef"
Fill-SimpleType $td $anonBase
Add-Child $node $td
}
foreach ($f in $FACETS) {
foreach ($fe in (XChildren $restriction $f)) { Add-Attr $node $f (XA $fe "value") }
}
foreach ($pe in (XChildren $restriction "pattern")) {
$pn = New-Node "pattern"; $pn.Text = (XA $pe "value"); Add-Child $node $pn
}
foreach ($en in (XChildren $restriction "enumeration")) {
$enode = New-Node "enumeration"
$mt = MA $en "type"
if ($null -ne $mt) {
$q = Split-QName $en $mt
Add-QAttr $enode "xsi:type" $q.Ns $q.Local
}
$enode.Text = (XA $en "value")
Add-Child $node $enode
}
}
}
function Get-PropKey($p) {
foreach ($a in $p.Attrs) { if ($a.Name -eq "name") { return $a.Value } }
foreach ($a in $p.Attrs) { if ($a.Name -eq "ref") { return "@" + $a.Local } }
return $null
}
# Восстановить исходный порядок свойств по зеркалу xdto:order
function Reorder-Properties($node, $names) {
$props = @($node.Children | Where-Object { $_.Tag -eq "property" })
if ($props.Count -lt 2) { return }
$byKey = @{}
foreach ($p in $props) {
$k = Get-PropKey $p
if ($null -ne $k -and -not $byKey.ContainsKey($k)) { $byKey[$k] = $p }
}
$ordered = New-Object System.Collections.ArrayList
foreach ($n in $names) {
if ($byKey.ContainsKey($n)) { [void]$ordered.Add($byKey[$n]); $byKey.Remove($n) }
}
foreach ($p in $props) { if ($ordered -notcontains $p) { [void]$ordered.Add($p) } }
$others = @($node.Children | Where-Object { $_.Tag -ne "property" })
$node.Children.Clear()
foreach ($p in $ordered) { [void]$node.Children.Add($p) }
foreach ($o in $others) { [void]$node.Children.Add($o) }
}
function Set-AttrValue($node, [string]$name, [string]$value) {
foreach ($a in $node.Attrs) { if ($a.Name -eq $name) { $a.Value = $value; return } }
Add-Attr $node $name $value
}
# --- element / attribute -> property ---
function Build-Property([System.Xml.XmlElement]$el, [bool]$isAttribute) {
$p = New-Node "property"
$xsdName = XA $el "name"
$mirrorName = MA $el "name"
if ($null -ne $mirrorName) {
Add-Attr $p "name" $mirrorName
$localName = $xsdName
} else {
Add-Attr $p "name" $xsdName
$localName = $null
}
$refQ = Split-QName $el (XA $el "ref")
if ($refQ) { Add-QAttr $p "ref" $refQ.Ns $refQ.Local }
$typeQ = Split-QName $el (XA $el "type")
if ($typeQ) { Add-QAttr $p "type" $typeQ.Ns $typeQ.Local }
if ($isAttribute) {
Add-Attr $p "lowerBound" (MA $el "lowerBound")
Add-Attr $p "upperBound" (MA $el "upperBound")
Add-Attr $p "nillable" (MA $el "nillable")
} else {
Add-Attr $p "lowerBound" (XA $el "minOccurs")
$maxOcc = XA $el "maxOccurs"
if ($null -ne $maxOcc) { Add-Attr $p "upperBound" $(if ($maxOcc -eq "unbounded") { "-1" } else { $maxOcc }) }
Add-Attr $p "nillable" (XA $el "nillable")
}
# XSD-шный fixed="V" несёт значение, в модели это fixed="true" + default="V".
# Прощающий ввод: модельная форма через зеркало xdto:fixed тоже принимается.
$mFixed = MA $el "fixed"
if ($null -ne $mFixed) {
Add-Attr $p "fixed" $mFixed
Add-Attr $p "default" (XA $el "default")
if ($mFixed -ceq "true" -and $null -eq (XA $el "default")) {
Warn "Свойство `"$(XA $el 'name')`": xdto:fixed=`"true`" без default — платформа отвергнет пакет («Отсутствует фиксированное значение»). Значение задаётся атрибутом default, либо пишите XSD-форму fixed=`"значение`""
}
} elseif ($null -ne (XA $el "fixed")) {
Add-Attr $p "fixed" "true"
Add-Attr $p "default" (XA $el "fixed")
} else {
Add-Attr $p "default" (XA $el "default")
}
if ($isAttribute) {
Add-Attr $p "form" "Attribute"
} else {
$mf = MA $el "form"
if ($null -ne $mf) { Add-Attr $p "form" $mf }
}
Add-Attr $p "localName" $localName
Add-Attr $p "qualified" (MA $el "qualified")
$p.Prefix = MA $el "prefix"
# Anonymous inline type
$anonSimple = XFirst $el "simpleType"
$anonComplex = XFirst $el "complexType"
if ($anonSimple) {
$td = New-Node "typeDef"
Add-Attr $td "xsi:type" "ValueType"
Fill-SimpleType $td $anonSimple
Add-Child $p $td
} elseif ($anonComplex) {
$td = New-Node "typeDef"
Add-Attr $td "xsi:type" "ObjectType"
Fill-ComplexType $td $anonComplex
Add-Child $p $td
}
return $p
}
# --- complexType -> objectType / typeDef(ObjectType) ---
# Разрешение xs:group / xs:attributeGroup по ссылке
$script:GROUPS = @{}
$script:ATTR_GROUPS = @{}
function Resolve-Group([System.Xml.XmlElement]$el, [string]$kind) {
$ref = XA $el "ref"
if (-not $ref) { return $null }
$q = Split-QName $el $ref
if (-not $q) { return $null }
$map = if ($kind -eq "group") { $script:GROUPS } else { $script:ATTR_GROUPS }
if ($map.ContainsKey($q.Local)) { return $map[$q.Local] }
return $null
}
# Модель XDTO знает только плоский список свойств: вложенные частицы уплощаются.
# Каждое уплощение — предупреждение, потому что меняется смысл схемы.
function Collect-Particle([System.Xml.XmlElement]$particle, $elemList, [ref]$isOpen, [string]$typeName, [int]$depth, [bool]$optionalize = $false) {
if ($depth -gt 20) { return }
foreach ($c in $particle.ChildNodes) {
if ($c.NodeType -ne [System.Xml.XmlNodeType]::Element -or $c.NamespaceURI -ne $XS_NS) { continue }
switch ($c.get_LocalName()) {
"element" {
$prop = Build-Property $c $false
# Ветка уплощённого xs:choice обязана стать необязательной: иначе
# «одно из двух» превращается в «оба сразу», и тип нельзя заполнить
if ($optionalize) { Set-AttrValue $prop "lowerBound" "0" }
[void]$elemList.Add($prop)
}
"any" { $isOpen.Value = $true }
"sequence" {
Warn "$typeName : вложенная xs:sequence уплощена — модель XDTO хранит плоский список свойств"
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1) $optionalize
}
"choice" {
$branches = @()
foreach ($b in $c.ChildNodes) {
if ($b.NodeType -eq [System.Xml.XmlNodeType]::Element -and $b.NamespaceURI -eq $XS_NS -and $b.HasAttribute("name")) {
$branches += $b.GetAttribute("name")
}
}
$list = if ($branches.Count -gt 0) { " (" + ($branches -join ", ") + ")" } else { "" }
Warn ("$typeName : вложенная xs:choice уплощена — ветки$list сделаны необязательными. " +
"Выбор одного из вариантов не сохранён: модель не запретит заполнить сразу несколько или ни одного")
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1) $true
}
"all" {
Warn "$typeName : xs:all трактуется как последовательность"
Collect-Particle $c $elemList $isOpen $typeName ($depth + 1) $optionalize
}
"group" {
$g = Resolve-Group $c "group"
if ($g) {
foreach ($gc in $g.ChildNodes) {
if ($gc.NodeType -eq [System.Xml.XmlNodeType]::Element -and $gc.NamespaceURI -eq $XS_NS -and
@("sequence", "choice", "all") -contains $gc.get_LocalName()) {
Collect-Particle $gc $elemList $isOpen $typeName ($depth + 1) $optionalize
}
}
} else {
Warn "$typeName : не найдена группа $(XA $c 'ref') — её свойства в пакет не попали"
}
}
}
# Кратность на самой частице модель выразить не может
if (@("sequence", "choice", "all", "group") -contains $c.get_LocalName()) {
if ((XA $c "maxOccurs") -or (XA $c "minOccurs")) {
Warn "$typeName : кратность на вложенной частице (<xs:$($c.get_LocalName()) minOccurs/maxOccurs>) не выражается в модели XDTO"
}
}
}
}
# open / ordered / sequenced / abstract / mixed: выводим где выводимо,
# остальное приходит зеркалом xdto:
function Set-TypeFlags($node, [System.Xml.XmlElement]$ct, $isOpen, $choice) {
$mOpen = MA $ct "open"
if ($null -ne $mOpen) { Add-Attr $node "open" $mOpen }
elseif ($isOpen) { Add-Attr $node "open" "true" }
$mOrdered = MA $ct "ordered"
if ($null -ne $mOrdered) { Add-Attr $node "ordered" $mOrdered }
elseif ($choice) { Add-Attr $node "ordered" "false" }
$mSeq = MA $ct "sequenced"
if ($null -ne $mSeq) { Add-Attr $node "sequenced" $mSeq }
$mAbstract = MA $ct "abstract"
if ($null -ne $mAbstract) { Add-Attr $node "abstract" $mAbstract }
elseif ((XA $ct "abstract") -eq "true") { Add-Attr $node "abstract" "true" }
$mMixed = MA $ct "mixed"
if ($null -ne $mMixed) { Add-Attr $node "mixed" $mMixed }
elseif ((XA $ct "mixed") -eq "true") { Add-Attr $node "mixed" "true" }
}
function Fill-ComplexType($node, [System.Xml.XmlElement]$ct) {
# xs:complexContent/xs:extension carries the base type
$content = XFirst $ct "complexContent"
$body = $ct
if ($content) {
$ext = XFirst $content "extension"
if ($ext) {
$b = Split-QName $ext (XA $ext "base")
if ($b) { Add-QAttr $node "base" $b.Ns $b.Local }
$body = $ext
}
}
# xs:simpleContent -> a "Text" property holding the element's own value
$simple = XFirst $ct "simpleContent"
if ($simple) {
$ext = XFirst $simple "extension"
if ($ext) {
foreach ($a in (XChildren $ext "attribute")) { Add-Child $node (Build-Property $a $true) }
$tp = New-Node "property"
$tName = MA $ext "textName"
Add-Attr $tp "name" $(if ($null -ne $tName) { $tName } else { "__content" })
$b = Split-QName $ext (XA $ext "base")
if ($b) { Add-QAttr $tp "type" $b.Ns $b.Local }
Add-Attr $tp "lowerBound" (MA $ext "textlowerBound")
Add-Attr $tp "upperBound" (MA $ext "textupperBound")
Add-Attr $tp "nillable" (MA $ext "textnillable")
Add-Attr $tp "form" "Text"
Add-Child $node $tp
# xs:simpleContent не отменяет флаги самого xs:complexType
Set-TypeFlags $node $ct $false $null
return
}
}
# Particle: xs:sequence (ordered) or xs:choice (ordered="false")
$seq = XFirst $body "sequence"
$cho = XFirst $body "choice"
$all = XFirst $body "all"
$grp = XFirst $body "group"
$particle = if ($seq) { $seq } elseif ($cho) { $cho } elseif ($all) { $all } else { $grp }
$isOpen = $false
# Порядок в XDTO: сначала form="Attribute", потом остальные (верно для 96.5%
# типов корпуса). Отклонения приходят зеркалом xdto:order.
$elemProps = New-Object System.Collections.ArrayList
$typeName = if ($ct.HasAttribute("name")) { $ct.GetAttribute("name") } else { "(анонимный тип)" }
if ($particle) {
$openRef = [ref]$isOpen
if ($all) {
Warn "$typeName : xs:all трактуется как последовательность"
}
if ($grp -and -not $seq -and -not $cho -and -not $all) {
# Корневая частица задана ссылкой на группу — раскрываем её содержимое
$g = Resolve-Group $grp "group"
if ($g) {
foreach ($gc in $g.ChildNodes) {
if ($gc.NodeType -eq [System.Xml.XmlNodeType]::Element -and $gc.NamespaceURI -eq $XS_NS -and
@("sequence", "choice", "all") -contains $gc.get_LocalName()) {
Collect-Particle $gc $elemProps $openRef $typeName 1
}
}
} else {
Warn "$typeName : не найдена группа $(XA $grp 'ref') — её свойства в пакет не попали"
}
} else {
Collect-Particle $particle $elemProps $openRef $typeName 0
}
$isOpen = $openRef.Value
}
foreach ($a in (XChildren $body "attribute")) { Add-Child $node (Build-Property $a $true) }
# xs:attributeGroup раскрываем по ссылке
foreach ($ag in (XChildren $body "attributeGroup")) {
$g = Resolve-Group $ag "attributeGroup"
if ($g) {
foreach ($a in (XChildren $g "attribute")) { Add-Child $node (Build-Property $a $true) }
} else {
Warn "Не найдена группа атрибутов $(XA $ag 'ref') — её атрибуты в пакет не попали"
}
}
foreach ($e in $elemProps) { Add-Child $node $e }
if ((XChildren $body "anyAttribute").Count -gt 0) { $isOpen = $true }
$mOrder = MA $ct "order"
if ($null -ne $mOrder) { Reorder-Properties $node ($mOrder -split "\|") }
Set-TypeFlags $node $ct $isOpen $cho
}
# --- Build the package tree ---
$pkgNode = New-Node "package"
Add-Attr $pkgNode "targetNamespace" $targetNs
$efqMirror = MA $schema "elementFormQualified"
$afqMirror = MA $schema "attributeFormQualified"
$efd = XA $schema "elementFormDefault"
$afd = XA $schema "attributeFormDefault"
if ($null -ne $efqMirror) { Add-Attr $pkgNode "elementFormQualified" $efqMirror }
elseif ($null -ne $efd) { Add-Attr $pkgNode "elementFormQualified" $(if ($efd -eq "qualified") { "true" } else { "false" }) }
if ($null -ne $afqMirror) { Add-Attr $pkgNode "attributeFormQualified" $afqMirror }
elseif ($null -ne $afd) { Add-Attr $pkgNode "attributeFormQualified" $(if ($afd -eq "qualified") { "true" } else { "false" }) }
# Metadata properties from xs:annotation/xs:appinfo
$metaName = $null; $metaComment = $null; $metaSynonym = @()
$ann = XFirst $schema "annotation"
if ($ann) {
$appinfo = XFirst $ann "appinfo"
if ($appinfo) {
foreach ($pk in $appinfo.ChildNodes) {
if ($pk.NodeType -ne [System.Xml.XmlNodeType]::Element -or $pk.NamespaceURI -ne $XDTO_NS) { continue }
foreach ($f in $pk.ChildNodes) {
if ($f.NodeType -ne [System.Xml.XmlNodeType]::Element) { continue }
switch ($f.get_LocalName()) {
"name" { $metaName = $f.InnerText }
"comment" { $metaComment = $f.InnerText }
"synonym" { $metaSynonym += @{ Lang = $f.GetAttribute("lang"); Content = $f.InnerText } }
}
}
}
}
}
# Реестр глобальных групп — нужен до обхода, чтобы раскрывать ссылки
foreach ($node in $schema.ChildNodes) {
if ($node.NodeType -ne [System.Xml.XmlNodeType]::Element -or $node.NamespaceURI -ne $XS_NS) { continue }
$nm = XA $node "name"
if ($node.get_LocalName() -eq "group" -and $nm) { $script:GROUPS[$nm] = $node }
if ($node.get_LocalName() -eq "attributeGroup" -and $nm) { $script:ATTR_GROUPS[$nm] = $node }
}
# Конструкции XSD, которым в модели XDTO нет соответствия
foreach ($sg in $schema.SelectNodes("//*[local-name()='element'][@substitutionGroup]")) {
Warn "Подстановочные группы (substitutionGroup) не поддерживаются моделью XDTO — объявление $($sg.GetAttribute('name')) сохранено как обычное"
}
foreach ($idc in @("key", "keyref", "unique")) {
if ($schema.SelectNodes("//*[local-name()='$idc']").Count -gt 0) {
Warn "Ограничения целостности (xs:$idc) в модели XDTO не хранятся — отброшены"
}
}
if ($schema.SelectNodes("//*[local-name()='redefine']").Count -gt 0) {
Warn "xs:redefine не поддерживается — переопределения проигнорированы"
}
if ($schema.SelectNodes("//*[local-name()='include']").Count -gt 0) {
Warn "xs:include проигнорирован: модель XDTO разрешает зависимости только по namespace. Соберите включаемую схему отдельным пакетом и добавьте <xs:import>"
}
foreach ($node in $schema.ChildNodes) {
if ($node.NodeType -ne [System.Xml.XmlNodeType]::Element -or $node.NamespaceURI -ne $XS_NS) { continue }
switch ($node.get_LocalName()) {
"annotation" { }
"group" { }
"attributeGroup" { }
"notation" { }
"import" {
$n = New-Node "import"
Add-Attr $n "namespace" (XA $node "namespace")
Add-Child $pkgNode $n
}
"include" { }
"element" { Add-Child $pkgNode (Build-Property $node $false) }
"attribute" { Add-Child $pkgNode (Build-Property $node $true) }
"simpleType" {
$n = New-Node "valueType"
Add-Attr $n "name" (XA $node "name")
Fill-SimpleType $n $node
Add-Child $pkgNode $n
}
"complexType" {
$n = New-Node "objectType"
Add-Attr $n "name" (XA $node "name")
Fill-ComplexType $n $node
Add-Child $pkgNode $n
}
}
}
# --- Serialize Package.bin ---
# Модель XDTO требует строгой последовательности элементов верхнего уровня:
# import → property → valueType → objectType. Порядок объявлений в XSD произвольный,
# поэтому пересортировываем — иначе платформа отвергает пакет с «Ошибка преобразования
# данных XDTO». Все 760 пакетов корпуса этому порядку удовлетворяют, так что
# round-trip не затрагивается.
$TOP_ORDER = @("import", "property", "valueType", "objectType")
$sortedChildren = New-Object System.Collections.ArrayList
foreach ($t in $TOP_ORDER) {
foreach ($c in $pkgNode.Children) { if ($c.Tag -eq $t) { [void]$sortedChildren.Add($c) } }
}
foreach ($c in $pkgNode.Children) { if ($TOP_ORDER -notcontains $c.Tag) { [void]$sortedChildren.Add($c) } }
$pkgNode.Children.Clear()
foreach ($c in $sortedChildren) { [void]$pkgNode.Children.Add($c) }
$attrsRoot = Sort-Attrs $pkgNode
$rootAttrText = ""
foreach ($a in $attrsRoot) { $rootAttrText += " $($a.Name)=`"$(Esc $a.Value)`"" }
[void]$out.Append("<package xmlns=`"$XDTO_NS`" xmlns:xs=`"$XS_NS`" xmlns:xsi=`"$XSI_NS`"$rootAttrText>`r`n")
foreach ($c in $pkgNode.Children) { Serialize-Node $c 2 @{} }
[void]$out.Append("</package>")
$binText = $out.ToString()
# --- Resolve the package name ---
if (-not $Name) {
if ($metaName) { $Name = $metaName } else { $Name = $defaultName }
}
# Санация под идентификатор 1С
$Name = ($Name -replace '[^\wЀ-ӿ]', '_')
if ($Name -match '^\d') { $Name = "_$Name" }
Assert-EditAllowed $OutputDir
$pkgRoot = Join-Path $OutputDir "XDTOPackages"
$pkgDir = Join-Path $pkgRoot $Name
$extDir = Join-Path $pkgDir "Ext"
$mdFile = Join-Path $pkgRoot "$Name.xml"
$binFile = Join-Path $extDir "Package.bin"
if ((Test-Path $binFile) -and -not $Force) {
throw "Пакет уже существует: $binFile. Используйте -Force для перезаписи."
}
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
$encBom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($binFile, $binText, $encBom)
# --- Metadata object file ---
if (-not $Synonym -and $metaSynonym.Count -gt 0) {
$synItems = $metaSynonym
} elseif ($Synonym -is [System.Collections.IDictionary]) {
$synItems = @()
foreach ($k in $Synonym.Keys) { $synItems += @{ Lang = [string]$k; Content = [string]$Synonym[$k] } }
} elseif ($Synonym) {
$synItems = @(@{ Lang = "ru"; Content = [string]$Synonym })
} else {
$synItems = @(@{ Lang = "ru"; Content = $Name })
}
if (-not $Comment -and $metaComment) { $Comment = $metaComment }
$uuid = [guid]::NewGuid().ToString()
$md = New-Object System.Text.StringBuilder
function M([string]$s) { [void]$md.Append($s); [void]$md.Append("`r`n") }
M '<?xml version="1.0" encoding="UTF-8"?>'
M '<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">'
M "`t<XDTOPackage uuid=`"$uuid`">"
M "`t`t<Properties>"
M "`t`t`t<Name>$(EscText $Name)</Name>"
M "`t`t`t<Synonym>"
foreach ($s in $synItems) {
M "`t`t`t`t<v8:item>"
M "`t`t`t`t`t<v8:lang>$(EscText $s.Lang)</v8:lang>"
M "`t`t`t`t`t<v8:content>$(EscText $s.Content)</v8:content>"
M "`t`t`t`t</v8:item>"
}
M "`t`t`t</Synonym>"
if ($Comment) { M "`t`t`t<Comment>$(EscText $Comment)</Comment>" } else { M "`t`t`t<Comment/>" }
M "`t`t`t<Namespace>$(EscText $targetNs)</Namespace>"
M "`t`t</Properties>"
M "`t</XDTOPackage>"
[void]$md.Append("</MetaDataObject>")
[System.IO.File]::WriteAllText($mdFile, $md.ToString(), $encBom)
# --- Register in Configuration.xml ---
# Ранняя диагностика: отказ платформы при db-update дешевле поймать на сборке
$xdtoRootDir = Join-Path $OutputDir "XDTOPackages"
$declaredImports = @()
foreach ($c in $pkgNode.Children) { if ($c.Tag -eq "import") { foreach ($a in $c.Attrs) { if ($a.Name -eq "namespace") { $declaredImports += $a.Value } } } }
if ($declaredImports.Count -gt 0 -and (Test-Path $xdtoRootDir)) {
$knownNs = @{}
foreach ($other in (Get-ChildItem $xdtoRootDir -Directory -ErrorAction SilentlyContinue)) {
$ob = Join-Path (Join-Path $other.FullName "Ext") "Package.bin"
if (-not (Test-Path $ob)) { continue }
try {
$od = New-Object System.Xml.XmlDocument
$od.Load($ob)
$knownNs[$od.DocumentElement.GetAttribute("targetNamespace")] = $true
} catch {}
}
foreach ($imp in $declaredImports) {
if (-not $knownNs.ContainsKey($imp) -and $PLATFORM_NS -notcontains $imp) {
Warn "Импорт `"$imp`" не разрешается: пакета с таким namespace в конфигурации нет. Платформа отвергнет пакет при обновлении — соберите зависимость первой"
}
}
}
$configXmlPath = Join-Path $OutputDir "Configuration.xml"
$regResult = "no-config"
if (Test-Path $configXmlPath) {
$configDoc = New-Object System.Xml.XmlDocument
$configDoc.PreserveWhitespace = $true
$configDoc.Load($configXmlPath)
$nsMgr = New-Object System.Xml.XmlNamespaceManager($configDoc.NameTable)
$nsMgr.AddNamespace("md", $MD_NS)
$childObjects = $configDoc.SelectSingleNode("//md:Configuration/md:ChildObjects", $nsMgr)
if ($childObjects) {
$existing = $childObjects.SelectNodes("md:XDTOPackage", $nsMgr)
$already = $false
foreach ($e in $existing) { if ($e.InnerText -eq $Name) { $already = $true; break } }
if ($already) {
$regResult = "already"
} else {
$newElem = $configDoc.CreateElement("XDTOPackage", $MD_NS)
$newElem.InnerText = $Name
if ($existing.Count -gt 0) {
$lastElem = $existing[$existing.Count - 1]
$newWs = $configDoc.CreateWhitespace("`n`t`t`t")
$childObjects.InsertAfter($newWs, $lastElem) | Out-Null
$childObjects.InsertAfter($newElem, $newWs) | Out-Null
} else {
$lastChild = $childObjects.LastChild
if ($lastChild -and $lastChild.NodeType -eq [System.Xml.XmlNodeType]::Whitespace) {
$newWs = $configDoc.CreateWhitespace("`n`t`t`t")
$childObjects.InsertBefore($newWs, $lastChild) | Out-Null
$childObjects.InsertBefore($newElem, $lastChild) | Out-Null
} else {
$childObjects.AppendChild($configDoc.CreateWhitespace("`n`t`t`t")) | Out-Null
$childObjects.AppendChild($newElem) | Out-Null
$childObjects.AppendChild($configDoc.CreateWhitespace("`n`t`t")) | Out-Null
}
}
$cfgSettings = New-Object System.Xml.XmlWriterSettings
$cfgSettings.Encoding = New-Object System.Text.UTF8Encoding($true)
$cfgSettings.Indent = $false
$stream = New-Object System.IO.FileStream($configXmlPath, [System.IO.FileMode]::Create)
$writer = [System.Xml.XmlWriter]::Create($stream, $cfgSettings)
$configDoc.Save($writer)
$writer.Close()
$stream.Close()
$regResult = "added"
}
}
}
# --- Report ---
$typeCount = 0
foreach ($c in $pkgNode.Children) { if ($c.Tag -eq "objectType" -or $c.Tag -eq "valueType") { $typeCount++ } }
Write-Host "✓ Пакет XDTO собран: $Name"
Write-Host " Namespace: $targetNs"
Write-Host " Типов: $typeCount"
Write-Host " Файлы: XDTOPackages/$Name.xml, XDTOPackages/$Name/Ext/Package.bin"
if ($script:warnings.Count -gt 0) {
Write-Host ""
Write-Host "Предупреждения ($($script:warnings.Count)) — конструкции XSD без точного соответствия в модели XDTO:"
foreach ($w in $script:warnings) { Write-Host " ! $w" }
Write-Host ""
}
switch ($regResult) {
"added" { Write-Host " Configuration.xml: <XDTOPackage>$Name</XDTOPackage> добавлен в ChildObjects" }
"already" { Write-Host " Configuration.xml: <XDTOPackage>$Name</XDTOPackage> уже зарегистрирован" }
"no-config" { Write-Host " Configuration.xml не найден — регистрация пропущена" }
}