Files
xsl-validator/src/conf.py
T
info cbcae3222f Feature: s9api-basierte SaxonWorkerPool-Variante für XSLT 2.0/3.0
Die JAXP-basierte SaxonWorkerPool-Implementierung ist nur für XSLT 1.0
vollständig spezifiziert und kann bei XSLT 2.0/3.0 zu fehlerhaften
Ausgaben führen.

Änderungen:
- Neue SaxonWorkerPoolS9Api-Klasse mit Saxon s9api für XSLT 2.0/3.0
- XsltVersion-Enum in conf.py (XSLT_1_0, XSLT_2_0_3_0)
- ComboBox in Performance-Einstellungen zur XSLT-Version-Auswahl
- MainWindow wählt automatisch richtige Worker-Pool-Variante
- Verbesserte Classpath-Behandlung und Fehlerbehandlung

Standard-Einstellung: XSLT 2.0/3.0 (s9api) - empfohlen für moderne Stylesheets
Fallback: XSLT 1.0 (JAXP) - verfügbar für Legacy-Stylesheets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 20:20:00 +01:00

232 lines
7.0 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import sys
from pathlib import Path
from sys import platform
from typing import Tuple, Type
from pydantic import Field
from pydantic_yaml import to_yaml_str
from ruamel.yaml import YAML
from enum import Enum
import logging
from pydantic import BaseModel
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, JsonConfigSettingsSource
logger = logging.getLogger(__name__)
app_name = "DocuMentor"
if platform == "win32":
tmp_config_path = f"%APPDATA%\\{app_name}\\config.json"
elif platform in ("linux", "linux2"):
tmp_config_path = f"~/.config/{app_name}/config.json"
elif platform == "darwin":
tmp_config_path = f"~/Library/Application Support/{app_name}/͏͏͏͏config.json"
else:
tmp_config_path = f"~/.config/{app_name}/config.json"
config_path = Path(os.path.expandvars(tmp_config_path)).expanduser()
class JavaVm(BaseModel):
id: int
version: str
path_to_binary_file: Path
class DiffPdf(BaseModel):
id: int
version: str
path_to_binary_file: Path
default_params: list[str]
output_file_extension: str = "pdf"
class SaxonJar(BaseModel):
id: int
version: str
path_to_jar_file: Path
output_file_extension: str = "fo"
class ApacheFop(BaseModel):
id: int
version: str
path_to_dir: Path
output_file_extension: str = "pdf"
class XslDir(BaseModel):
id: int
name: str
path_to_root_dir: Path
class SSLMode(str, Enum):
DISABLE = "disable"
ALLOW = "allow"
PREFER = "prefer"
REQUIRE = "require"
VERIFY_CA = "verify-ca"
VERIFY_FULL = "verify-full"
class XsltVersion(str, Enum):
"""XSLT-Version für Saxon-Transformationen."""
XSLT_1_0 = "1.0" # JAXP API (nur XSLT 1.0)
XSLT_2_0_3_0 = "2.0/3.0" # s9api (XSLT 2.0 und 3.0)
class PostgreSqlDb(BaseModel):
id: int
name: str
host: str
port: int = 5432
database: str
username: str
password: str
ssl_mode: SSLMode = SSLMode.PREFER
class Project(BaseModel):
id: int = Field(..., description="Eindeutige Projekt-ID", gt=0)
name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255)
project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis")
java_vm_id: int = Field(..., description="ID der Java VM", gt=0)
diff_pdf_id: int = Field(..., description="ID der diff-pdf Konfiguration", gt=0)
saxon_jar_id: int = Field(..., description="ID der Saxon JAR Konfiguration", gt=0)
apache_fop_id: int = Field(..., description="ID der Apache FOP Konfiguration", gt=0)
xsl_dir_id: int = Field(..., description="ID des XSL-Verzeichnisses", gt=0)
postgre_sql_db_id: int = Field(..., description="ID der PostgreSQL Datenbank", gt=0)
fop_config_dir: Path | None = Field(None, description="Optionaler Pfad zum Apache FOP Config-Verzeichnis")
def getXsl(self) -> str:
global app_settings
value = [x.name for x in app_settings.xsl_dirs if x.id == self.xsl_dir_id]
return value[0] if len(value) else ""
def getJavaVm(self) -> str:
global app_settings
value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
return value[0] if len(value) else ""
def getSaxon(self) -> str:
global app_settings
value = [x.version for x in app_settings.saxon_jars if x.id == self.saxon_jar_id]
return value[0] if len(value) else ""
def getApacheFop(self) -> str:
global app_settings
value = [x.version for x in app_settings.apache_fops if x.id == self.apache_fop_id]
return value[0] if len(value) else ""
def getDiffPdf(self) -> str:
global app_settings
value = [x.version for x in app_settings.diff_pdfs if x.id == self.diff_pdf_id]
return value[0] if len(value) else ""
def getPostgreSqlDb(self) -> str:
global app_settings
value = [x.name for x in app_settings.postgresql_dbs if x.id == self.postgre_sql_db_id]
return value[0] if len(value) else ""
class AppSettings(BaseSettings):
java_vms: list[JavaVm] = []
diff_pdfs: list[DiffPdf] = []
saxon_jars: list[SaxonJar] = []
apache_fops: list[ApacheFop] = []
xsl_dirs: list[XslDir] = []
pdf_projects: list[Project] = []
postgresql_dbs: list[PostgreSqlDb] = []
theme: str | None = None
max_workers: int = 8 # Anzahl paralleler Worker für Transformationen (Standard: 8)
use_saxon_worker_pool: bool = True # SaxonWorkerPool aktivieren (schneller, benötigt JDK)
saxon_xslt_version: XsltVersion = XsltVersion.XSLT_2_0_3_0 # XSLT-Version für Saxon (Standard: 2.0/3.0 mit s9api)
use_fop_worker_pool: bool = True # FopWorkerPool aktivieren (schneller, benötigt JDK)
# UI-Zustand
window_geometry: tuple[int, int, int, int] | None = None # (x, y, width, height)
splitter_sizes: list[int] | None = None # Splitter-Positionen
tree_column_widths: list[int] | None = None # TreeWidget-Spaltenbreiten
model_config = SettingsConfigDict(json_file=config_path)
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (JsonConfigSettingsSource(settings_cls),)
def save(self):
global config_path
# Ordner existert nicht
if not config_path.parent.exists():
config_path.parent.mkdir(parents=True, exist_ok=True)
if not config_path.parent.is_dir() or not os.access(config_path.parent, os.W_OK):
logger.exception(f"{config_path.parent} ist kein Verzeichnis oder es gibt keine Schreibrechte")
sys.exit(1)
# Konfiguration speichern
with open(config_path, "wb") as c:
c.write(app_settings.model_dump_json(indent=4).encode())
app_settings = AppSettings()
class XmlFile(BaseModel):
xml: Path
# blake2b hashsum
hashsum: str | None = None
class XslFile(BaseModel):
id: tuple
bez: str
xsl_file: Path
xslt_params: dict[str, str] = {}
xmls: list[XmlFile] = []
class TreeNode(BaseModel):
id: tuple
bez: str
xslt_params: dict[str, str] = {}
children: list["TreeNode|XslFile"]
class ProjectData(BaseModel):
"""
Speichert die Projekteinstellungen direkt im Projektordner in einer .yaml-Datei.
"""
nodes: list[TreeNode] = []
@classmethod
def readSettings(cls, project_dir: Path):
# Explizit UTF-8 Encoding verwenden
project_yaml_path = project_dir / "project.yaml"
with open(project_yaml_path, "r", encoding="utf-8") as f:
yaml = YAML(typ="safe")
yaml_data = yaml.load(f)
return cls.model_validate(yaml_data)
def writeSettings(self, project_dir: Path):
with open(project_dir / "project.yaml", "w", encoding="utf8") as f:
f.write(to_yaml_str(self))