mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-28 23:51:03 +03:00
Auto-build: codeassistant (powershell) from ba27f22
This commit is contained in:
@@ -0,0 +1,562 @@
|
||||
# xdto-edit v1.0 — Point edits of a 1C XDTO package
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[Alias('Path')]
|
||||
[string]$PackagePath,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateSet("add-property", "replace-property", "remove-property",
|
||||
"add-type", "remove-type", "add-enum", "add-import",
|
||||
"rename", "set-synonym", "set-comment", "set-namespace")]
|
||||
[string]$Operation,
|
||||
[string]$Target,
|
||||
[string]$Value,
|
||||
[switch]$NoValidate
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$XDTO_NS = "http://v8.1c.ru/8.1/xdto"
|
||||
$XS_NS = "http://www.w3.org/2001/XMLSchema"
|
||||
$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 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 {
|
||||
$d = $targetPath
|
||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||
foreach ($x in @(Get-ChildItem -Path $d -Filter "*.xml" -File -ErrorAction SilentlyContinue)) {
|
||||
if (Test-ExternalObjectRoot $x.FullName) { return }
|
||||
}
|
||||
$cfgXml = Join-Path $d "Configuration.xml"
|
||||
$supportBin = Join-Path (Join-Path $d "Ext") "ParentConfigurations.bin"
|
||||
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 {}
|
||||
}
|
||||
|
||||
# --- Resolve package ------------------------------------------------------------
|
||||
|
||||
if (-not [System.IO.Path]::IsPathRooted($PackagePath)) {
|
||||
$PackagePath = Join-Path (Get-Location).Path $PackagePath
|
||||
}
|
||||
|
||||
$pkgDir = $null
|
||||
if (Test-Path $PackagePath -PathType Container) {
|
||||
if (Test-Path (Join-Path (Join-Path $PackagePath "Ext") "Package.bin")) { $pkgDir = $PackagePath }
|
||||
} elseif ((Test-Path $PackagePath -PathType Leaf) -and ([System.IO.Path]::GetFileName($PackagePath) -eq "Package.bin")) {
|
||||
$pkgDir = Split-Path (Split-Path $PackagePath -Parent) -Parent
|
||||
} elseif ($PackagePath.EndsWith(".xml")) {
|
||||
$stem = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($PackagePath),
|
||||
[System.IO.Path]::GetFileNameWithoutExtension($PackagePath))
|
||||
if (Test-Path (Join-Path (Join-Path $stem "Ext") "Package.bin")) { $pkgDir = $stem }
|
||||
}
|
||||
if (-not $pkgDir) { throw "Не найден пакет XDTO по пути: $PackagePath" }
|
||||
|
||||
$pkgName = [System.IO.Path]::GetFileName($pkgDir)
|
||||
$xdtoRoot = Split-Path $pkgDir -Parent
|
||||
$configRoot = Split-Path $xdtoRoot -Parent
|
||||
$binFile = Join-Path (Join-Path $pkgDir "Ext") "Package.bin"
|
||||
$mdFile = Join-Path $xdtoRoot "$pkgName.xml"
|
||||
$configXml = Join-Path $configRoot "Configuration.xml"
|
||||
|
||||
# -Value "@путь" — содержимое берётся из файла. Передавать XSD-фрагмент инлайном
|
||||
# через powershell.exe -File ненадёжно: вложенные кавычки схлопываются на границе
|
||||
# процессов, и вместо понятной ошибки получается сырой сбой разбора XML.
|
||||
if ($Value -and $Value.StartsWith("@")) {
|
||||
$valueFile = $Value.Substring(1)
|
||||
if (-not [System.IO.Path]::IsPathRooted($valueFile)) {
|
||||
$valueFile = Join-Path (Get-Location).Path $valueFile
|
||||
}
|
||||
if (-not (Test-Path $valueFile -PathType Leaf)) { throw "Файл значения не найден: $valueFile" }
|
||||
$Value = [System.IO.File]::ReadAllText($valueFile).Trim()
|
||||
}
|
||||
|
||||
Assert-EditAllowed $pkgDir
|
||||
|
||||
$encBom = New-Object System.Text.UTF8Encoding($true)
|
||||
|
||||
# --- Sibling skills -------------------------------------------------------------
|
||||
# Правка идёт через уже проверенный round-trip: пакет выгружается в XSD, операция
|
||||
# применяется к схеме, пакет собирается обратно. Второго эмиттера не заводим —
|
||||
# байт-точность для всего нетронутого достаётся от компилятора.
|
||||
|
||||
$decompileScript = Join-Path (Join-Path $PSScriptRoot "..\..\xdto-decompile") "scripts\xdto-decompile.ps1"
|
||||
$compileScript = Join-Path (Join-Path $PSScriptRoot "..\..\xdto-compile") "scripts\xdto-compile.ps1"
|
||||
$validateScript = Join-Path (Join-Path $PSScriptRoot "..\..\xdto-validate") "scripts\xdto-validate.ps1"
|
||||
|
||||
# Исключение из автономности навыков, сделанное осознанно: конвертер XSD ↔ модель
|
||||
# нельзя скопировать буквально (xdto-compile — скрипт со сквозным потоком, не библиотека),
|
||||
# а вторая его реализация разошлась бы с первой. Обещание «правка не меняет ни байта
|
||||
# в нетронутом» держится именно на том, что код тот же самый.
|
||||
# Проверяем комплектность заранее, чтобы не падать на середине правки.
|
||||
function Assert-SiblingsPresent([string]$operation) {
|
||||
$needed = @{}
|
||||
if (@("rename", "set-synonym", "set-comment") -notcontains $operation) {
|
||||
$needed["xdto-decompile"] = $decompileScript
|
||||
$needed["xdto-compile"] = $compileScript
|
||||
}
|
||||
$missing = @()
|
||||
foreach ($k in $needed.Keys) { if (-not (Test-Path $needed[$k])) { $missing += $k } }
|
||||
if ($missing.Count -gt 0) {
|
||||
$skillsRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
||||
throw ("Навык неработоспособен: рядом нет " + ($missing -join ", ") + ".`n" +
|
||||
"Операция `"$operation`" выполняется через " +
|
||||
$(if ($missing.Count -gt 1) { "них" } else { "него" }) + ".`n" +
|
||||
"Ожидаются в каталоге навыков: $skillsRoot")
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-Sibling([string]$script, [string[]]$argList, [string]$what) {
|
||||
if (-not (Test-Path $script)) { throw "Не найден навык $what по пути: $script" }
|
||||
$out = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $script @argList 2>&1
|
||||
if ($LASTEXITCODE -ne 0) { throw "$what завершился с ошибкой:`n$($out -join "`n")" }
|
||||
return $out
|
||||
}
|
||||
|
||||
# --- Metadata object edits ------------------------------------------------------
|
||||
|
||||
function Edit-Metadata([string]$field, [string]$newValue) {
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
$doc.Load($mdFile)
|
||||
$nsm = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$nsm.AddNamespace("md", $MD_NS)
|
||||
$nsm.AddNamespace("v8", $V8_NS)
|
||||
$props = $doc.SelectSingleNode("//md:XDTOPackage/md:Properties", $nsm)
|
||||
if (-not $props) { throw "В объекте метаданных не найден блок <Properties>" }
|
||||
|
||||
switch ($field) {
|
||||
"Name" {
|
||||
$n = $props.SelectSingleNode("md:Name", $nsm)
|
||||
if (-not $n) { throw "В объекте метаданных нет <Name>" }
|
||||
$n.InnerText = $newValue
|
||||
}
|
||||
"Comment" {
|
||||
$c = $props.SelectSingleNode("md:Comment", $nsm)
|
||||
if (-not $c) {
|
||||
$c = $doc.CreateElement("Comment", $MD_NS)
|
||||
$props.AppendChild($c) | Out-Null
|
||||
}
|
||||
$c.InnerText = $newValue
|
||||
}
|
||||
"Namespace" {
|
||||
$ns = $props.SelectSingleNode("md:Namespace", $nsm)
|
||||
if (-not $ns) { throw "В объекте метаданных нет <Namespace>" }
|
||||
$ns.InnerText = $newValue
|
||||
}
|
||||
"Synonym" {
|
||||
$syn = $props.SelectSingleNode("md:Synonym", $nsm)
|
||||
if (-not $syn) {
|
||||
$syn = $doc.CreateElement("Synonym", $MD_NS)
|
||||
$props.AppendChild($syn) | Out-Null
|
||||
}
|
||||
$item = $syn.SelectSingleNode("v8:item[v8:lang='ru']", $nsm)
|
||||
if (-not $item) {
|
||||
$item = $doc.CreateElement("v8", "item", $V8_NS)
|
||||
$lang = $doc.CreateElement("v8", "lang", $V8_NS); $lang.InnerText = "ru"
|
||||
$cont = $doc.CreateElement("v8", "content", $V8_NS)
|
||||
$item.AppendChild($lang) | Out-Null
|
||||
$item.AppendChild($cont) | Out-Null
|
||||
$syn.AppendChild($item) | Out-Null
|
||||
}
|
||||
$item.SelectSingleNode("v8:content", $nsm).InnerText = $newValue
|
||||
}
|
||||
}
|
||||
|
||||
$settings = New-Object System.Xml.XmlWriterSettings
|
||||
$settings.Encoding = $encBom
|
||||
$settings.Indent = $false
|
||||
$stream = New-Object System.IO.FileStream($mdFile, [System.IO.FileMode]::Create)
|
||||
$writer = [System.Xml.XmlWriter]::Create($stream, $settings)
|
||||
$doc.Save($writer)
|
||||
$writer.Close(); $stream.Close()
|
||||
}
|
||||
|
||||
function Rename-Package([string]$newName) {
|
||||
if ($newName -notmatch '^[\wЀ-ӿ]+$' -or $newName -match '^\d') {
|
||||
throw "`"$newName`" не является допустимым идентификатором 1С"
|
||||
}
|
||||
$newMd = Join-Path $xdtoRoot "$newName.xml"
|
||||
$newDir = Join-Path $xdtoRoot $newName
|
||||
if ((Test-Path $newMd) -or (Test-Path $newDir)) { throw "Имя `"$newName`" уже занято другим пакетом" }
|
||||
|
||||
Edit-Metadata "Name" $newName
|
||||
Move-Item $mdFile $newMd
|
||||
Move-Item $pkgDir $newDir
|
||||
|
||||
if (Test-Path $configXml) {
|
||||
$cfg = New-Object System.Xml.XmlDocument
|
||||
$cfg.PreserveWhitespace = $true
|
||||
$cfg.Load($configXml)
|
||||
$nsm = New-Object System.Xml.XmlNamespaceManager($cfg.NameTable)
|
||||
$nsm.AddNamespace("md", $MD_NS)
|
||||
$found = $false
|
||||
foreach ($e in $cfg.SelectNodes("//md:Configuration/md:ChildObjects/md:XDTOPackage", $nsm)) {
|
||||
if ($e.InnerText -eq $pkgName) { $e.InnerText = $newName; $found = $true; break }
|
||||
}
|
||||
if ($found) {
|
||||
$s = New-Object System.Xml.XmlWriterSettings
|
||||
$s.Encoding = $encBom; $s.Indent = $false
|
||||
$st = New-Object System.IO.FileStream($configXml, [System.IO.FileMode]::Create)
|
||||
$w = [System.Xml.XmlWriter]::Create($st, $s)
|
||||
$cfg.Save($w); $w.Close(); $st.Close()
|
||||
Write-Host " Configuration.xml: <XDTOPackage> переименован в $newName"
|
||||
} else {
|
||||
Write-Warning "В Configuration.xml не найдена запись <XDTOPackage>$pkgName</XDTOPackage> — зарегистрируйте пакет вручную"
|
||||
}
|
||||
}
|
||||
Write-Host "✓ Пакет переименован: $pkgName → $newName"
|
||||
Write-Host " Перемещены: $newName.xml, $newName/"
|
||||
}
|
||||
|
||||
# --- Model edits through the XSD round-trip -------------------------------------
|
||||
|
||||
$XSD_DECL = @{ "add-property" = "element"; "replace-property" = "element"; "remove-property" = "element" }
|
||||
|
||||
function Get-SchemaChildren([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) }
|
||||
}
|
||||
return ,$res
|
||||
}
|
||||
function Get-SchemaFirst([System.Xml.XmlElement]$el, [string]$local) {
|
||||
$r = Get-SchemaChildren $el $local
|
||||
if ($r.Count -gt 0) { return $r[0] }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Find-TypeElement($schema, [string]$typeName) {
|
||||
foreach ($kind in @("complexType", "simpleType")) {
|
||||
foreach ($t in (Get-SchemaChildren $schema $kind)) {
|
||||
if ($t.GetAttribute("name") -eq $typeName) { return $t }
|
||||
}
|
||||
}
|
||||
throw "В пакете нет типа `"$typeName`""
|
||||
}
|
||||
|
||||
# Тело типа: внутрь xs:complexContent/xs:extension, если тип наследуется
|
||||
function Get-TypeBody([System.Xml.XmlElement]$ct) {
|
||||
$content = Get-SchemaFirst $ct "complexContent"
|
||||
if ($content) {
|
||||
$ext = Get-SchemaFirst $content "extension"
|
||||
if ($ext) { return $ext }
|
||||
}
|
||||
return $ct
|
||||
}
|
||||
|
||||
function Find-Declaration([System.Xml.XmlElement]$body, [string]$propName) {
|
||||
foreach ($node in $body.SelectNodes(".//*")) {
|
||||
if ($node.NamespaceURI -ne $XS_NS) { continue }
|
||||
if (@("element", "attribute") -notcontains $node.get_LocalName()) { continue }
|
||||
if ($node.GetAttribute("name") -eq $propName) { return $node }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Путь Тип.Свойство[.Вложенное...] — точка безопасна: имена в модели XDTO
|
||||
# являются идентификаторами 1С и точку содержать не могут
|
||||
function Resolve-Path($schema, [string]$path) {
|
||||
$segments = $path.Split(".")
|
||||
$typeEl = Find-TypeElement $schema $segments[0]
|
||||
if ($segments.Count -eq 1) { return [pscustomobject]@{ Type = $typeEl; Decl = $null } }
|
||||
|
||||
$body = Get-TypeBody $typeEl
|
||||
$decl = $null
|
||||
for ($i = 1; $i -lt $segments.Count; $i++) {
|
||||
$decl = Find-Declaration $body $segments[$i]
|
||||
if (-not $decl) { throw "По пути `"$path`" не найдено свойство `"$($segments[$i])`"" }
|
||||
if ($i -lt $segments.Count - 1) {
|
||||
$inner = Get-SchemaFirst $decl "complexType"
|
||||
if (-not $inner) { throw "Свойство `"$($segments[$i])`" не содержит вложенного типа — путь дальше не идёт" }
|
||||
$body = Get-TypeBody $inner
|
||||
}
|
||||
}
|
||||
return [pscustomobject]@{ Type = $typeEl; Decl = $decl }
|
||||
}
|
||||
|
||||
function Import-Fragment($schema, [string]$xml) {
|
||||
$tmp = New-Object System.Xml.XmlDocument
|
||||
$nsAttrs = " xmlns:xs=`"$XS_NS`" xmlns:xdto=`"$XDTO_NS`""
|
||||
$tns = $schema.GetAttribute("targetNamespace")
|
||||
if ($tns) { $nsAttrs += " xmlns:tns=`"$tns`"" }
|
||||
foreach ($a in $schema.Attributes) {
|
||||
if ($a.Prefix -eq "xmlns" -and $a.get_LocalName() -notin @("xs", "xdto", "tns")) {
|
||||
$nsAttrs += " xmlns:$($a.get_LocalName())=`"$($a.Value)`""
|
||||
}
|
||||
}
|
||||
try { $tmp.LoadXml("<wrap$nsAttrs>$xml</wrap>") }
|
||||
catch {
|
||||
throw ("Не удалось разобрать -Value как фрагмент XML-схемы: " + $_.Exception.InnerException.Message + "`n" +
|
||||
"Получено: " + $xml + "`n" +
|
||||
"Если фрагмент передан инлайном, кавычки могли схлопнуться на границе процессов — " +
|
||||
"положите его в файл и укажите -Value `"@путь`".")
|
||||
}
|
||||
$res = New-Object System.Collections.ArrayList
|
||||
foreach ($c in $tmp.DocumentElement.ChildNodes) {
|
||||
if ($c.NodeType -eq [System.Xml.XmlNodeType]::Element) {
|
||||
[void]$res.Add($schema.OwnerDocument.ImportNode($c, $true))
|
||||
}
|
||||
}
|
||||
if ($res.Count -eq 0) { throw "Во фрагменте нет ни одного элемента: $xml" }
|
||||
return ,$res
|
||||
}
|
||||
|
||||
function Apply-ModelOperation($schema) {
|
||||
switch ($Operation) {
|
||||
|
||||
"add-property" {
|
||||
if (-not $Target) { throw "add-property требует -Target <Тип>" }
|
||||
$loc = Resolve-Path $schema $Target
|
||||
$body = Get-TypeBody $(if ($loc.Decl) { Get-SchemaFirst $loc.Decl "complexType" } else { $loc.Type })
|
||||
foreach ($frag in (Import-Fragment $schema $Value)) {
|
||||
$kind = $frag.get_LocalName()
|
||||
if ($kind -eq "attribute") {
|
||||
$body.AppendChild($frag) | Out-Null
|
||||
} elseif ($kind -eq "element") {
|
||||
$particle = Get-SchemaFirst $body "sequence"
|
||||
if (-not $particle) { $particle = Get-SchemaFirst $body "choice" }
|
||||
if (-not $particle) { $particle = Get-SchemaFirst $body "all" }
|
||||
if (-not $particle) {
|
||||
$particle = $schema.OwnerDocument.CreateElement("xs", "sequence", $XS_NS)
|
||||
$firstAttr = Get-SchemaFirst $body "attribute"
|
||||
if ($firstAttr) { $body.InsertBefore($particle, $firstAttr) | Out-Null }
|
||||
else { $body.AppendChild($particle) | Out-Null }
|
||||
}
|
||||
$particle.AppendChild($frag) | Out-Null
|
||||
} else {
|
||||
throw "add-property ожидает <xs:element> или <xs:attribute>, получен <xs:$kind>"
|
||||
}
|
||||
Write-Host " + $($frag.GetAttribute('name')) в тип $Target"
|
||||
}
|
||||
}
|
||||
|
||||
"replace-property" {
|
||||
if (-not $Target) { throw "replace-property требует -Target `"Тип.Свойство`"" }
|
||||
$loc = Resolve-Path $schema $Target
|
||||
if (-not $loc.Decl) { throw "replace-property требует путь вида `"Тип.Свойство`"" }
|
||||
$frags = Import-Fragment $schema $Value
|
||||
if ($frags.Count -ne 1) { throw "replace-property ожидает ровно одно объявление" }
|
||||
$loc.Decl.ParentNode.ReplaceChild($frags[0], $loc.Decl) | Out-Null
|
||||
Write-Host " ~ $Target заменено"
|
||||
}
|
||||
|
||||
"remove-property" {
|
||||
if (-not $Target) { throw "remove-property требует путь `"Тип.Свойство`"" }
|
||||
foreach ($one in ($Target -split "\s*;;\s*")) {
|
||||
if (-not $one) { continue }
|
||||
$loc = Resolve-Path $schema $one
|
||||
if (-not $loc.Decl) { throw "remove-property требует путь вида `"Тип.Свойство`", получено `"$one`"" }
|
||||
$loc.Decl.ParentNode.RemoveChild($loc.Decl) | Out-Null
|
||||
Write-Host " − $one удалено"
|
||||
}
|
||||
}
|
||||
|
||||
"add-type" {
|
||||
foreach ($frag in (Import-Fragment $schema $Value)) {
|
||||
if (@("complexType", "simpleType") -notcontains $frag.get_LocalName()) {
|
||||
throw "add-type ожидает <xs:complexType> или <xs:simpleType>, получен <xs:$($frag.get_LocalName())>"
|
||||
}
|
||||
$schema.AppendChild($frag) | Out-Null
|
||||
Write-Host " + тип $($frag.GetAttribute('name'))"
|
||||
}
|
||||
}
|
||||
|
||||
"remove-type" {
|
||||
if (-not $Target) { throw "remove-type требует -Target <Тип>" }
|
||||
foreach ($one in ($Target -split "\s*;;\s*")) {
|
||||
if (-not $one) { continue }
|
||||
$t = Find-TypeElement $schema $one
|
||||
$t.ParentNode.RemoveChild($t) | Out-Null
|
||||
Write-Host " − тип $one удалён"
|
||||
}
|
||||
}
|
||||
|
||||
"add-enum" {
|
||||
if (-not $Target) { throw "add-enum требует -Target <ТипЗначения>" }
|
||||
$t = Find-TypeElement $schema $Target
|
||||
$restriction = Get-SchemaFirst $t "restriction"
|
||||
if (-not $restriction) { throw "Тип `"$Target`" не является ограничением простого типа" }
|
||||
foreach ($lit in ($Value -split "\s*;;\s*")) {
|
||||
if (-not $lit) { continue }
|
||||
$e = $schema.OwnerDocument.CreateElement("xs", "enumeration", $XS_NS)
|
||||
$e.SetAttribute("value", $lit)
|
||||
$restriction.AppendChild($e) | Out-Null
|
||||
Write-Host " + значение `"$lit`" в тип $Target"
|
||||
}
|
||||
}
|
||||
|
||||
"add-import" {
|
||||
foreach ($uri in ($Value -split "\s*;;\s*")) {
|
||||
if (-not $uri) { continue }
|
||||
$exists = $false
|
||||
foreach ($i in (Get-SchemaChildren $schema "import")) {
|
||||
if ($i.GetAttribute("namespace") -eq $uri) { $exists = $true; break }
|
||||
}
|
||||
if ($exists) { Write-Host " = импорт $uri уже объявлен"; continue }
|
||||
$imp = $schema.OwnerDocument.CreateElement("xs", "import", $XS_NS)
|
||||
$imp.SetAttribute("namespace", $uri)
|
||||
$firstOther = $null
|
||||
foreach ($c in $schema.ChildNodes) {
|
||||
if ($c.NodeType -eq [System.Xml.XmlNodeType]::Element -and $c.get_LocalName() -notin @("annotation", "import")) { $firstOther = $c; break }
|
||||
}
|
||||
if ($firstOther) { $schema.InsertBefore($imp, $firstOther) | Out-Null } else { $schema.AppendChild($imp) | Out-Null }
|
||||
Write-Host " + импорт $uri"
|
||||
}
|
||||
}
|
||||
|
||||
"set-namespace" {
|
||||
if (-not $Value) { throw "set-namespace требует -Value <URI>" }
|
||||
$old = $schema.GetAttribute("targetNamespace")
|
||||
# Установка того же значения не отбрасывается: пакет пересобирается вхолостую,
|
||||
# и это заодно проба точности пути «выгрузить → собрать» на любом пакете
|
||||
if ($old -eq $Value) { Write-Host " = namespace уже $Value, пакет пересобран без изменений" }
|
||||
# Меняем и targetNamespace, и объявление префикса, который на него указывал:
|
||||
# иначе ссылки на собственные типы станут ссылками в чужое пространство имён
|
||||
$schema.SetAttribute("targetNamespace", $Value)
|
||||
foreach ($a in @($schema.Attributes)) {
|
||||
if ($a.Prefix -eq "xmlns" -and $a.Value -eq $old) {
|
||||
$schema.SetAttribute("xmlns:$($a.get_LocalName())", $Value)
|
||||
}
|
||||
}
|
||||
Write-Host " ~ namespace: $old → $Value"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Dispatch -------------------------------------------------------------------
|
||||
|
||||
$metaOps = @("rename", "set-synonym", "set-comment")
|
||||
$touchesModel = ($metaOps -notcontains $Operation)
|
||||
|
||||
Assert-SiblingsPresent $Operation
|
||||
|
||||
Write-Host "Пакет: $pkgName"
|
||||
|
||||
if ($Operation -eq "rename") {
|
||||
if (-not $Value) { throw "rename требует -Value <НовоеИмя>" }
|
||||
Rename-Package $Value
|
||||
$pkgName = $Value
|
||||
$pkgDir = Join-Path $xdtoRoot $Value
|
||||
} elseif ($Operation -eq "set-synonym") {
|
||||
if (-not $Value) { throw "set-synonym требует -Value <текст>" }
|
||||
Edit-Metadata "Synonym" $Value
|
||||
Write-Host "✓ Синоним: $Value"
|
||||
} elseif ($Operation -eq "set-comment") {
|
||||
Edit-Metadata "Comment" $Value
|
||||
Write-Host "✓ Комментарий обновлён"
|
||||
} else {
|
||||
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ("xdto-edit_" + [guid]::NewGuid().ToString("N").Substring(0, 8))
|
||||
New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
|
||||
try {
|
||||
$xsdPath = Join-Path $tmpDir "schema.xsd"
|
||||
Invoke-Sibling $decompileScript @("-PackagePath", $binFile, "-OutFile", $xsdPath) "xdto-decompile" | Out-Null
|
||||
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $false
|
||||
$doc.Load($xsdPath)
|
||||
$schema = $doc.DocumentElement
|
||||
|
||||
$oldNamespace = $schema.GetAttribute("targetNamespace")
|
||||
Apply-ModelOperation $schema
|
||||
|
||||
$doc.Save($xsdPath)
|
||||
Invoke-Sibling $compileScript @("-XsdPath", $xsdPath, "-OutputDir", $configRoot, "-Name", $pkgName, "-Force") "xdto-compile" | Out-Null
|
||||
|
||||
if ($Operation -eq "set-namespace") {
|
||||
Edit-Metadata "Namespace" $Value
|
||||
# Зависящие пакеты не трогаем: при версионировании они обязаны продолжать
|
||||
# смотреть на прежний namespace. Но молчать о них нельзя.
|
||||
$dependents = @()
|
||||
foreach ($d in (Get-ChildItem $xdtoRoot -Directory -ErrorAction SilentlyContinue)) {
|
||||
if ($d.Name -eq $pkgName) { continue }
|
||||
$ob = Join-Path (Join-Path $d.FullName "Ext") "Package.bin"
|
||||
if (-not (Test-Path $ob)) { continue }
|
||||
try {
|
||||
$od = New-Object System.Xml.XmlDocument
|
||||
$od.Load($ob)
|
||||
foreach ($imp in $od.DocumentElement.ChildNodes) {
|
||||
if ($imp.NodeType -eq [System.Xml.XmlNodeType]::Element -and $imp.get_LocalName() -eq "import" -and
|
||||
$imp.GetAttribute("namespace") -eq $oldNamespace) { $dependents += $d.Name; break }
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if ($dependents.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning ("Старый namespace импортируют пакеты ($($dependents.Count)): " + ($dependents -join ", ") +
|
||||
". Они не изменены — при версионировании это верно; если нет, поправьте их импорты.")
|
||||
}
|
||||
}
|
||||
Write-Host "✓ Пакет пересобран: XDTOPackages/$pkgName/Ext/Package.bin"
|
||||
} finally {
|
||||
Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# --- Validate -------------------------------------------------------------------
|
||||
|
||||
if (-not $NoValidate) {
|
||||
if (Test-Path $validateScript) {
|
||||
Write-Host ""
|
||||
Write-Host "--- xdto-validate ---"
|
||||
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $validateScript -PackagePath (Join-Path $xdtoRoot $pkgName)
|
||||
} else {
|
||||
Write-Host "[SKIP] xdto-validate не найден: $validateScript"
|
||||
}
|
||||
}
|
||||
exit 0
|
||||
Reference in New Issue
Block a user