Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* This file is a part of MDClasses.
*
* Copyright (c) 2019 - 2026
* Tymko Oleg <[email protected]>, Maximov Valery <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* MDClasses is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* MDClasses is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with MDClasses.
*/
package com.github._1c_syntax.bsl.mdclasses;

import lombok.Builder;
import org.jspecify.annotations.Nullable;

import java.nio.charset.StandardCharsets;

/**
* Настройки записи объектов метаданных в файлы (EDT и Designer).
* Используется при вызове {@link com.github._1c_syntax.bsl.mdclasses.MDClasses#writeObject(java.nio.file.Path, Object, MDCWriteSettings)}.
*
* @param encoding Кодировка записываемых файлов (по умолчанию UTF-8)
*/
@Builder
public record MDCWriteSettings(@Nullable String encoding) {

/**
* Настройки по умолчанию: кодировка UTF-8.
*/
public static final MDCWriteSettings DEFAULT = MDCWriteSettings.builder()
.encoding(StandardCharsets.UTF_8.name())
.build();

/**
* Возвращает кодировку для записи файлов; при null в настройках возвращается UTF-8.
*/
public String encoding() {
return encoding != null ? encoding : StandardCharsets.UTF_8.name();
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/github/_1c_syntax/bsl/mdclasses/MDClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@
import com.github._1c_syntax.bsl.reader.MDMerger;
import com.github._1c_syntax.bsl.reader.MDOReader;
import com.github._1c_syntax.bsl.types.MDOType;
import com.github._1c_syntax.bsl.writer.MDOWriter;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.io.IOException;
import java.nio.file.Files;
Expand Down Expand Up @@ -66,6 +69,29 @@ public ExternalSource createExternalReport() {
return ExternalReport.EMPTY;
}

/**
* Записывает объект метаданных в файл (формат по расширению пути: .mdo — EDT, .xml — Designer).
*
* @param path Путь к файлу (например, .../Subsystems/Name/Name.mdo или .../Subsystems/Name.xml)
* @param object Объект метаданных (поддерживается Subsystem, Catalog, Configuration)
* @throws IOException при ошибке записи
*/
public void writeObject(@NonNull Path path, @NonNull Object object) throws IOException {
MDOWriter.writeObject(path, object);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Записывает объект метаданных в файл с настройками записи.
*
* @param path Путь к файлу
* @param object Объект метаданных
* @param writeSettings Настройки записи
* @throws IOException при ошибке записи
*/
public void writeObject(@NonNull Path path, @NonNull Object object, @Nullable MDCWriteSettings writeSettings) throws IOException {
MDOWriter.writeObject(path, object, writeSettings);
}

/**
* Создает конфигурацию или расширение по указанному пути
*
Expand Down
75 changes: 75 additions & 0 deletions src/main/java/com/github/_1c_syntax/bsl/writer/MDOWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* This file is a part of MDClasses.
*
* Copyright (c) 2019 - 2026
* Tymko Oleg <[email protected]>, Maximov Valery <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* MDClasses is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* MDClasses is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with MDClasses.
*/
package com.github._1c_syntax.bsl.writer;

import com.github._1c_syntax.bsl.mdclasses.MDCWriteSettings;
import com.github._1c_syntax.bsl.writer.designer.DesignerWriter;
import com.github._1c_syntax.bsl.writer.edt.EDTWriter;
import lombok.experimental.UtilityClass;
import org.apache.commons.io.FilenameUtils;

import java.io.IOException;
import java.nio.file.Path;

/**
* Фасад записи объектов метаданных в файлы (EDT .mdo и Designer .xml).
*/
@UtilityClass
public class MDOWriter {

/**
* Записывает объект метаданных в файл.
* Формат определяется по расширению пути: .mdo — EDT, .xml — Designer.
*
* @param path Путь к файлу (например, .../Subsystems/Name/Name.mdo или .../Subsystems/Name.xml)
* @param object Объект метаданных (Subsystem, Catalog, Configuration)
* @throws IOException при ошибке записи
* @throws UnsupportedOperationException если формат или тип объекта не поддерживается
*/
public void writeObject(Path path, Object object) throws IOException {
writeObject(path, object, MDCWriteSettings.DEFAULT);
}

/**
* Записывает объект метаданных в файл с настройками.
*
* @param path Путь к файлу (расширение .mdo или .xml определяет формат)
* @param object Объект метаданных (Subsystem, Catalog, Configuration)
* @param writeSettings Настройки записи (кодировка и др.); может быть null — тогда используются настройки по умолчанию
* @throws IOException при ошибке записи
* @throws UnsupportedOperationException если формат или тип объекта не поддерживается
*/
public void writeObject(Path path, Object object, MDCWriteSettings writeSettings) throws IOException {
if (path == null || object == null) {
throw new IllegalArgumentException("path and object must not be null");
}
if (FilenameUtils.isExtension(path.toString(), "mdo")) {
var writer = new EDTWriter(writeSettings);
writer.write(path, object);
} else if (FilenameUtils.isExtension(path.toString(), "xml")) {
var writer = new DesignerWriter(writeSettings);
writer.write(path, object);
} else {
throw new UnsupportedOperationException("Write is supported only for EDT (.mdo) or Designer (.xml) format, got: " + path);
}
Comment on lines +61 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize unsupported-type handling before dispatch.

The facade documents one failure mode, but the .mdo branch can currently leak IllegalArgumentException from src/main/java/com/github/_1c_syntax/bsl/writer/edt/EDTWriter.java:77-80, while the .xml branch uses UnsupportedOperationException via src/main/java/com/github/_1c_syntax/bsl/writer/designer/DesignerWriter.java:67-70. The same unsupported object should not fail differently based only on the extension.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/github/_1c_syntax/bsl/writer/MDOWriter.java` around lines
61 - 73, MDOWriter.writeObject currently dispatches to EDTWriter or
DesignerWriter and can leak an IllegalArgumentException from EDTWriter while
DesignerWriter throws UnsupportedOperationException; update writeObject to
normalize unsupported-type handling by validating the object before dispatch or
by catching IllegalArgumentException from new
EDTWriter(writeSettings).writer.write(...) and rethrowing a consistent
UnsupportedOperationException with the same message format used for the .xml
branch; reference MDOWriter.writeObject, EDTWriter, and DesignerWriter to locate
the code and ensure all unsupported-object failures use
UnsupportedOperationException.

}
}
135 changes: 135 additions & 0 deletions src/main/java/com/github/_1c_syntax/bsl/writer/ReadWriteDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* This file is a part of MDClasses.
*
* Copyright (c) 2019 - 2026
* Tymko Oleg <[email protected]>, Maximov Valery <[email protected]> and contributors
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* MDClasses is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* MDClasses is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with MDClasses.
*/
package com.github._1c_syntax.bsl.writer;

import com.github._1c_syntax.bsl.mdclasses.Configuration;
import com.github._1c_syntax.bsl.mdclasses.MDClasses;
import com.github._1c_syntax.bsl.mdclasses.MDCReadSettings;
import com.github._1c_syntax.bsl.mdo.Catalog;
import com.github._1c_syntax.bsl.mdo.Subsystem;
import com.github._1c_syntax.bsl.reader.MDOReader;
import com.github._1c_syntax.bsl.reader.edt.EDTReader;
import com.github._1c_syntax.bsl.types.MultiLanguageString;

import java.nio.file.Files;
import java.nio.file.Paths;

/**
* Демонстрация чтения и записи метаданных: три типа объектов (Subsystem, Catalog, Configuration)
* в двух форматах — EDT (.mdo) и Конфигуратор (Designer .xml). Артефакты раскладываются по каталогам:
* build/read-write-demo-output/edt/ и build/read-write-demo-output/designer/.
* Запуск: {@code ./gradlew runReadWriteDemo} или указать путь в аргументе.
*/
public final class ReadWriteDemo {

private ReadWriteDemo() {
}

/**
* Точка входа: создаёт примеры Subsystem, Catalog, Configuration и записывает их в EDT и Designer.
*
* @param args необязательный путь к каталогу вывода; по умолчанию build/read-write-demo-output
*/
public static void main(String[] args) throws Exception {
var baseDir = args.length > 0 ? Paths.get(args[0]) : Paths.get("build", "read-write-demo-output");
var edtDir = baseDir.resolve("edt");
var designerDir = baseDir.resolve("designer");
var edtSrc = edtDir.resolve("src");
var designerSrc = designerDir.resolve("src").resolve("cf");
Files.createDirectories(edtSrc);
Files.createDirectories(designerSrc);

var subsystem = Subsystem.builder()
.name("DemoSubsystem")
.uuid("0421b67e-ed26-491d-ab98-ec59002ed4ce")
.synonym(MultiLanguageString.create("ru", "Демо подсистема"))
.build();
var catalog = Catalog.builder()
.name("DemoCatalog")
.uuid("c6c26c3c-de7a-4ed4-944d-ada62cf1ab8f")
.synonym(MultiLanguageString.create("ru", "Демо справочник"))
.build();
var config = Configuration.builder()
.name("DemoConfiguration")
.uuid("46c7c1d0-b04d-4295-9b04-ae3207c18d29")
.build();

var ok = true;

// --- EDT (.mdo) — каталог edt/ ---
System.out.println("=== EDT (edt/) ===");
var subsystemMdo = edtSrc.resolve("Subsystems").resolve("DemoSubsystem").resolve("DemoSubsystem.mdo");
MDClasses.writeObject(subsystemMdo, subsystem);
System.out.println("Written: " + subsystemMdo.toAbsolutePath());
var readSub = new EDTReader(subsystemMdo, MDCReadSettings.SKIP_SUPPORT).read(subsystemMdo);
ok &= checkReadBack("Subsystem", readSub instanceof Subsystem s ? s.getName() : null, subsystem.getName());

var catalogMdo = edtSrc.resolve("Catalogs").resolve("DemoCatalog").resolve("DemoCatalog.mdo");
MDClasses.writeObject(catalogMdo, catalog);
System.out.println("Written: " + catalogMdo.toAbsolutePath());
var readCat = new EDTReader(catalogMdo, MDCReadSettings.SKIP_SUPPORT).read(catalogMdo);
ok &= checkReadBack("Catalog", readCat instanceof Catalog c ? c.getName() : null, catalog.getName());

var configMdo = edtSrc.resolve("Configuration").resolve("Configuration.mdo");
MDClasses.writeObject(configMdo, config);
System.out.println("Written: " + configMdo.toAbsolutePath());
var readConfig = MDOReader.readConfiguration(configMdo, MDCReadSettings.SKIP_SUPPORT);
if (readConfig != null && readConfig instanceof Configuration) {
System.out.println(" Read back Configuration: " + readConfig.getClass().getSimpleName());
} else {
System.out.println(" ERROR: read back Configuration failed");
ok = false;
}

// --- Конфигуратор (Designer .xml) — каталог designer/ ---
System.out.println("=== Designer (designer/) ===");
var subsystemXml = designerSrc.resolve("Subsystems").resolve("DemoSubsystem.xml");
MDClasses.writeObject(subsystemXml, subsystem);
System.out.println("Written: " + subsystemXml.toAbsolutePath());
ok &= Files.exists(subsystemXml) && Files.size(subsystemXml) > 0;

var catalogXml = designerSrc.resolve("Catalogs").resolve("DemoCatalog.xml");
MDClasses.writeObject(catalogXml, catalog);
System.out.println("Written: " + catalogXml.toAbsolutePath());
ok &= Files.exists(catalogXml) && Files.size(catalogXml) > 0;

var configXml = designerSrc.resolve("Configuration.xml");
MDClasses.writeObject(configXml, config);
System.out.println("Written: " + configXml.toAbsolutePath());
ok &= Files.exists(configXml) && Files.size(configXml) > 0;

if (ok) {
System.out.println("OK: all objects written to edt/ and designer/ (Subsystem, Catalog, Configuration).");
} else {
System.exit(1);
}
}

private static boolean checkReadBack(String type, String readName, String expectedName) {
if (readName != null && readName.equals(expectedName)) {
System.out.println(" Read back " + type + ": " + readName);
return true;
}
System.out.println(" ERROR: read back " + type + " failed (expected " + expectedName + ", got " + readName + ")");
return false;
}
}
Loading
Loading