Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <olegtymko@yandex.ru>, Maximov Valery <maximovvalery@gmail.com> 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);
}

/**
* Записывает объект метаданных в файл с настройками записи.
*
* @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 <olegtymko@yandex.ru>, Maximov Valery <maximovvalery@gmail.com> 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

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 <olegtymko@yandex.ru>, Maximov Valery <maximovvalery@gmail.com> 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