from __future__ import annotations import json from dataclasses import dataclass, field from sapcli.utils.xml_utils import ddl_escape as _ddl_escape from sapcli.utils.xml_utils import xml_escape as _xml_escape @dataclass class DomainDefinition: datatype: str = "CHAR" length: int = 10 decimals: int = 0 lowercase: bool = False fix_values: list[dict[str, str]] = field(default_factory=list) def to_xml(self, name: str, description: str, package: str = "$TMP") -> str: ns = ( 'xmlns:doma="http://www.sap.com/dictionary/domain"' ' xmlns:adtcore="http://www.sap.com/adt/core"' ) header = ( f'' f'' ) type_info = ( "" "" f"{self.datatype}" f"{self.length:06d}" f"{self.decimals:06d}" "" "" f"{self.length:06d}" "00" "" f"false" f"{'true' if self.lowercase else 'false'}" "false" "" ) fix_xml = "" if self.fix_values: fix_xml = "false" for i, fv in enumerate(self.fix_values, 1): low = fv.get("low", "") high = fv.get("high", "") text = fv.get("text", "") fix_xml += ( f"" f"{i:04d}" f"{low}" f"{high}" f"{text}" f"" ) fix_xml += "" return f"{header}{type_info}{fix_xml}" @dataclass class DataElementDefinition: datatype: str = "CHAR" length: int = 10 decimals: int = 0 domain_name: str = "" def to_xml(self, name: str, description: str, package: str = "$TMP") -> str: ns = ( 'xmlns:blue="http://www.sap.com/wbobj/dictionary/dtel"' ' xmlns:adtcore="http://www.sap.com/adt/core"' ) header = ( f'' f'' ) type_block = ( '' ) if self.domain_name: type_block += ( f"domain" f"{self.domain_name}" "" "" "" ) else: type_block += ( "predefinedAbapType" "" f"{self.datatype}" f"{self.length:06d}" f"{self.decimals:06d}" ) type_block += ( "" "00" "10" "" "00" "20" "" "00" "40" "" "00" "55" "" "" "" "" "false" "false" "false" "false" "" ) return f"{header}{type_block}" @dataclass class TableField: name: str type_name: str is_key: bool = False not_null: bool = False def to_ddl(self) -> str: parts = [f" {self.name:40s}: {self.type_name}"] if self.not_null: parts.append("not null") return " ".join(parts) + ";" @dataclass class TableDefinition: fields: list[TableField] = field(default_factory=list) label: str = "" enhancement_category: str = "#NOT_CLASSIFIED" delivery_class: str = "#A" data_maintenance: str = "#LIMITED" table_category: str = "#TRANSPARENT" def to_ddl(self, name: str, description: str) -> str: lines = [] lines.append(f"@EndUserText.label : '{_ddl_escape(description)}'") lines.append(f"@AbapCatalog.enhancementCategory : {self.enhancement_category}") lines.append(f"@AbapCatalog.tableCategory : {self.table_category}") lines.append(f"@AbapCatalog.deliveryClass : {self.delivery_class}") lines.append(f"@AbapCatalog.dataMaintenance : {self.data_maintenance}") lines.append(f"define table {name.lower()} {{") for f in self.fields: lines.append(f.to_ddl()) lines.append("}") return "\n".join(lines) @dataclass class StructureDefinition: fields: list[TableField] = field(default_factory=list) enhancement_category: str = "#NOT_CLASSIFIED" def to_ddl(self, name: str, description: str) -> str: lines = [] lines.append(f"@EndUserText.label : '{_ddl_escape(description)}'") lines.append(f"@AbapCatalog.enhancementCategory : {self.enhancement_category}") lines.append(f"define structure {name.lower()} {{") for f in self.fields: lines.append(f.to_ddl()) lines.append("}") return "\n".join(lines) @dataclass class TableTypeDefinition: line_type: str = "" key_type: str = "#USER_DEFINED" access_mode: str = "#STANDARD" key_fields: list[str] = field(default_factory=list) def to_xml(self, name: str, description: str, package: str = "$TMP") -> str: ns = ( 'xmlns:ttyp="http://www.sap.com/adt/dictionary/tabletypes"' ' xmlns:adtcore="http://www.sap.com/adt/core"' ) header = ( f'' f'' ) body = ( "" "" f"{self.access_mode}" "" "" f"{self.key_type}" "" "" f"structured" f"{self.line_type}" "" "" ) return f"{header}{body}" def load_definition_from_json(path: str, obj_type: str) -> str | None: with open(path, "r", encoding="utf-8") as f: data = json.load(f) return build_definition(data, obj_type) def build_definition(data: dict, obj_type: str) -> str | None: if obj_type == "domain": return _build_domain(data) elif obj_type == "dataelement": return _build_dataelement(data) elif obj_type in ("table", "structure"): return None elif obj_type == "tabletype": return _build_tabletype(data) return None def _build_domain(data: dict) -> str: defn = DomainDefinition( datatype=data.get("datatype", "CHAR"), length=data.get("length", 10), decimals=data.get("decimals", 0), lowercase=data.get("lowercase", False), fix_values=data.get("fix_values", []), ) name = data.get("name", "ZDOMAIN") description = data.get("description", name) package = data.get("package", "$TMP") return defn.to_xml(name, description, package) def _build_dataelement(data: dict) -> str: defn = DataElementDefinition( datatype=data.get("datatype", "CHAR"), length=data.get("length", 10), decimals=data.get("decimals", 0), domain_name=data.get("domain_name", ""), ) name = data.get("name", "ZDATAELEMENT") description = data.get("description", name) package = data.get("package", "$TMP") return defn.to_xml(name, description, package) def _build_tabletype(data: dict) -> str: defn = TableTypeDefinition( line_type=data.get("line_type", ""), key_type=data.get("key_type", "#USER_DEFINED"), access_mode=data.get("access_mode", "#STANDARD"), key_fields=data.get("key_fields", []), ) name = data.get("name", "ZTABLETYPE") description = data.get("description", name) package = data.get("package", "$TMP") return defn.to_xml(name, description, package)