跳到正文

ADR-0025:离线声明式模板 ​

问题 ​

现有 DocumentTemplate 已有变量、布尔条件、词法循环和动态表格行,但调用方必须在 Java 中构建全部节点。结构与数据处理混在一起,业务人员难以查看报告结构。直接接入通用表达式、HTML 或外部资源解析又会引入不受控执行和静默损失。

决策 ​

新增实际承担解析职责的 export4j-adapter-template,仅单向依赖模型模块。采用自主定义的版本 1 XML 子集,使用 JDK StAX 解析;编译返回既有 DocumentTemplate,展开后仍为同一语义 Document。布局、字体和后端不进入解析器,也不复制模板绑定引擎。

首版支持段落、标题、无序列表、分页、矩形语义表格、富文本 span、段落/单元格行内图片,以及 if、each、rows。XML 根显式声明版本。元素、属性、结构顺序和变量语法均严格检查,包括未选择的分支。表达式只允许最多 256 个 UTF-16 单元的 ASCII 点路径,不提供调用、运算、反射、索引或递归替换。

模板不声明文件或网络来源。图片引用调用方预先注册的不可变 ImageResource;字体和输出由调用方独立配置。禁止命名空间、命名空间声明、DTD、外部/自定义实体、处理指令及 include。解析器使用 JDK 默认实现并关闭 DTD/外部实体,resolver 始终拒绝。XML 预定义实体、数值实体和有界注释可用。

解析前限制 UTF-16 输入长度,解析时限制元素总数、嵌套深度及解码文本码点。借用 Reader 最多读取输入限额加 1 个 UTF-16 单元,不关闭它。模板、模型和布局仍驻留内存,不将增量 XML 读取称为流式布局。源预算与既有数据/展开预算独立,输出还须通过布局、后端和执行上下文预算。

DocumentTemplate 同批加固追加前预算、Map 键文字计费、标准大数精确类型与规模预检、循环检查点及无递归点路径扫描。词法父链替代每次循环复制根 Map。任意数值对象和 BigInteger/BigDecimal 子类不再作为可信标量接纳;不执行其重载 toString。

诊断与兼容 ​

编译失败返回稳定 TemplateException.Code 和一基行列,不返回部分模板。XML 解析器的错误文本可能带入源文档或 URL,因此对不可信解析失败只暴露标准化拒绝,不将其原始异常挂入诊断链;调用方 Reader 的 IOException 仍原样传播。绑定失败保留既有 BindingException.Code/安全路径,不承诺能将动态展开位置映射回 XML 行列。布局错误继续使用模型 ID 和既有来源上下文。

块 ID 可省略,编译器分配确定性 ID;显式 ID 使用受限 ASCII 语法,全模板唯一。循环展开继续增加 @索引 后缀。图片与字段原文位置由模型重新计算,本语法不暴露引用字段,避免未经定义的循环局部目标重映射。结构位置只接受空白;内联空白逐字保留。

本批没有宽松导入或忽略未知内容模式。Markdown、HTML/CSS、任意 XML、数值列表、链接、最终字段、组件网格、注释、合并语义单元格、include 和模板自定义脚本均不在本语法中。程序化 Java API 保持独立,不能从文本子集推断其全部功能都能声明。

新模块、自研模板样例和代码生成图片采用 Apache-2.0。没有新增外部运行依赖、字体或第三方素材;BOM、SBOM、本地 bundle 和隔离坐标调用方纳入验收。