mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
Add inline mode and complex properties to /meta-edit skill
- Inline mode (-Operation/-Value) as alternative to JSON -DefinitionFile for quick single operations: add/remove/modify attributes, TS, dims, resources, enum values, forms, templates, commands - Batch syntax with ;; separator, positional insertion (>> after, << before) - Complex property support: Owners, RegisterRecords, BasedOn, InputByString with add-*/remove-*/set-* inline ops and JSON modify.properties arrays - Paren-aware comma splitting for types like Number(15,2) in modify values Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: meta-edit
|
||||
description: Точечное редактирование объекта метаданных 1С (добавление/удаление/модификация реквизитов, ТЧ, измерений, ресурсов, значений перечислений, свойств объекта)
|
||||
argument-hint: <DefinitionFile> <ObjectPath> [-NoValidate]
|
||||
description: Точечное редактирование объекта метаданных 1С (добавление/удаление/модификация реквизитов, ТЧ, измерений, ресурсов, значений перечислений, свойств объекта, владельцев, движений, ввода по строке)
|
||||
argument-hint: <ObjectPath> -Operation <op> -Value "<val>" | -DefinitionFile <json> [-NoValidate]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -11,23 +11,127 @@ allowed-tools:
|
||||
|
||||
# /meta-edit — точечное редактирование метаданных 1С
|
||||
|
||||
Атомарные операции модификации существующих XML объектов метаданных: добавление, удаление и модификация реквизитов, табличных частей, измерений, ресурсов, значений перечислений, свойств объекта.
|
||||
Атомарные операции модификации существующих XML объектов метаданных: добавление, удаление и модификация реквизитов, табличных частей, измерений, ресурсов, значений перечислений, свойств объекта, владельцев, движений по регистрам, оснований, ввода по строке.
|
||||
|
||||
## Параметры и команда
|
||||
## Два режима работы
|
||||
|
||||
| Параметр | Обязательный | Описание |
|
||||
|----------|:------------:|----------|
|
||||
| DefinitionFile | да | JSON-файл с операциями |
|
||||
| ObjectPath | да | XML-файл или директория объекта |
|
||||
| NoValidate | нет | Не запускать meta-validate после правки |
|
||||
### Inline mode (рекомендуется для простых операций)
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -ObjectPath "<path>" -Operation <op> -Value "<val>"
|
||||
```
|
||||
|
||||
### JSON mode (для сложных/комбинированных операций)
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -DefinitionFile "<json>" -ObjectPath "<path>"
|
||||
```
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| ObjectPath | XML-файл или директория объекта (обязательный) |
|
||||
| Operation | Inline-операция (альтернатива DefinitionFile) |
|
||||
| Value | Значение для inline-операции |
|
||||
| DefinitionFile | JSON-файл с операциями (альтернатива Operation) |
|
||||
| NoValidate | Не запускать meta-validate после правки |
|
||||
|
||||
`ObjectPath` авторезолв: если указана директория — ищет `<dirName>.xml` в ней.
|
||||
|
||||
## JSON DSL — три операции
|
||||
## Inline mode — операции
|
||||
|
||||
### Batch-режим
|
||||
|
||||
Несколько элементов через `;;`:
|
||||
```
|
||||
-Value "Комментарий: Строка(200) ;; Сумма: Число(15,2) | index"
|
||||
```
|
||||
|
||||
### add-attribute / add-dimension / add-resource / add-column
|
||||
|
||||
Shorthand-формат: `Имя: Тип | флаги`
|
||||
|
||||
```powershell
|
||||
-Operation add-attribute -Value "Комментарий: Строка(200)"
|
||||
-Operation add-attribute -Value "Сумма: Число(15,2) | req, index"
|
||||
-Operation add-attribute -Value "Ном: CatalogRef.Номенклатура | req ;; Кол: Число(15,3)"
|
||||
-Operation add-dimension -Value "Организация: CatalogRef.Организации | master, mainFilter"
|
||||
```
|
||||
|
||||
Позиционная вставка: `>> after ИмяЭлемента` или `<< before ИмяЭлемента`:
|
||||
```powershell
|
||||
-Operation add-attribute -Value "Склад: CatalogRef.Склады >> after Организация"
|
||||
```
|
||||
|
||||
### add-ts
|
||||
|
||||
Формат: `ИмяТЧ: Реквизит1: Тип1, Реквизит2: Тип2, ...`
|
||||
|
||||
```powershell
|
||||
-Operation add-ts -Value "Товары: Ном: CatalogRef.Ном | req, Кол: Число(15,3), Цена: Число(15,2), Сумма: Число(15,2)"
|
||||
```
|
||||
|
||||
### add-enumValue / add-form / add-template / add-command
|
||||
|
||||
Просто имена (batch через `;;`):
|
||||
```powershell
|
||||
-Operation add-enumValue -Value "Значение1 ;; Значение2 ;; Значение3"
|
||||
-Operation add-form -Value "ФормаЭлемента ;; ФормаСписка"
|
||||
```
|
||||
|
||||
### add-owner / add-registerRecord / add-basedOn
|
||||
|
||||
Полное имя метаданных `MetaType.Name`:
|
||||
```powershell
|
||||
-Operation add-owner -Value "Catalog.Контрагенты ;; Catalog.Организации"
|
||||
-Operation add-registerRecord -Value "AccumulationRegister.ОстаткиТоваров"
|
||||
-Operation add-basedOn -Value "Document.ЗаказКлиента"
|
||||
```
|
||||
|
||||
### add-inputByString
|
||||
|
||||
Пути полей (префикс `MetaType.Name.` добавляется автоматически):
|
||||
```powershell
|
||||
-Operation add-inputByString -Value "StandardAttribute.Description ;; StandardAttribute.Code"
|
||||
```
|
||||
|
||||
### remove-*
|
||||
|
||||
Имя элемента (или несколько через `;;`):
|
||||
```powershell
|
||||
-Operation remove-attribute -Value "СтарыйРеквизит ;; ЕщёОдин"
|
||||
-Operation remove-owner -Value "Catalog.Контрагенты"
|
||||
-Operation remove-inputByString -Value "Catalog.МойСпр.StandardAttribute.Code"
|
||||
```
|
||||
|
||||
### modify-attribute / modify-dimension / modify-resource / modify-enumValue / modify-column
|
||||
|
||||
Формат: `ИмяЭлемента: ключ=значение, ключ=значение`
|
||||
|
||||
Ключи: `name` (rename), `type`, `synonym`, `indexing`, `fillChecking`, `use` и др.
|
||||
|
||||
```powershell
|
||||
-Operation modify-attribute -Value "СтароеИмя: name=НовоеИмя, type=Строка(500)"
|
||||
-Operation modify-attribute -Value "Комментарий: indexing=Index"
|
||||
```
|
||||
|
||||
### modify-property
|
||||
|
||||
Формат: `Ключ=Значение` (batch через `;;`):
|
||||
```powershell
|
||||
-Operation modify-property -Value "CodeLength=11 ;; DescriptionLength=150"
|
||||
-Operation modify-property -Value "Hierarchical=true"
|
||||
```
|
||||
|
||||
### set-owners / set-registerRecords / set-basedOn / set-inputByString
|
||||
|
||||
Заменяют **весь список** (в отличие от add/remove):
|
||||
```powershell
|
||||
-Operation set-owners -Value "Catalog.Организации ;; Catalog.Контрагенты"
|
||||
-Operation set-registerRecords -Value "AccumulationRegister.Продажи ;; AccumulationRegister.ОстаткиТоваров"
|
||||
-Operation set-inputByString -Value "StandardAttribute.Description ;; StandardAttribute.Code"
|
||||
```
|
||||
|
||||
## JSON DSL
|
||||
|
||||
### add — добавить элементы
|
||||
|
||||
@@ -67,7 +171,13 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
```json
|
||||
{
|
||||
"modify": {
|
||||
"properties": { "CodeLength": 11, "Hierarchical": true },
|
||||
"properties": {
|
||||
"CodeLength": 11,
|
||||
"Hierarchical": true,
|
||||
"Owners": ["Catalog.Контрагенты", "Catalog.Организации"],
|
||||
"RegisterRecords": ["AccumulationRegister.Продажи"],
|
||||
"InputByString": ["StandardAttribute.Description"]
|
||||
},
|
||||
"реквизиты": {
|
||||
"Комментарий": { "type": "Строка(500)" },
|
||||
"СтароеИмя": { "name": "НовоеИмя" }
|
||||
@@ -78,7 +188,7 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
|
||||
### Комбинирование
|
||||
|
||||
Все три операции можно указать в одном JSON-файле.
|
||||
Все три операции можно указать в одном JSON-файле. Для сложных сценариев (ТЧ с реквизитами + удаление + модификация) используйте JSON DSL.
|
||||
|
||||
### Синонимы ключей (case-insensitive)
|
||||
|
||||
@@ -103,14 +213,11 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
|
||||
`Строка(200)`, `Число(15,2)`, `Булево`, `Дата`, `ДатаВремя`, `ХранилищеЗначения`, `СправочникСсылка.XXX`, `ДокументСсылка.XXX`, `ПеречислениеСсылка.XXX`, `ОпределяемыйТип.XXX`.
|
||||
|
||||
### Позиционная вставка (опционально)
|
||||
### Позиционная вставка
|
||||
|
||||
```json
|
||||
{ "name": "Склад", "type": "CatalogRef.Склады", "after": "Организация" }
|
||||
{ "name": "Приоритет", "type": "Число(1)", "before": "Комментарий" }
|
||||
```
|
||||
JSON: `{ "name": "Склад", "type": "CatalogRef.Склады", "after": "Организация" }`
|
||||
|
||||
Без указания — в конец группы однотипных элементов.
|
||||
Inline: `"Склад: CatalogRef.Склады >> after Организация"` или `"Склад: CatalogRef.Склады << before Комментарий"`
|
||||
|
||||
### Shorthand-формат реквизитов
|
||||
|
||||
@@ -118,6 +225,17 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
"ИмяРеквизита: Тип | req, index"
|
||||
```
|
||||
|
||||
## Complex properties — Owners, RegisterRecords, BasedOn, InputByString
|
||||
|
||||
Свойства со вложенной XML-структурой (не скалярный InnerText). Поддерживаются через inline-операции `add-*` / `remove-*` / `set-*` и через JSON `modify.properties`.
|
||||
|
||||
| Свойство | Объекты | XML-тег | Inline-значение |
|
||||
|----------|---------|---------|-----------------|
|
||||
| Owners | Catalog, ChartOfCharacteristicTypes | `<xr:Item xsi:type="xr:MDObjectRef">` | `Catalog.XXX` |
|
||||
| RegisterRecords | Document | `<xr:Item xsi:type="xr:MDObjectRef">` | `AccumulationRegister.XXX` |
|
||||
| BasedOn | Document, Catalog, BP, Task | `<xr:Item xsi:type="xr:MDObjectRef">` | `Document.XXX` |
|
||||
| InputByString | Catalog, ChartOf*, Task | `<xr:Field>` | `StandardAttribute.Description` |
|
||||
|
||||
## Поддерживаемые типы объектов
|
||||
|
||||
| Тип объекта | Допустимые add-типы |
|
||||
@@ -130,58 +248,55 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
|
||||
## Примеры
|
||||
|
||||
### Добавить реквизиты в справочник
|
||||
### Inline: добавить реквизиты
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"attributes": [
|
||||
"Комментарий: String(200)",
|
||||
{ "name": "Сумма", "type": "Number(15,2)", "indexing": "Index" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```powershell
|
||||
-Operation add-attribute -Value "Комментарий: String(200) ;; Сумма: Число(15,2) | index"
|
||||
```
|
||||
|
||||
### Добавить ТЧ в документ
|
||||
### Inline: добавить ТЧ с реквизитами
|
||||
|
||||
```powershell
|
||||
-Operation add-ts -Value "Товары: Ном: CatalogRef.Ном | req, Кол: Число(15,3), Цена: Число(15,2)"
|
||||
```
|
||||
|
||||
### Inline: удалить + изменить (два вызова)
|
||||
|
||||
```powershell
|
||||
-Operation remove-attribute -Value "УстаревшийРеквизит"
|
||||
-Operation modify-attribute -Value "СтароеИмя: name=НовоеИмя, type=String(500)"
|
||||
```
|
||||
|
||||
### Inline: владельцы справочника
|
||||
|
||||
```powershell
|
||||
-Operation set-owners -Value "Catalog.Контрагенты ;; Catalog.Организации"
|
||||
```
|
||||
|
||||
### Inline: движения документа
|
||||
|
||||
```powershell
|
||||
-Operation add-registerRecord -Value "AccumulationRegister.Продажи ;; AccumulationRegister.ОстаткиТоваров"
|
||||
```
|
||||
|
||||
### JSON: комплексное редактирование
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"attributes": ["Комментарий: String(200)"],
|
||||
"tabularSections": [{
|
||||
"name": "Товары",
|
||||
"attrs": [
|
||||
"Номенклатура: CatalogRef.Номенклатура | req",
|
||||
"Количество: Number(15,3)",
|
||||
"Цена: Number(15,2)",
|
||||
"Сумма: Number(15,2)"
|
||||
]
|
||||
"attrs": ["Ном: CatalogRef.Ном | req", "Кол: Number(15,3)"]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Добавить измерения и ресурсы в регистр
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"dimensions": ["Организация: CatalogRef.Организации | master, mainFilter"],
|
||||
"resources": ["Сумма: Number(15,2)"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Удалить + изменить
|
||||
|
||||
```json
|
||||
{
|
||||
},
|
||||
"remove": { "attributes": ["УстаревшийРеквизит"] },
|
||||
"modify": {
|
||||
"properties": { "DescriptionLength": 150 },
|
||||
"attributes": {
|
||||
"СтароеИмя": { "name": "НовоеИмя", "type": "String(500)" }
|
||||
}
|
||||
"properties": {
|
||||
"DescriptionLength": 150,
|
||||
"Owners": ["Catalog.Контрагенты", "Catalog.Организации"]
|
||||
},
|
||||
"attributes": { "СтароеИмя": { "name": "НовоеИмя" } }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -201,3 +316,7 @@ powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -
|
||||
- **Изменение свойств** объекта (длина кода, иерархия и т.д.)
|
||||
- **Добавление значений** перечислений
|
||||
- **Добавление измерений/ресурсов** в регистры
|
||||
- **Управление владельцами** справочников (Owners)
|
||||
- **Управление движениями** документов (RegisterRecords)
|
||||
- **Настройка ввода по строке** (InputByString)
|
||||
- **Управление основаниями** (BasedOn)
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
# meta-edit v1.0 — Edit existing 1C metadata object XML
|
||||
# meta-edit v1.1 — Edit existing 1C metadata object XML (inline mode + complex properties)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$DefinitionFile,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ObjectPath,
|
||||
|
||||
# Inline mode (alternative to DefinitionFile)
|
||||
[ValidateSet(
|
||||
"add-attribute", "add-ts", "add-dimension", "add-resource",
|
||||
"add-enumValue", "add-column", "add-form", "add-template", "add-command",
|
||||
"add-owner", "add-registerRecord", "add-basedOn", "add-inputByString",
|
||||
"remove-attribute", "remove-ts", "remove-dimension", "remove-resource",
|
||||
"remove-enumValue", "remove-column", "remove-form", "remove-template", "remove-command",
|
||||
"remove-owner", "remove-registerRecord", "remove-basedOn", "remove-inputByString",
|
||||
"modify-attribute", "modify-dimension", "modify-resource",
|
||||
"modify-enumValue", "modify-column",
|
||||
"modify-property",
|
||||
"set-owners", "set-registerRecords", "set-basedOn", "set-inputByString"
|
||||
)]
|
||||
[string]$Operation,
|
||||
[string]$Value,
|
||||
|
||||
[switch]$NoValidate
|
||||
)
|
||||
|
||||
@@ -17,13 +32,26 @@ $ErrorActionPreference = "Stop"
|
||||
# Section 1: Parameters + loading
|
||||
# ============================================================
|
||||
|
||||
# --- Load JSON definition ---
|
||||
if (-not (Test-Path $DefinitionFile)) {
|
||||
Write-Error "Definition file not found: $DefinitionFile"
|
||||
# --- Mode validation ---
|
||||
if ($DefinitionFile -and $Operation) {
|
||||
Write-Error "Cannot use both -DefinitionFile and -Operation"
|
||||
exit 1
|
||||
}
|
||||
$jsonText = Get-Content -Raw -Encoding UTF8 $DefinitionFile
|
||||
$def = $jsonText | ConvertFrom-Json
|
||||
if (-not $DefinitionFile -and -not $Operation) {
|
||||
Write-Error "Either -DefinitionFile or -Operation is required"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Load JSON definition (DefinitionFile mode) ---
|
||||
$def = $null
|
||||
if ($DefinitionFile) {
|
||||
if (-not (Test-Path $DefinitionFile)) {
|
||||
Write-Error "Definition file not found: $DefinitionFile"
|
||||
exit 1
|
||||
}
|
||||
$jsonText = Get-Content -Raw -Encoding UTF8 $DefinitionFile
|
||||
$def = $jsonText | ConvertFrom-Json
|
||||
}
|
||||
|
||||
# --- Resolve object path ---
|
||||
if (Test-Path $ObjectPath -PathType Container) {
|
||||
@@ -553,6 +581,14 @@ function Parse-AttributeShorthand {
|
||||
flags = @(); fillChecking = ""; indexing = ""
|
||||
after = ""; before = ""
|
||||
}
|
||||
# Extract positional markers: >> after Name, << before Name
|
||||
if ($str -match '\s*>>\s*after\s+(\S+)\s*$') {
|
||||
$parsed.after = $Matches[1]
|
||||
$str = ($str -replace '\s*>>\s*after\s+\S+\s*$', '').Trim()
|
||||
} elseif ($str -match '\s*<<\s*before\s+(\S+)\s*$') {
|
||||
$parsed.before = $Matches[1]
|
||||
$str = ($str -replace '\s*<<\s*before\s+\S+\s*$', '').Trim()
|
||||
}
|
||||
# Split by | for flags
|
||||
$parts = $str -split '\|', 2
|
||||
$mainPart = $parts[0].Trim()
|
||||
@@ -1164,6 +1200,186 @@ function Resolve-ChildTypeKey([string]$key) {
|
||||
return $null
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Section 9.5: Inline mode converter
|
||||
# ============================================================
|
||||
|
||||
function Split-ByCommaOutsideParens([string]$str) {
|
||||
$result = @()
|
||||
$depth = 0
|
||||
$current = ""
|
||||
foreach ($ch in $str.ToCharArray()) {
|
||||
if ($ch -eq '(') { $depth++ }
|
||||
elseif ($ch -eq ')') { $depth-- }
|
||||
if ($ch -eq ',' -and $depth -eq 0) {
|
||||
$result += $current
|
||||
$current = ""
|
||||
} else {
|
||||
$current += $ch
|
||||
}
|
||||
}
|
||||
if ($current) { $result += $current }
|
||||
return ,$result
|
||||
}
|
||||
|
||||
function Convert-InlineToDefinition([string]$operation, [string]$value) {
|
||||
# Parse operation: "add-attribute" → ("add", "attribute")
|
||||
$opParts = $operation -split '-', 2
|
||||
$op = $opParts[0] # add, remove, modify, set
|
||||
$target = $opParts[1] # attribute, ts, owner, owners, property, etc.
|
||||
|
||||
# Complex property targets
|
||||
$complexTargetMap = @{
|
||||
"owner" = "Owners"; "owners" = "Owners"
|
||||
"registerRecord" = "RegisterRecords"; "registerRecords" = "RegisterRecords"
|
||||
"basedOn" = "BasedOn"
|
||||
"inputByString" = "InputByString"
|
||||
}
|
||||
|
||||
if ($complexTargetMap.ContainsKey($target)) {
|
||||
$propName = $complexTargetMap[$target]
|
||||
$values = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
# For InputByString, auto-prefix with MetaType.Name.
|
||||
if ($propName -eq "InputByString") {
|
||||
$prefix = "$($script:objType).$($script:objName)."
|
||||
$values = @($values | ForEach-Object {
|
||||
if ($_ -notmatch '\.') {
|
||||
"$prefix$_"
|
||||
} elseif ($_ -notmatch '^(Catalog|Document|InformationRegister|AccumulationRegister|AccountingRegister|CalculationRegister|ChartOfCharacteristicTypes|ChartOfCalculationTypes|ChartOfAccounts|ExchangePlan|BusinessProcess|Task|Enum|Report|DataProcessor)\.') {
|
||||
"$prefix$_"
|
||||
} else { $_ }
|
||||
})
|
||||
}
|
||||
$def = New-Object PSCustomObject
|
||||
$complexAction = if ($op -eq "set") { "set" } else { $op }
|
||||
$def | Add-Member -NotePropertyName "_complex" -NotePropertyValue @(
|
||||
@{ action = $complexAction; property = $propName; values = $values }
|
||||
)
|
||||
return $def
|
||||
}
|
||||
|
||||
# Target → JSON DSL child type
|
||||
$targetMap = @{
|
||||
"attribute" = "attributes"
|
||||
"ts" = "tabularSections"
|
||||
"dimension" = "dimensions"
|
||||
"resource" = "resources"
|
||||
"enumValue" = "enumValues"
|
||||
"column" = "columns"
|
||||
"form" = "forms"
|
||||
"template" = "templates"
|
||||
"command" = "commands"
|
||||
"property" = "properties"
|
||||
}
|
||||
|
||||
$childType = $targetMap[$target]
|
||||
if (-not $childType) {
|
||||
Write-Error "Unknown inline target: $target"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$def = New-Object PSCustomObject
|
||||
|
||||
switch ($op) {
|
||||
"add" {
|
||||
$items = @()
|
||||
if ($childType -eq "tabularSections") {
|
||||
# TS format: "TSName: attr1_shorthand, attr2_shorthand, ..."
|
||||
$tsValues = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
foreach ($tsVal in $tsValues) {
|
||||
$colonIdx = $tsVal.IndexOf(':')
|
||||
if ($colonIdx -gt 0) {
|
||||
$tsName = $tsVal.Substring(0, $colonIdx).Trim()
|
||||
$attrsPart = $tsVal.Substring($colonIdx + 1).Trim()
|
||||
# Split attrs by comma (paren-aware), reassemble if part doesn't start with "Name:"
|
||||
$rawParts = Split-ByCommaOutsideParens $attrsPart
|
||||
$attrStrs = @()
|
||||
$current = ""
|
||||
foreach ($rp in $rawParts) {
|
||||
$rp = $rp.Trim()
|
||||
if ($current -and $rp -match '^[А-Яа-яЁёA-Za-z_]\w*\s*:') {
|
||||
$attrStrs += $current
|
||||
$current = $rp
|
||||
} elseif ($current) {
|
||||
$current += ", $rp"
|
||||
} else {
|
||||
$current = $rp
|
||||
}
|
||||
}
|
||||
if ($current) { $attrStrs += $current }
|
||||
$items += [PSCustomObject]@{ name = $tsName; attrs = $attrStrs }
|
||||
} else {
|
||||
# Just a name, no attrs
|
||||
$items += $tsVal
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Batch split by ;;
|
||||
$items = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
}
|
||||
$addObj = New-Object PSCustomObject
|
||||
$addObj | Add-Member -NotePropertyName $childType -NotePropertyValue $items
|
||||
$def | Add-Member -NotePropertyName "add" -NotePropertyValue $addObj
|
||||
}
|
||||
"remove" {
|
||||
$items = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
$removeObj = New-Object PSCustomObject
|
||||
$removeObj | Add-Member -NotePropertyName $childType -NotePropertyValue $items
|
||||
$def | Add-Member -NotePropertyName "remove" -NotePropertyValue $removeObj
|
||||
}
|
||||
"modify" {
|
||||
if ($childType -eq "properties") {
|
||||
# "CodeLength=11 ;; DescriptionLength=150"
|
||||
$kvPairs = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
$propsObj = New-Object PSCustomObject
|
||||
foreach ($kv in $kvPairs) {
|
||||
$eqIdx = $kv.IndexOf('=')
|
||||
if ($eqIdx -gt 0) {
|
||||
$k = $kv.Substring(0, $eqIdx).Trim()
|
||||
$v = $kv.Substring($eqIdx + 1).Trim()
|
||||
$propsObj | Add-Member -NotePropertyName $k -NotePropertyValue $v
|
||||
} else {
|
||||
Warn "Invalid property format (expected Key=Value): $kv"
|
||||
}
|
||||
}
|
||||
$modifyObj = New-Object PSCustomObject
|
||||
$modifyObj | Add-Member -NotePropertyName "properties" -NotePropertyValue $propsObj
|
||||
$def | Add-Member -NotePropertyName "modify" -NotePropertyValue $modifyObj
|
||||
} else {
|
||||
# "ElementName: key=val, key=val ;; Element2: key=val"
|
||||
$elemDefs = @($value -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
$childModObj = New-Object PSCustomObject
|
||||
foreach ($elemDef in $elemDefs) {
|
||||
$colonIdx = $elemDef.IndexOf(':')
|
||||
if ($colonIdx -le 0) {
|
||||
Warn "Invalid modify format (expected Name: key=val): $elemDef"
|
||||
continue
|
||||
}
|
||||
$elemName = $elemDef.Substring(0, $colonIdx).Trim()
|
||||
$changesPart = $elemDef.Substring($colonIdx + 1).Trim()
|
||||
$changesObj = New-Object PSCustomObject
|
||||
$changePairs = Split-ByCommaOutsideParens $changesPart
|
||||
foreach ($cp in $changePairs) {
|
||||
$cp = $cp.Trim()
|
||||
$eqIdx = $cp.IndexOf('=')
|
||||
if ($eqIdx -gt 0) {
|
||||
$ck = $cp.Substring(0, $eqIdx).Trim()
|
||||
$cv = $cp.Substring($eqIdx + 1).Trim()
|
||||
$changesObj | Add-Member -NotePropertyName $ck -NotePropertyValue $cv
|
||||
}
|
||||
}
|
||||
$childModObj | Add-Member -NotePropertyName $elemName -NotePropertyValue $changesObj
|
||||
}
|
||||
$modifyObj = New-Object PSCustomObject
|
||||
$modifyObj | Add-Member -NotePropertyName $childType -NotePropertyValue $childModObj
|
||||
$def | Add-Member -NotePropertyName "modify" -NotePropertyValue $modifyObj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $def
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Section 10: ADD operations
|
||||
# ============================================================
|
||||
@@ -1443,6 +1659,18 @@ function Modify-Properties($propsDef) {
|
||||
return
|
||||
}
|
||||
|
||||
# Complex property: Owners, RegisterRecords, BasedOn, InputByString
|
||||
if ($script:complexPropertyMap.ContainsKey($propName)) {
|
||||
$valuesList = @()
|
||||
if ($propValue -is [array]) {
|
||||
$valuesList = @($propValue | ForEach-Object { "$_" })
|
||||
} else {
|
||||
$valuesList = @("$propValue" -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
}
|
||||
Set-ComplexProperty $propName $valuesList
|
||||
return
|
||||
}
|
||||
|
||||
# Handle boolean values
|
||||
$valueStr = "$propValue"
|
||||
if ($propValue -is [bool]) {
|
||||
@@ -1647,12 +1875,196 @@ function Process-Modify($modifyDef) {
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Section 12.5: Complex property helpers
|
||||
# ============================================================
|
||||
|
||||
$script:complexPropertyMap = @{
|
||||
"Owners" = @{ tag = "xr:Item"; attr = 'xsi:type="xr:MDObjectRef"' }
|
||||
"RegisterRecords" = @{ tag = "xr:Item"; attr = 'xsi:type="xr:MDObjectRef"' }
|
||||
"BasedOn" = @{ tag = "xr:Item"; attr = 'xsi:type="xr:MDObjectRef"' }
|
||||
"InputByString" = @{ tag = "xr:Field"; attr = $null }
|
||||
}
|
||||
|
||||
function Find-PropertyElement([string]$propName) {
|
||||
foreach ($child in $script:propertiesEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $propName) {
|
||||
return $child
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-ComplexPropertyValues([System.Xml.XmlElement]$propEl) {
|
||||
$values = @()
|
||||
foreach ($child in $propEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element') {
|
||||
$values += $child.InnerText.Trim()
|
||||
}
|
||||
}
|
||||
return $values
|
||||
}
|
||||
|
||||
function Add-ComplexPropertyItem([string]$propertyName, [string[]]$values) {
|
||||
$mapEntry = $script:complexPropertyMap[$propertyName]
|
||||
if (-not $mapEntry) { Warn "Unknown complex property: $propertyName"; return }
|
||||
|
||||
$propEl = Find-PropertyElement $propertyName
|
||||
if (-not $propEl) {
|
||||
Warn "Property element '$propertyName' not found in Properties"
|
||||
return
|
||||
}
|
||||
|
||||
# Get existing values to check duplicates
|
||||
$existing = Get-ComplexPropertyValues $propEl
|
||||
|
||||
$indent = Get-ChildIndent $script:propertiesEl
|
||||
$childIndent = "$indent`t"
|
||||
|
||||
# Check if element is self-closing (empty)
|
||||
$isEmpty = $true
|
||||
foreach ($ch in $propEl.ChildNodes) {
|
||||
if ($ch.NodeType -eq 'Element') { $isEmpty = $false; break }
|
||||
}
|
||||
|
||||
# If self-closing / empty, add closing whitespace
|
||||
if ($isEmpty -and $propEl.ChildNodes.Count -eq 0) {
|
||||
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$indent")
|
||||
$propEl.AppendChild($closeWs) | Out-Null
|
||||
}
|
||||
|
||||
foreach ($val in $values) {
|
||||
if ($val -in $existing) {
|
||||
Warn "$propertyName already contains '$val', skipping"
|
||||
continue
|
||||
}
|
||||
$tag = $mapEntry.tag
|
||||
$attrStr = $mapEntry.attr
|
||||
if ($attrStr) {
|
||||
$fragXml = "<$tag $attrStr>$(Esc-Xml $val)</$tag>"
|
||||
} else {
|
||||
$fragXml = "<$tag>$(Esc-Xml $val)</$tag>"
|
||||
}
|
||||
$nodes = Import-Fragment $fragXml
|
||||
foreach ($node in $nodes) {
|
||||
Insert-BeforeElement $propEl $node $null $childIndent
|
||||
}
|
||||
Info "Added $propertyName item: $val"
|
||||
$script:addCount++
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-ComplexPropertyItem([string]$propertyName, [string[]]$values) {
|
||||
$propEl = Find-PropertyElement $propertyName
|
||||
if (-not $propEl) {
|
||||
Warn "Property element '$propertyName' not found in Properties"
|
||||
return
|
||||
}
|
||||
|
||||
foreach ($val in $values) {
|
||||
$found = $false
|
||||
foreach ($child in @($propEl.ChildNodes)) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.InnerText.Trim() -eq $val) {
|
||||
Remove-NodeWithWhitespace $child
|
||||
Info "Removed $propertyName item: $val"
|
||||
$script:removeCount++
|
||||
$found = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $found) {
|
||||
Warn "$propertyName item '$val' not found, skipping"
|
||||
}
|
||||
}
|
||||
|
||||
# Collapse if empty
|
||||
$hasElements = $false
|
||||
foreach ($ch in $propEl.ChildNodes) {
|
||||
if ($ch.NodeType -eq 'Element') { $hasElements = $true; break }
|
||||
}
|
||||
if (-not $hasElements) {
|
||||
while ($propEl.HasChildNodes) {
|
||||
$propEl.RemoveChild($propEl.FirstChild) | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Set-ComplexProperty([string]$propertyName, [string[]]$values) {
|
||||
$mapEntry = $script:complexPropertyMap[$propertyName]
|
||||
if (-not $mapEntry) { Warn "Unknown complex property: $propertyName"; return }
|
||||
|
||||
$propEl = Find-PropertyElement $propertyName
|
||||
if (-not $propEl) {
|
||||
Warn "Property element '$propertyName' not found in Properties"
|
||||
return
|
||||
}
|
||||
|
||||
$indent = Get-ChildIndent $script:propertiesEl
|
||||
$childIndent = "$indent`t"
|
||||
|
||||
# Remove all existing children
|
||||
while ($propEl.HasChildNodes) {
|
||||
$propEl.RemoveChild($propEl.FirstChild) | Out-Null
|
||||
}
|
||||
|
||||
if ($values.Count -eq 0) {
|
||||
# Leave self-closing
|
||||
Info "Cleared $propertyName"
|
||||
$script:modifyCount++
|
||||
return
|
||||
}
|
||||
|
||||
# Add closing whitespace
|
||||
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$indent")
|
||||
$propEl.AppendChild($closeWs) | Out-Null
|
||||
|
||||
# Add each value
|
||||
foreach ($val in $values) {
|
||||
$tag = $mapEntry.tag
|
||||
$attrStr = $mapEntry.attr
|
||||
if ($attrStr) {
|
||||
$fragXml = "<$tag $attrStr>$(Esc-Xml $val)</$tag>"
|
||||
} else {
|
||||
$fragXml = "<$tag>$(Esc-Xml $val)</$tag>"
|
||||
}
|
||||
$nodes = Import-Fragment $fragXml
|
||||
foreach ($node in $nodes) {
|
||||
Insert-BeforeElement $propEl $node $null $childIndent
|
||||
}
|
||||
}
|
||||
$count = $values.Count
|
||||
Info "Set $propertyName`: $count items"
|
||||
$script:modifyCount++
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Section 13: Main processing
|
||||
# ============================================================
|
||||
|
||||
# --- Inline mode conversion ---
|
||||
if ($Operation) {
|
||||
$def = Convert-InlineToDefinition $Operation $Value
|
||||
}
|
||||
if (-not $def) {
|
||||
Write-Error "No definition loaded"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Process complex property operations ---
|
||||
if ($def.PSObject.Properties.Match("_complex").Count -gt 0 -and $def._complex) {
|
||||
foreach ($cop in $def._complex) {
|
||||
switch ($cop.action) {
|
||||
"add" { Add-ComplexPropertyItem $cop.property $cop.values }
|
||||
"remove" { Remove-ComplexPropertyItem $cop.property $cop.values }
|
||||
"set" { Set-ComplexProperty $cop.property $cop.values }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Process standard operations ---
|
||||
$def.PSObject.Properties | ForEach-Object {
|
||||
$prop = $_
|
||||
if ($prop.Name -eq "_complex") { return }
|
||||
$opKey = Resolve-OperationKey $prop.Name
|
||||
if (-not $opKey) {
|
||||
Warn "Unknown operation: $($prop.Name)"
|
||||
|
||||
Reference in New Issue
Block a user