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)