update
This commit is contained in:
@@ -4,7 +4,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'com.dmiki'
|
group 'com.dmiki'
|
||||||
version '1.0.0'
|
version '1.1.0'
|
||||||
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ MetaPlugin 是一个 IntelliJ IDEA 插件,用于把 XSD 结构转换为可配
|
|||||||
当前项目中的插件信息:
|
当前项目中的插件信息:
|
||||||
- 插件名:`MetaPlugin`
|
- 插件名:`MetaPlugin`
|
||||||
- 插件 ID:`com.dmiki.metaplugin`
|
- 插件 ID:`com.dmiki.metaplugin`
|
||||||
- 当前版本:`1.0.0`
|
- 当前版本:`1.1.0`
|
||||||
- IDE 兼容下限:`sinceBuild = 193`(IntelliJ IDEA 2019.3+)
|
- IDE 兼容下限:`sinceBuild = 193`(IntelliJ IDEA 2019.3+)
|
||||||
|
|
||||||
## 2. 核心能力
|
## 2. 核心能力
|
||||||
@@ -46,7 +46,7 @@ MetaPlugin 是一个 IntelliJ IDEA 插件,用于把 XSD 结构转换为可配
|
|||||||
|
|
||||||
打包产物位于:
|
打包产物位于:
|
||||||
|
|
||||||
- `build/distributions/MetaPlugin-1.0.0.zip`
|
- `build/distributions/MetaPlugin-1.1.0.zip`
|
||||||
|
|
||||||
在 IDEA 中通过 `Settings -> Plugins -> Install Plugin from Disk...` 选择该 ZIP 安装。
|
在 IDEA 中通过 `Settings -> Plugins -> Install Plugin from Disk...` 选择该 ZIP 安装。
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ MetaPlugin 是一个 IntelliJ IDEA 插件,用于把 XSD 结构转换为可配
|
|||||||
- 包名:默认 `com.example.message`。
|
- 包名:默认 `com.example.message`。
|
||||||
- 注意这里填写的是“包名”,最终 `resultMap.type` 会自动拼接为:`包名 + 报文类型大写格式`。
|
- 注意这里填写的是“包名”,最终 `resultMap.type` 会自动拼接为:`包名 + 报文类型大写格式`。
|
||||||
- 例如 `hvps.101.001.01` 会转为 `HVPS_101_001_01`。
|
- 例如 `hvps.101.001.01` 会转为 `HVPS_101_001_01`。
|
||||||
- 输出目录:支持绝对路径或相对项目根目录路径。
|
- 输出目录:支持绝对路径或相对模块根目录路径。
|
||||||
- 自定义属性:全局默认值,默认 `sign`。
|
- 自定义属性:全局默认值,默认 `sign`。
|
||||||
- 预处理属性:全局默认值,默认空。
|
- 预处理属性:全局默认值,默认空。
|
||||||
|
|
||||||
@@ -147,11 +147,9 @@ MetaPlugin 是一个 IntelliJ IDEA 插件,用于把 XSD 结构转换为可配
|
|||||||
|
|
||||||
### 8.3 输出目录与文件名
|
### 8.3 输出目录与文件名
|
||||||
|
|
||||||
- 默认输出目录:`src/main/resources` 下第一个不存在的目录:
|
- 默认输出目录分两种情况:
|
||||||
- `msgmapper`
|
- 如果 XSD 位于某个 `resources` 目录下,则输出到该 `resources` 目录下第一个不存在的 `msgmapperN`
|
||||||
- `msgmapper1`
|
- 否则输出到 `XSD 父目录` 的同级第一个不存在的 `msgmapperN`
|
||||||
- `msgmapper2`
|
|
||||||
- ... 依次类推
|
|
||||||
- 默认输出文件名:`报文类型.xml`。
|
- 默认输出文件名:`报文类型.xml`。
|
||||||
- 右键“生成选中节点XOM文件”时,默认文件名:`报文类型_节点名.xml`。
|
- 右键“生成选中节点XOM文件”时,默认文件名:`报文类型_节点名.xml`。
|
||||||
|
|
||||||
|
|||||||
218
docs/hvps.101.001.01.xsd
Normal file
218
docs/hvps.101.001.01.xsd
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns="urn:cnaps:std:hvps:2010:tech:xsd:hvps.111.001.01"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
targetNamespace="urn:cnaps:std:hvps:2010:tech:xsd:hvps.111.001.01"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
|
||||||
|
<!-- 根节点 Document -->
|
||||||
|
<xs:element name="Document" type="Document"/>
|
||||||
|
|
||||||
|
<!-- 1. 文档结构 -->
|
||||||
|
<xs:complexType name="Document">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="FIToFICstmrCdtTrf" type="FIToFICstmrCdtTrf"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<!-- 2. 金融机构间客户汇兑主体 (大额汇兑) -->
|
||||||
|
<xs:complexType name="FIToFICstmrCdtTrf">
|
||||||
|
<xs:sequence>
|
||||||
|
<!-- 业务公共报头 -->
|
||||||
|
<xs:element name="GrpHdr" type="GroupHeader"/>
|
||||||
|
<!-- 资金交易明细 (大额报文通常只有一笔明细) -->
|
||||||
|
<xs:element name="CdtTrfTxInf" type="CreditTransferTransactionInformation" maxOccurs="unbounded"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<!-- 3. 业务头 (Group Header) -->
|
||||||
|
<xs:complexType name="GroupHeader">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="MsgId" type="Max35Text"/> <!-- 报文标识号 -->
|
||||||
|
<xs:element name="CreDtTm" type="ISODateTime"/> <!-- 报文创建时间 -->
|
||||||
|
<xs:element name="InstgPty" type="InstructingParty"/> <!-- 发起参与机构 -->
|
||||||
|
<xs:element name="InstdPty" type="InstructedParty"/> <!-- 接收参与机构 -->
|
||||||
|
<xs:element name="SysCd" type="SystemCode"/> <!-- 系统代码 (如 HVPS) -->
|
||||||
|
<xs:element name="Rmk" type="Max256Text" minOccurs="0"/> <!-- 备注 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<!-- 4. 汇兑交易信息 (Transaction Information) -->
|
||||||
|
<xs:complexType name="CreditTransferTransactionInformation">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="PmtId" type="PaymentIdentification"/> <!-- 支付标识 -->
|
||||||
|
<xs:element name="PmtTpInf" type="PaymentTypeInformation" minOccurs="0"/> <!-- 支付类型信息 (业务种类) -->
|
||||||
|
<xs:element name="IntrBkSttlmAmt" type="ActiveCurrencyAndAmount"/> <!-- 银行间结算金额 -->
|
||||||
|
<xs:element name="Dbtr" type="PartyIdentification"/> <!-- 付款人 -->
|
||||||
|
<xs:element name="DbtrAcct" type="CashAccount" minOccurs="0"/> <!-- 付款人账户 -->
|
||||||
|
<xs:element name="DbtrAgt" type="BranchAndFinancialInstitutionIdentification"/> <!-- 付款行 -->
|
||||||
|
<xs:element name="CdtrAgt" type="BranchAndFinancialInstitutionIdentification"/> <!-- 收款行 -->
|
||||||
|
<xs:element name="Cdtr" type="PartyIdentification"/> <!-- 收款人 -->
|
||||||
|
<xs:element name="CdtrAcct" type="CashAccount"/> <!-- 收款人账户 -->
|
||||||
|
<xs:element name="Purp" type="Purpose" minOccurs="0"/> <!-- 用途/附言 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<!-- ==== 以下为子组件装配定义 ==== -->
|
||||||
|
<xs:complexType name="PaymentIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="EndToEndId" type="Max35Text"/> <!-- 端到端标识号 -->
|
||||||
|
<xs:element name="TxId" type="Max35Text"/> <!-- 交易标识号 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="InstructingParty">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="InstgDrctPty" type="Max14Text"/> <!-- 发起直接参与机构 (14位行号) -->
|
||||||
|
<xs:element name="InstgPty" type="Max14Text" minOccurs="0"/> <!-- 发起间接参与机构 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="InstructedParty">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="InstdDrctPty" type="Max14Text"/> <!-- 接收直接参与机构 (14位行号) -->
|
||||||
|
<xs:element name="InstdPty" type="Max14Text" minOccurs="0"/> <!-- 接收间接参与机构 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="PartyIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Nm" type="Max60Text"/> <!-- 户名 -->
|
||||||
|
<xs:element name="PstlAdr" type="PostalAddress" minOccurs="0"/> <!-- 地址 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="CashAccount">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Id" type="AccountIdentification"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="AccountIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Othr" type="GenericAccountIdentification"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="GenericAccountIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Id" type="Max32Text"/> <!-- 账号 (一般最长32位) -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="BranchAndFinancialInstitutionIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="FinInstnId" type="FinancialInstitutionIdentification"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="FinancialInstitutionIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="ClrSysMmbId" type="ClearingSystemMemberIdentification"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="ClearingSystemMemberIdentification">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="MmbId" type="Max14Text"/> <!-- CNAPS 14位支付行号 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="PaymentTypeInformation">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="CtgyPurp" type="CategoryPurpose"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="CategoryPurpose">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Prtry" type="Max5Text"/> <!-- 二代支付中业务种类为最多5位数字,如大额A100 -->
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="Purpose">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Prtry" type="Max256Text"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="PostalAddress">
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="AdrLine" type="Max256Text" minOccurs="0" maxOccurs="2"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<!-- ==== 以下为基本数据类型 (Simple Types) ==== -->
|
||||||
|
<xs:simpleType name="Max35Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:maxLength value="35"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="Max32Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:maxLength value="32"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="Max14Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:length value="14"/> <!-- 人行行号通常定长14位 -->
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="Max5Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:maxLength value="5"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="Max60Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:maxLength value="60"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="Max256Text">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:minLength value="1"/>
|
||||||
|
<xs:maxLength value="256"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="SystemCode">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:enumeration value="HVPS"/> <!-- 单笔大额 -->
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="ISODateTime">
|
||||||
|
<xs:restriction base="xs:dateTime"/>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<!-- 人民币金额类型定义 -->
|
||||||
|
<xs:complexType name="ActiveCurrencyAndAmount">
|
||||||
|
<xs:simpleContent>
|
||||||
|
<xs:extension base="ActiveCurrencyAndAmount_SimpleType">
|
||||||
|
<xs:attribute name="Ccy" type="ActiveCurrencyCode" use="required"/>
|
||||||
|
</xs:extension>
|
||||||
|
</xs:simpleContent>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:simpleType name="ActiveCurrencyAndAmount_SimpleType">
|
||||||
|
<xs:restriction base="xs:decimal">
|
||||||
|
<xs:fractionDigits value="2"/>
|
||||||
|
<xs:totalDigits value="18"/>
|
||||||
|
<xs:minInclusive value="0.01"/>
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
|
||||||
|
<xs:simpleType name="ActiveCurrencyCode">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:pattern value="[A-Z]{3,3}"/> <!-- 绝大多数情况为 CNY -->
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType>
|
||||||
|
</xs:schema>
|
||||||
@@ -65,16 +65,18 @@ public class XomGenerationService {
|
|||||||
|
|
||||||
private String resolveOutputPath(XomGenerateOptions options) throws XomGenerationException {
|
private String resolveOutputPath(XomGenerateOptions options) throws XomGenerationException {
|
||||||
String outputPath = trimToNull(options.getOutputPath());
|
String outputPath = trimToNull(options.getOutputPath());
|
||||||
|
File xsdFile = resolveXsdFile(options);
|
||||||
|
File moduleBaseDir = XomPathStrategy.resolveModuleBaseDir(options.getProjectBasePath(), xsdFile);
|
||||||
if (outputPath != null) {
|
if (outputPath != null) {
|
||||||
File outputFile = resolveOutputFile(outputPath, options);
|
File outputFile = resolveOutputFile(outputPath, moduleBaseDir);
|
||||||
if (outputFile.isDirectory()) {
|
if (outputFile.isDirectory()) {
|
||||||
return new File(outputFile, safeName(options.getSchemaId()) + ".xml").getAbsolutePath();
|
return new File(outputFile, safeName(options.getSchemaId()) + ".xml").getAbsolutePath();
|
||||||
}
|
}
|
||||||
return outputFile.getAbsolutePath();
|
return outputFile.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
File resourcesDir = resolveResourcesDirectory(options);
|
File targetDirectory = XomPathStrategy.resolveNextMsgMapperDirectory(
|
||||||
File targetDirectory = resolveNextMsgMapperDirectory(resourcesDir);
|
XomPathStrategy.resolveDefaultOutputBaseDir(xsdFile));
|
||||||
String schemaId = trimToNull(options.getSchemaId());
|
String schemaId = trimToNull(options.getSchemaId());
|
||||||
if (schemaId == null) {
|
if (schemaId == null) {
|
||||||
schemaId = nameFromXsd(options);
|
schemaId = nameFromXsd(options);
|
||||||
@@ -82,46 +84,16 @@ public class XomGenerationService {
|
|||||||
return new File(targetDirectory, safeName(schemaId) + ".xml").getAbsolutePath();
|
return new File(targetDirectory, safeName(schemaId) + ".xml").getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private File resolveOutputFile(String outputPath, XomGenerateOptions options) {
|
private File resolveOutputFile(String outputPath, File moduleBaseDir) {
|
||||||
File outputFile = new File(outputPath);
|
return XomPathStrategy.resolvePath(outputPath, moduleBaseDir);
|
||||||
if (outputFile.isAbsolute()) {
|
|
||||||
return outputFile;
|
|
||||||
}
|
|
||||||
String projectBasePath = trimToNull(options.getProjectBasePath());
|
|
||||||
if (projectBasePath == null) {
|
|
||||||
return outputFile;
|
|
||||||
}
|
|
||||||
return new File(projectBasePath, outputPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private File resolveResourcesDirectory(XomGenerateOptions options) throws XomGenerationException {
|
private File resolveXsdFile(XomGenerateOptions options) throws XomGenerationException {
|
||||||
String projectBasePath = trimToNull(options.getProjectBasePath());
|
|
||||||
if (projectBasePath != null) {
|
|
||||||
return new File(projectBasePath, "src/main/resources");
|
|
||||||
}
|
|
||||||
|
|
||||||
String xsdPath = trimToNull(options.getXsdPath());
|
String xsdPath = trimToNull(options.getXsdPath());
|
||||||
if (xsdPath == null) {
|
if (xsdPath == null) {
|
||||||
throw new XomGenerationException("XOM-001", "无法推导输出路径,xsdPath为空");
|
throw new XomGenerationException("XOM-001", "无法推导输出路径,xsdPath为空");
|
||||||
}
|
}
|
||||||
File xsdFile = new File(xsdPath);
|
return new File(xsdPath);
|
||||||
File parent = xsdFile.getParentFile();
|
|
||||||
if (parent == null) {
|
|
||||||
throw new XomGenerationException("XOM-001", "无法推导输出路径,XSD父目录为空");
|
|
||||||
}
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File resolveNextMsgMapperDirectory(File resourcesDir) {
|
|
||||||
int suffix = 0;
|
|
||||||
while (true) {
|
|
||||||
String name = suffix == 0 ? "msgmapper" : "msgmapper" + suffix;
|
|
||||||
File candidate = new File(resourcesDir, name);
|
|
||||||
if (!candidate.exists()) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
suffix++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String nameFromXsd(XomGenerateOptions options) {
|
private String nameFromXsd(XomGenerateOptions options) {
|
||||||
|
|||||||
180
src/main/java/com/dmiki/metaplugin/xom/XomPathStrategy.java
Normal file
180
src/main/java/com/dmiki/metaplugin/xom/XomPathStrategy.java
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package com.dmiki.metaplugin.xom;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public final class XomPathStrategy {
|
||||||
|
private static final String[] MODULE_MARKERS = {
|
||||||
|
"build.gradle",
|
||||||
|
"build.gradle.kts",
|
||||||
|
"pom.xml",
|
||||||
|
".idea"
|
||||||
|
};
|
||||||
|
|
||||||
|
private XomPathStrategy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File resolveModuleBaseDir(String basePath, File anchorFile) {
|
||||||
|
String normalizedBasePath = trimToNull(basePath);
|
||||||
|
if (normalizedBasePath != null) {
|
||||||
|
return new File(normalizedBasePath);
|
||||||
|
}
|
||||||
|
return inferModuleBaseDir(anchorFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File inferModuleBaseDir(File anchorFile) {
|
||||||
|
File current = toDirectory(anchorFile);
|
||||||
|
while (current != null) {
|
||||||
|
if (containsModuleMarker(current)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current.getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
File srcDir = findAncestorByName(anchorFile, "src");
|
||||||
|
if (srcDir != null && srcDir.getParentFile() != null) {
|
||||||
|
return srcDir.getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
File anchorDir = toDirectory(anchorFile);
|
||||||
|
if (anchorDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File parent = anchorDir.getParentFile();
|
||||||
|
return parent == null ? anchorDir : parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File resolveDefaultOutputBaseDir(File xsdFile) {
|
||||||
|
File resourcesDir = findAncestorByName(xsdFile, "resources");
|
||||||
|
if (resourcesDir != null) {
|
||||||
|
return resourcesDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
File xsdParent = xsdFile == null ? null : xsdFile.getParentFile();
|
||||||
|
if (xsdParent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File siblingBase = xsdParent.getParentFile();
|
||||||
|
return siblingBase == null ? xsdParent : siblingBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File resolveNextMsgMapperDirectory(File baseDir) {
|
||||||
|
if (baseDir == null) {
|
||||||
|
return new File("msgmapper");
|
||||||
|
}
|
||||||
|
int suffix = 0;
|
||||||
|
while (true) {
|
||||||
|
String dirName = suffix == 0 ? "msgmapper" : "msgmapper" + suffix;
|
||||||
|
File candidate = new File(baseDir, dirName);
|
||||||
|
if (!candidate.exists()) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
suffix++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File resolvePath(String path, File baseDir) {
|
||||||
|
String normalizedPath = trimToNull(path);
|
||||||
|
if (normalizedPath == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File candidate = new File(normalizedPath);
|
||||||
|
if (candidate.isAbsolute() || baseDir == null) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
return new File(baseDir, normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toRelativePath(File baseDir, File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (baseDir == null) {
|
||||||
|
return normalizeSeparators(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Path normalizedBase = baseDir.toPath().toAbsolutePath().normalize();
|
||||||
|
Path normalizedTarget = file.toPath().toAbsolutePath().normalize();
|
||||||
|
if (!normalizedTarget.startsWith(normalizedBase)) {
|
||||||
|
return normalizeSeparators(normalizedTarget.toString());
|
||||||
|
}
|
||||||
|
String relative = normalizedBase.relativize(normalizedTarget).toString();
|
||||||
|
return relative.isEmpty() ? "." : normalizeSeparators(relative);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return normalizeSeparators(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String resolveRenderedXsdPath(File xsdFile, File moduleBaseDir) {
|
||||||
|
if (xsdFile == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
File resourcesDir = findAncestorByName(xsdFile, "resources");
|
||||||
|
File relativeBase = resourcesDir != null ? resourcesDir : moduleBaseDir;
|
||||||
|
if (relativeBase == null) {
|
||||||
|
return normalizeSeparators(xsdFile.getPath());
|
||||||
|
}
|
||||||
|
return toRelativePath(relativeBase, xsdFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File findAncestorByName(File node, String directoryName) {
|
||||||
|
String normalizedName = trimToNull(directoryName);
|
||||||
|
if (normalizedName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
File current = toDirectory(node);
|
||||||
|
String targetName = normalizedName.toLowerCase(Locale.ROOT);
|
||||||
|
while (current != null) {
|
||||||
|
if (targetName.equals(current.getName().toLowerCase(Locale.ROOT))) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
current = current.getParentFile();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isXmlFilePath(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file.getName().toLowerCase(Locale.ROOT).endsWith(".xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File toDirectory(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
return file.getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsModuleMarker(File directory) {
|
||||||
|
for (String marker : MODULE_MARKERS) {
|
||||||
|
if (new File(directory, marker).exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeSeparators(String path) {
|
||||||
|
if (path == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return path.replace(File.separatorChar, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trimToNull(String text) {
|
||||||
|
if (text == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = text.trim();
|
||||||
|
if (trimmed.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,6 @@ import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
public class XomXmlRenderer {
|
public class XomXmlRenderer {
|
||||||
|
|
||||||
@@ -250,22 +248,8 @@ public class XomXmlRenderer {
|
|||||||
if (rawXsdPath == null) {
|
if (rawXsdPath == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
String projectBasePath = trimToNull(options.getProjectBasePath());
|
File xsdFile = new File(rawXsdPath);
|
||||||
if (projectBasePath == null) {
|
File moduleBaseDir = XomPathStrategy.resolveModuleBaseDir(options.getProjectBasePath(), xsdFile);
|
||||||
return rawXsdPath;
|
return XomPathStrategy.resolveRenderedXsdPath(xsdFile, moduleBaseDir);
|
||||||
}
|
|
||||||
try {
|
|
||||||
Path resourcePath = Paths.get(projectBasePath, "src", "main", "resources")
|
|
||||||
.toAbsolutePath()
|
|
||||||
.normalize();
|
|
||||||
Path xsdPath = Paths.get(rawXsdPath).toAbsolutePath().normalize();
|
|
||||||
String relative = resourcePath.relativize(xsdPath).toString();
|
|
||||||
if (relative.isEmpty()) {
|
|
||||||
return rawXsdPath;
|
|
||||||
}
|
|
||||||
return relative.replace(File.separatorChar, '/');
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return rawXsdPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.dmiki.metaplugin.xom.reverse;
|
package com.dmiki.metaplugin.xom.reverse;
|
||||||
|
|
||||||
|
import com.dmiki.metaplugin.xom.XomPathStrategy;
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
||||||
import com.dmiki.metaplugin.xsd.parser.XsdSchemaParser;
|
import com.dmiki.metaplugin.xsd.parser.XsdSchemaParser;
|
||||||
@@ -14,7 +15,6 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ReservedXomReverseDisplayService implements XomReverseDisplayService {
|
public class ReservedXomReverseDisplayService implements XomReverseDisplayService {
|
||||||
@@ -29,14 +29,32 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
prepareForReplay(root);
|
prepareForReplay(root);
|
||||||
applyReplay(root, mappingDocument.getResults());
|
applyReplay(root, mappingDocument.getResults());
|
||||||
|
|
||||||
return new XomReverseDisplayResult(
|
return buildDisplayResult(root, xsdFile, mappingDocument);
|
||||||
root,
|
}
|
||||||
xsdFile,
|
|
||||||
mappingDocument.getSchemaId(),
|
@Override
|
||||||
mappingDocument.getResultMapType(),
|
public XomReverseDisplayResult applyToCurrentTree(File mappingXmlFile,
|
||||||
inferTopInput(mappingDocument.getCustomAttributeValues()),
|
String projectBasePath,
|
||||||
inferTopInput(mappingDocument.getPreProcessorValues())
|
XsdConfigItem currentRoot,
|
||||||
);
|
XsdConfigItem targetNode) throws Exception {
|
||||||
|
if (currentRoot == null) {
|
||||||
|
throw new IllegalArgumentException("当前UI未加载XSD结构");
|
||||||
|
}
|
||||||
|
if (targetNode == null) {
|
||||||
|
throw new IllegalArgumentException("未选择要应用XOM的节点");
|
||||||
|
}
|
||||||
|
|
||||||
|
MappingDocument mappingDocument = parseMappingDocument(mappingXmlFile);
|
||||||
|
File xsdFile = resolveXsdFile(mappingDocument.getXsdPath(), mappingXmlFile, projectBasePath);
|
||||||
|
|
||||||
|
List<XsdConfigItem> targetPath = resolvePath(currentRoot, targetNode);
|
||||||
|
if (targetPath == null) {
|
||||||
|
throw new IllegalArgumentException("选中节点不属于当前XSD结构");
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareForReplay(targetNode);
|
||||||
|
applyReplay(targetNode, resolveReplayPool(mappingDocument.getResults(), targetPath));
|
||||||
|
return buildDisplayResult(currentRoot, xsdFile, mappingDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MappingDocument parseMappingDocument(File mappingXmlFile) throws Exception {
|
private MappingDocument parseMappingDocument(File mappingXmlFile) throws Exception {
|
||||||
@@ -76,6 +94,7 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item.setConfigMode(item.isEffectiveLeaf() ? XsdConfigMode.IGNORE : XsdConfigMode.FLATTEN_AND_IGNORE_CHILDREN);
|
item.setConfigMode(item.isEffectiveLeaf() ? XsdConfigMode.IGNORE : XsdConfigMode.FLATTEN_AND_IGNORE_CHILDREN);
|
||||||
|
item.setJavaProperty(null);
|
||||||
item.setCustomAttributeEnabled(false);
|
item.setCustomAttributeEnabled(false);
|
||||||
item.setCustomAttributeValue(null);
|
item.setCustomAttributeValue(null);
|
||||||
item.setPreProcessorEnabled(false);
|
item.setPreProcessorEnabled(false);
|
||||||
@@ -85,13 +104,24 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private XomReverseDisplayResult buildDisplayResult(XsdConfigItem root, File xsdFile, MappingDocument mappingDocument) {
|
||||||
|
return new XomReverseDisplayResult(
|
||||||
|
root,
|
||||||
|
xsdFile,
|
||||||
|
mappingDocument.getSchemaId(),
|
||||||
|
mappingDocument.getResultMapType(),
|
||||||
|
inferTopInput(mappingDocument.getCustomAttributeValues()),
|
||||||
|
inferTopInput(mappingDocument.getPreProcessorValues())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void applyReplay(XsdConfigItem root, List<ResultNode> topResults) {
|
private void applyReplay(XsdConfigItem root, List<ResultNode> topResults) {
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ResultNode> pool = new ArrayList<ResultNode>(topResults);
|
List<ResultNode> pool = new ArrayList<ResultNode>(topResults);
|
||||||
ResultNode direct = removeFirstMatch(pool, root);
|
ResultNode direct = removeFirstDirectMatch(pool, root);
|
||||||
if (direct != null) {
|
if (direct != null) {
|
||||||
applyDirectConfig(root, direct);
|
applyDirectConfig(root, direct);
|
||||||
applyChildrenReplay(root, new ArrayList<ResultNode>(direct.getChildren()));
|
applyChildrenReplay(root, new ArrayList<ResultNode>(direct.getChildren()));
|
||||||
@@ -114,7 +144,7 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
|
|
||||||
private void applyChildrenReplay(XsdConfigItem parent, List<ResultNode> pool) {
|
private void applyChildrenReplay(XsdConfigItem parent, List<ResultNode> pool) {
|
||||||
for (XsdConfigItem child : parent.getChildren()) {
|
for (XsdConfigItem child : parent.getChildren()) {
|
||||||
ResultNode direct = removeFirstMatch(pool, child);
|
ResultNode direct = removeFirstDirectMatch(pool, child);
|
||||||
if (direct != null) {
|
if (direct != null) {
|
||||||
applyDirectConfig(child, direct);
|
applyDirectConfig(child, direct);
|
||||||
applyChildrenReplay(child, new ArrayList<ResultNode>(direct.getChildren()));
|
applyChildrenReplay(child, new ArrayList<ResultNode>(direct.getChildren()));
|
||||||
@@ -156,10 +186,10 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultNode removeFirstMatch(List<ResultNode> pool, XsdConfigItem item) {
|
private ResultNode removeFirstDirectMatch(List<ResultNode> pool, XsdConfigItem item) {
|
||||||
for (int i = 0; i < pool.size(); i++) {
|
for (int i = 0; i < pool.size(); i++) {
|
||||||
ResultNode node = pool.get(i);
|
ResultNode node = pool.get(i);
|
||||||
if (matches(item, node)) {
|
if (matchesDirect(item, node)) {
|
||||||
pool.remove(i);
|
pool.remove(i);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -168,6 +198,14 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean matches(XsdConfigItem item, ResultNode node) {
|
private boolean matches(XsdConfigItem item, ResultNode node) {
|
||||||
|
return matches(item, node, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchesDirect(XsdConfigItem item, ResultNode node) {
|
||||||
|
return matches(item, node, item != null && item.isEffectiveLeaf());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matches(XsdConfigItem item, ResultNode node, boolean allowTailMatch) {
|
||||||
if (item == null || node == null) {
|
if (item == null || node == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -183,7 +221,101 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
if (actual == null) {
|
if (actual == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return expected.equals(actual) || expected.equalsIgnoreCase(actual);
|
return matchesName(expected, actual, allowTailMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchesName(String expected, String actual, boolean allowTailMatch) {
|
||||||
|
String normalizedExpected = trimToNull(expected);
|
||||||
|
String normalizedActual = trimToNull(actual);
|
||||||
|
if (normalizedExpected == null || normalizedActual == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizedExpected.equals(normalizedActual) || normalizedExpected.equalsIgnoreCase(normalizedActual)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!allowTailMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int index = normalizedActual.lastIndexOf(':');
|
||||||
|
if (index < 0 || index >= normalizedActual.length() - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String tail = trimToNull(normalizedActual.substring(index + 1));
|
||||||
|
return tail != null && (normalizedExpected.equals(tail) || normalizedExpected.equalsIgnoreCase(tail));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResultNode> resolveReplayPool(List<ResultNode> topResults, List<XsdConfigItem> targetPath) {
|
||||||
|
List<XsdConfigItem> ancestors = new ArrayList<XsdConfigItem>();
|
||||||
|
if (targetPath != null && targetPath.size() > 1) {
|
||||||
|
ancestors.addAll(targetPath.subList(0, targetPath.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ReplayPool> pools = new ArrayList<ReplayPool>();
|
||||||
|
collectReplayPools(topResults, new ArrayList<ResultNode>(), pools);
|
||||||
|
|
||||||
|
ReplayPool best = null;
|
||||||
|
int bestScore = -1;
|
||||||
|
for (ReplayPool pool : pools) {
|
||||||
|
int score = matchAncestorSuffix(ancestors, pool.getAncestorPath());
|
||||||
|
if (score > bestScore) {
|
||||||
|
best = pool;
|
||||||
|
bestScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best == null) {
|
||||||
|
return new ArrayList<ResultNode>();
|
||||||
|
}
|
||||||
|
return new ArrayList<ResultNode>(best.getNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectReplayPools(List<ResultNode> nodes, List<ResultNode> ancestors, List<ReplayPool> pools) {
|
||||||
|
pools.add(new ReplayPool(nodes, ancestors));
|
||||||
|
if (nodes == null || nodes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ResultNode node : nodes) {
|
||||||
|
List<ResultNode> nextAncestors = new ArrayList<ResultNode>(ancestors);
|
||||||
|
nextAncestors.add(node);
|
||||||
|
collectReplayPools(node.getChildren(), nextAncestors, pools);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int matchAncestorSuffix(List<XsdConfigItem> ancestors, List<ResultNode> candidatePath) {
|
||||||
|
if (candidatePath == null || candidatePath.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ancestors == null || candidatePath.size() > ancestors.size()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int offset = ancestors.size() - candidatePath.size();
|
||||||
|
for (int i = 0; i < candidatePath.size(); i++) {
|
||||||
|
if (!matches(ancestors.get(offset + i), candidatePath.get(i))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidatePath.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<XsdConfigItem> resolvePath(XsdConfigItem root, XsdConfigItem targetNode) {
|
||||||
|
List<XsdConfigItem> path = new ArrayList<XsdConfigItem>();
|
||||||
|
return collectPath(root, targetNode, path) ? path : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean collectPath(XsdConfigItem current, XsdConfigItem target, List<XsdConfigItem> path) {
|
||||||
|
if (current == null || target == null || path == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
path.add(current);
|
||||||
|
if (current == target) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (XsdConfigItem child : current.getChildren()) {
|
||||||
|
if (collectPath(child, target, path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyDirectConfig(XsdConfigItem item, ResultNode node) {
|
private void applyDirectConfig(XsdConfigItem item, ResultNode node) {
|
||||||
@@ -213,6 +345,7 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<File> candidates = new ArrayList<File>();
|
List<File> candidates = new ArrayList<File>();
|
||||||
|
File moduleBaseDir = XomPathStrategy.resolveModuleBaseDir(projectBasePath, mappingXmlFile);
|
||||||
File declared = new File(xsdPath);
|
File declared = new File(xsdPath);
|
||||||
if (declared.isAbsolute()) {
|
if (declared.isAbsolute()) {
|
||||||
candidates.add(declared);
|
candidates.add(declared);
|
||||||
@@ -223,16 +356,14 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
candidates.add(new File(mappingParent, xsdPath));
|
candidates.add(new File(mappingParent, xsdPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
File resourcesRoot = findAncestorByName(mappingParent, "resources");
|
File resourcesRoot = XomPathStrategy.findAncestorByName(mappingParent, "resources");
|
||||||
if (resourcesRoot != null) {
|
if (resourcesRoot != null) {
|
||||||
candidates.add(new File(resourcesRoot, xsdPath));
|
candidates.add(new File(resourcesRoot, xsdPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalizedBasePath = trimToNull(projectBasePath);
|
if (moduleBaseDir != null) {
|
||||||
if (normalizedBasePath != null) {
|
candidates.add(new File(moduleBaseDir, xsdPath));
|
||||||
File projectBaseDir = new File(normalizedBasePath);
|
candidates.add(new File(new File(new File(new File(moduleBaseDir, "src"), "main"), "resources"), xsdPath));
|
||||||
candidates.add(new File(projectBaseDir, xsdPath));
|
|
||||||
candidates.add(new File(new File(new File(new File(projectBaseDir, "src"), "main"), "resources"), xsdPath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> dedup = new LinkedHashSet<String>();
|
Set<String> dedup = new LinkedHashSet<String>();
|
||||||
@@ -253,21 +384,6 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
throw new IllegalArgumentException("根据xsdPath未找到XSD文件: " + xsdPath);
|
throw new IllegalArgumentException("根据xsdPath未找到XSD文件: " + xsdPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private File findAncestorByName(File node, String directoryName) {
|
|
||||||
if (directoryName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String normalizedName = directoryName.toLowerCase(Locale.ROOT);
|
|
||||||
File current = node;
|
|
||||||
while (current != null) {
|
|
||||||
if (normalizedName.equals(current.getName().toLowerCase(Locale.ROOT))) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
current = current.getParentFile();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ResultNode> parseResultChildren(Element parent,
|
private List<ResultNode> parseResultChildren(Element parent,
|
||||||
Set<String> customAttributeValues,
|
Set<String> customAttributeValues,
|
||||||
Set<String> preProcessorValues) {
|
Set<String> preProcessorValues) {
|
||||||
@@ -482,4 +598,22 @@ public class ReservedXomReverseDisplayService implements XomReverseDisplayServic
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class ReplayPool {
|
||||||
|
private final List<ResultNode> nodes;
|
||||||
|
private final List<ResultNode> ancestorPath;
|
||||||
|
|
||||||
|
private ReplayPool(List<ResultNode> nodes, List<ResultNode> ancestorPath) {
|
||||||
|
this.nodes = nodes == null ? new ArrayList<ResultNode>() : new ArrayList<ResultNode>(nodes);
|
||||||
|
this.ancestorPath = ancestorPath == null ? new ArrayList<ResultNode>() : new ArrayList<ResultNode>(ancestorPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResultNode> getNodes() {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResultNode> getAncestorPath() {
|
||||||
|
return ancestorPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
package com.dmiki.metaplugin.xom.reverse;
|
package com.dmiki.metaplugin.xom.reverse;
|
||||||
|
|
||||||
|
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public interface XomReverseDisplayService {
|
public interface XomReverseDisplayService {
|
||||||
|
|
||||||
XomReverseDisplayResult loadForDisplay(File mappingXmlFile, String projectBasePath) throws Exception;
|
XomReverseDisplayResult loadForDisplay(File mappingXmlFile, String projectBasePath) throws Exception;
|
||||||
|
|
||||||
|
XomReverseDisplayResult applyToCurrentTree(File mappingXmlFile,
|
||||||
|
String projectBasePath,
|
||||||
|
XsdConfigItem currentRoot,
|
||||||
|
XsdConfigItem targetNode) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,321 @@
|
|||||||
|
package com.dmiki.metaplugin.xsd.ui;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class JavaPackageResolver {
|
||||||
|
private static final String[] PREFERRED_PACKAGES = {"entity", "domain", "message"};
|
||||||
|
|
||||||
|
private JavaPackageResolver() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String resolveDefaultPackage(File moduleBaseDir) {
|
||||||
|
List<File> sourceRoots = collectJavaSourceRoots(moduleBaseDir);
|
||||||
|
if (sourceRoots.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String preferredPackage = resolvePreferredPackage(sourceRoots);
|
||||||
|
if (preferredPackage != null) {
|
||||||
|
return preferredPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inferModulePackage(sourceRoots);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<File> collectJavaSourceRoots(File moduleBaseDir) {
|
||||||
|
if (moduleBaseDir == null || !moduleBaseDir.isDirectory()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, File> sourceRoots = new LinkedHashMap<String, File>();
|
||||||
|
registerSourceRoot(sourceRoots, new File(moduleBaseDir, "src/main/java"));
|
||||||
|
registerSourceRoot(sourceRoots, new File(moduleBaseDir, "src/test/java"));
|
||||||
|
registerSourceRoot(sourceRoots, new File(moduleBaseDir, "src/java"));
|
||||||
|
|
||||||
|
File srcDir = new File(moduleBaseDir, "src");
|
||||||
|
for (File child : listDirectories(srcDir)) {
|
||||||
|
registerSourceRoot(sourceRoots, new File(child, "java"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<File>(sourceRoots.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerSourceRoot(Map<String, File> sourceRoots, File sourceRoot) {
|
||||||
|
if (sourceRoot == null || !sourceRoot.isDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String key;
|
||||||
|
try {
|
||||||
|
key = sourceRoot.getCanonicalPath();
|
||||||
|
sourceRoot = sourceRoot.getCanonicalFile();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
key = sourceRoot.getAbsolutePath();
|
||||||
|
sourceRoot = sourceRoot.getAbsoluteFile();
|
||||||
|
}
|
||||||
|
sourceRoots.put(key, sourceRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolvePreferredPackage(List<File> sourceRoots) {
|
||||||
|
for (String preferredPackageName : PREFERRED_PACKAGES) {
|
||||||
|
List<String> candidates = new ArrayList<String>();
|
||||||
|
for (File sourceRoot : sourceRoots) {
|
||||||
|
collectPackageDirectoriesByName(sourceRoot, preferredPackageName, candidates);
|
||||||
|
}
|
||||||
|
String bestMatch = chooseBestPackage(candidates);
|
||||||
|
if (bestMatch != null) {
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void collectPackageDirectoriesByName(File sourceRoot, String directoryName, List<String> candidates) {
|
||||||
|
if (sourceRoot == null || directoryName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Deque<File> stack = new ArrayDeque<File>();
|
||||||
|
stack.push(sourceRoot);
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
File current = stack.pop();
|
||||||
|
List<File> children = listDirectories(current);
|
||||||
|
for (int i = children.size() - 1; i >= 0; i--) {
|
||||||
|
File child = children.get(i);
|
||||||
|
if (directoryName.equals(child.getName())) {
|
||||||
|
String packageName = toPackageName(sourceRoot, child);
|
||||||
|
if (packageName != null) {
|
||||||
|
candidates.add(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String inferModulePackage(List<File> sourceRoots) {
|
||||||
|
Set<String> packageNames = new LinkedHashSet<String>();
|
||||||
|
for (File sourceRoot : sourceRoots) {
|
||||||
|
collectJavaFilePackages(sourceRoot, packageNames);
|
||||||
|
String chainedPackage = inferSinglePathPackage(sourceRoot);
|
||||||
|
if (chainedPackage != null) {
|
||||||
|
packageNames.add(chainedPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packageNames.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String commonPrefix = longestCommonPackagePrefix(packageNames);
|
||||||
|
if (commonPrefix != null) {
|
||||||
|
return commonPrefix;
|
||||||
|
}
|
||||||
|
return chooseBestPackage(new ArrayList<String>(packageNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void collectJavaFilePackages(File sourceRoot, Set<String> packageNames) {
|
||||||
|
Deque<File> stack = new ArrayDeque<File>();
|
||||||
|
stack.push(sourceRoot);
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
File current = stack.pop();
|
||||||
|
for (File child : listChildren(current)) {
|
||||||
|
if (child.isDirectory()) {
|
||||||
|
if (isSkippableDirectory(child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stack.push(child);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!child.isFile() || !child.getName().endsWith(".java")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String packageName = toPackageName(sourceRoot, child.getParentFile());
|
||||||
|
if (packageName != null) {
|
||||||
|
packageNames.add(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String inferSinglePathPackage(File sourceRoot) {
|
||||||
|
List<String> segments = new ArrayList<String>();
|
||||||
|
File current = sourceRoot;
|
||||||
|
while (current != null && current.isDirectory()) {
|
||||||
|
if (containsJavaFiles(current)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<File> children = listPackageDirectories(current);
|
||||||
|
if (children.size() != 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
File child = children.get(0);
|
||||||
|
segments.add(child.getName());
|
||||||
|
current = child;
|
||||||
|
}
|
||||||
|
if (segments.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return joinSegments(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsJavaFiles(File directory) {
|
||||||
|
for (File child : listChildren(directory)) {
|
||||||
|
if (child.isFile() && child.getName().endsWith(".java")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String longestCommonPackagePrefix(Set<String> packageNames) {
|
||||||
|
if (packageNames.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] commonSegments = null;
|
||||||
|
for (String packageName : packageNames) {
|
||||||
|
if (packageName == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] segments = packageName.split("\\.");
|
||||||
|
if (commonSegments == null) {
|
||||||
|
commonSegments = segments;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int length = Math.min(commonSegments.length, segments.length);
|
||||||
|
int index = 0;
|
||||||
|
while (index < length && commonSegments[index].equals(segments[index])) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (index == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
commonSegments = Arrays.copyOf(commonSegments, index);
|
||||||
|
}
|
||||||
|
if (commonSegments == null || commonSegments.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return joinSegments(Arrays.asList(commonSegments));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String chooseBestPackage(List<String> packageNames) {
|
||||||
|
if (packageNames == null || packageNames.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Collections.sort(packageNames, new Comparator<String>() {
|
||||||
|
@Override
|
||||||
|
public int compare(String left, String right) {
|
||||||
|
int leftDepth = left.split("\\.").length;
|
||||||
|
int rightDepth = right.split("\\.").length;
|
||||||
|
if (leftDepth != rightDepth) {
|
||||||
|
return Integer.compare(leftDepth, rightDepth);
|
||||||
|
}
|
||||||
|
if (left.length() != right.length()) {
|
||||||
|
return Integer.compare(left.length(), right.length());
|
||||||
|
}
|
||||||
|
return left.compareTo(right);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return packageNames.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toPackageName(File sourceRoot, File directory) {
|
||||||
|
if (sourceRoot == null || directory == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String relativePath;
|
||||||
|
try {
|
||||||
|
relativePath = sourceRoot.toPath().relativize(directory.toPath()).toString();
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (relativePath.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] segments = relativePath.replace('\\', '/').split("/");
|
||||||
|
for (String segment : segments) {
|
||||||
|
if (!isValidPackageSegment(segment)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return joinSegments(Arrays.asList(segments));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<File> listDirectories(File directory) {
|
||||||
|
List<File> directories = new ArrayList<File>();
|
||||||
|
for (File child : listChildren(directory)) {
|
||||||
|
if (child.isDirectory() && !isSkippableDirectory(child)) {
|
||||||
|
directories.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(directories, new Comparator<File>() {
|
||||||
|
@Override
|
||||||
|
public int compare(File left, File right) {
|
||||||
|
return left.getName().compareTo(right.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return directories;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<File> listPackageDirectories(File directory) {
|
||||||
|
List<File> directories = new ArrayList<File>();
|
||||||
|
for (File child : listDirectories(directory)) {
|
||||||
|
if (isValidPackageSegment(child.getName())) {
|
||||||
|
directories.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directories;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File[] listChildren(File directory) {
|
||||||
|
if (directory == null || !directory.isDirectory()) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
File[] children = directory.listFiles();
|
||||||
|
return children == null ? new File[0] : children;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSkippableDirectory(File directory) {
|
||||||
|
String name = directory.getName();
|
||||||
|
return name.startsWith(".")
|
||||||
|
|| "build".equals(name)
|
||||||
|
|| "out".equals(name)
|
||||||
|
|| "target".equals(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidPackageSegment(String segment) {
|
||||||
|
if (segment == null || segment.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Character.isJavaIdentifierStart(segment.charAt(0))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < segment.length(); i++) {
|
||||||
|
if (!Character.isJavaIdentifierPart(segment.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String joinSegments(List<String> segments) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (String segment : segments) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append('.');
|
||||||
|
}
|
||||||
|
builder.append(segment);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,18 +5,23 @@ import com.dmiki.metaplugin.xom.XomGenerationDefaults;
|
|||||||
import com.dmiki.metaplugin.xom.XomGenerationException;
|
import com.dmiki.metaplugin.xom.XomGenerationException;
|
||||||
import com.dmiki.metaplugin.xom.XomGenerationResult;
|
import com.dmiki.metaplugin.xom.XomGenerationResult;
|
||||||
import com.dmiki.metaplugin.xom.XomGenerationService;
|
import com.dmiki.metaplugin.xom.XomGenerationService;
|
||||||
|
import com.dmiki.metaplugin.xom.XomPathStrategy;
|
||||||
import com.dmiki.metaplugin.xom.reverse.ReservedXomReverseDisplayService;
|
import com.dmiki.metaplugin.xom.reverse.ReservedXomReverseDisplayService;
|
||||||
import com.dmiki.metaplugin.xom.reverse.XomReverseDisplayResult;
|
import com.dmiki.metaplugin.xom.reverse.XomReverseDisplayResult;
|
||||||
import com.dmiki.metaplugin.xom.reverse.XomReverseDisplayService;
|
import com.dmiki.metaplugin.xom.reverse.XomReverseDisplayService;
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
||||||
import com.dmiki.metaplugin.xsd.parser.XsdSchemaParser;
|
import com.dmiki.metaplugin.xsd.parser.XsdSchemaParser;
|
||||||
|
import com.intellij.openapi.fileChooser.FileChooser;
|
||||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager;
|
||||||
import com.intellij.openapi.ui.DialogWrapper;
|
import com.intellij.openapi.ui.DialogWrapper;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.ui.JBColor;
|
import com.intellij.ui.JBColor;
|
||||||
import com.intellij.ui.ScrollPaneFactory;
|
import com.intellij.ui.ScrollPaneFactory;
|
||||||
import com.intellij.ui.components.JBLabel;
|
import com.intellij.ui.components.JBLabel;
|
||||||
@@ -76,15 +81,19 @@ import java.awt.event.KeyEvent;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class XsdConfigDialog extends DialogWrapper {
|
public class XsdConfigDialog extends DialogWrapper {
|
||||||
private static final int TOGGLE_INPUT_GAP = 14;
|
private static final int TOGGLE_INPUT_GAP = 10;
|
||||||
|
private static final int TOGGLE_INPUT_OUTER_HORIZONTAL_PADDING = 4;
|
||||||
|
private static final int TOGGLE_INPUT_INNER_HORIZONTAL_PADDING = 3;
|
||||||
|
private static final int TOGGLE_INPUT_TEXT_HORIZONTAL_PADDING = 2;
|
||||||
|
private static final int TOGGLE_INPUT_TEXT_WIDTH = 60;
|
||||||
|
private static final int TOGGLE_INPUT_CHECKBOX_MIN_SIZE = 20;
|
||||||
|
private static final String DEFAULT_RESULT_MAP_PACKAGE = "com.example.message";
|
||||||
private final Project project;
|
private final Project project;
|
||||||
private final XsdSchemaParser parser = new XsdSchemaParser();
|
private final XsdSchemaParser parser = new XsdSchemaParser();
|
||||||
private final XomGenerationService xomGenerationService = new XomGenerationService();
|
private final XomGenerationService xomGenerationService = new XomGenerationService();
|
||||||
@@ -109,6 +118,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
private MouseAdapter outsideClickEditStopper;
|
private MouseAdapter outsideClickEditStopper;
|
||||||
private XsdConfigItem currentRoot;
|
private XsdConfigItem currentRoot;
|
||||||
private File currentXsdFile;
|
private File currentXsdFile;
|
||||||
|
private String currentModuleBasePath;
|
||||||
private String lastAutoResultMapType;
|
private String lastAutoResultMapType;
|
||||||
|
|
||||||
public XsdConfigDialog(@Nullable Project project) {
|
public XsdConfigDialog(@Nullable Project project) {
|
||||||
@@ -154,7 +164,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
|
|
||||||
installOutsideClickEditTermination(panel);
|
installOutsideClickEditTermination(panel);
|
||||||
|
|
||||||
panel.setPreferredSize(new Dimension(1220, 720));
|
panel.setPreferredSize(new Dimension(1280, 720));
|
||||||
setActionButtonsEnabled(false);
|
setActionButtonsEnabled(false);
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
@@ -213,7 +223,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
row1.setOpaque(false);
|
row1.setOpaque(false);
|
||||||
|
|
||||||
row1.add(createOptionLabel("报文类型:"));
|
row1.add(createOptionLabel("报文类型:"));
|
||||||
msgTypeField = createOptionField(180);
|
msgTypeField = createOptionField(130);
|
||||||
row1.add(msgTypeField);
|
row1.add(msgTypeField);
|
||||||
|
|
||||||
row1.add(createOptionLabel("包名:"));
|
row1.add(createOptionLabel("包名:"));
|
||||||
@@ -222,6 +232,16 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
installResultMapTypeSync();
|
installResultMapTypeSync();
|
||||||
syncResultMapTypeFromMsgType(true);
|
syncResultMapTypeFromMsgType(true);
|
||||||
|
|
||||||
|
row1.add(createOptionLabel("自定义属性:"));
|
||||||
|
customAttributeField = createOptionField(120);
|
||||||
|
customAttributeField.setText("sign");
|
||||||
|
row1.add(customAttributeField);
|
||||||
|
|
||||||
|
row1.add(createOptionLabel("预处理属性:"));
|
||||||
|
preProcessorField = createOptionField(150);
|
||||||
|
preProcessorField.setText("");
|
||||||
|
row1.add(preProcessorField);
|
||||||
|
|
||||||
row1.add(createOptionLabel("输出目录:"));
|
row1.add(createOptionLabel("输出目录:"));
|
||||||
outputPathField = new TextFieldWithBrowseButton();
|
outputPathField = new TextFieldWithBrowseButton();
|
||||||
outputPathField.setPreferredSize(new Dimension(340, 32));
|
outputPathField.setPreferredSize(new Dimension(340, 32));
|
||||||
@@ -236,22 +256,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
});
|
});
|
||||||
row1.add(outputPathField);
|
row1.add(outputPathField);
|
||||||
|
|
||||||
JBPanel row2 = new JBPanel(new HorizontalLayout(8));
|
|
||||||
row2.setOpaque(false);
|
|
||||||
row2.setBorder(new EmptyBorder(6, 0, 0, 0));
|
|
||||||
|
|
||||||
row2.add(createOptionLabel("自定义属性:"));
|
|
||||||
customAttributeField = createOptionField(180);
|
|
||||||
customAttributeField.setText("sign");
|
|
||||||
row2.add(customAttributeField);
|
|
||||||
|
|
||||||
row2.add(createOptionLabel("预处理属性:"));
|
|
||||||
preProcessorField = createOptionField(220);
|
|
||||||
preProcessorField.setText("");
|
|
||||||
row2.add(preProcessorField);
|
|
||||||
|
|
||||||
container.add(row1);
|
container.add(row1);
|
||||||
container.add(row2);
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +309,8 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildDefaultResultMapType() {
|
private String buildDefaultResultMapType() {
|
||||||
return "com.example.message";
|
String packageName = JavaPackageResolver.resolveDefaultPackage(moduleBaseDir());
|
||||||
|
return packageName == null ? DEFAULT_RESULT_MAP_PACKAGE : packageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void styleTextField(JTextField textField) {
|
private void styleTextField(JTextField textField) {
|
||||||
@@ -436,7 +442,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
try {
|
try {
|
||||||
XomReverseDisplayResult reverseResult = xomReverseDisplayService.loadForDisplay(
|
XomReverseDisplayResult reverseResult = xomReverseDisplayService.loadForDisplay(
|
||||||
xomFile,
|
xomFile,
|
||||||
project == null ? null : project.getBasePath());
|
resolveModuleBasePath(xomFile));
|
||||||
File xsdFile = reverseResult.getXsdFile();
|
File xsdFile = reverseResult.getXsdFile();
|
||||||
XsdConfigItem root = reverseResult.getRoot();
|
XsdConfigItem root = reverseResult.getRoot();
|
||||||
|
|
||||||
@@ -453,6 +459,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
private void displayTree(File xsdFile, XsdConfigItem root) {
|
private void displayTree(File xsdFile, XsdConfigItem root) {
|
||||||
currentRoot = root;
|
currentRoot = root;
|
||||||
currentXsdFile = xsdFile;
|
currentXsdFile = xsdFile;
|
||||||
|
currentModuleBasePath = resolveModuleBasePath(xsdFile);
|
||||||
applyGenerationDefaults(xsdFile);
|
applyGenerationDefaults(xsdFile);
|
||||||
|
|
||||||
DefaultMutableTreeNode swingRoot = XsdTreeTableModel.toSwingTree(root);
|
DefaultMutableTreeNode swingRoot = XsdTreeTableModel.toSwingTree(root);
|
||||||
@@ -483,7 +490,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
}
|
}
|
||||||
if (outputPathField != null && xomFile != null) {
|
if (outputPathField != null && xomFile != null) {
|
||||||
File parent = xomFile.getParentFile();
|
File parent = xomFile.getParentFile();
|
||||||
outputPathField.setText(toProjectRelativePath(parent == null ? xomFile : parent));
|
outputPathField.setText(toModuleRelativePath(parent == null ? xomFile : parent));
|
||||||
}
|
}
|
||||||
if (customAttributeField != null) {
|
if (customAttributeField != null) {
|
||||||
customAttributeField.setText(trimToEmpty(reverseResult.getCustomAttribute()));
|
customAttributeField.setText(trimToEmpty(reverseResult.getCustomAttribute()));
|
||||||
@@ -610,6 +617,12 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
private JPopupMenu createNodeContextMenu(XsdConfigItem selectedItem) {
|
private JPopupMenu createNodeContextMenu(XsdConfigItem selectedItem) {
|
||||||
JPopupMenu menu = new JPopupMenu();
|
JPopupMenu menu = new JPopupMenu();
|
||||||
|
|
||||||
|
JMenuItem applyItem = new JMenuItem("应用已存在的XOM");
|
||||||
|
applyItem.addActionListener(event -> applyExistingXomToNode(selectedItem));
|
||||||
|
menu.add(applyItem);
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
JMenuItem previewItem = new JMenuItem("预览选中节点XOM");
|
JMenuItem previewItem = new JMenuItem("预览选中节点XOM");
|
||||||
previewItem.addActionListener(event -> previewXomForNode(selectedItem));
|
previewItem.addActionListener(event -> previewXomForNode(selectedItem));
|
||||||
menu.add(previewItem);
|
menu.add(previewItem);
|
||||||
@@ -621,6 +634,86 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyExistingXomToNode(XsdConfigItem selectedItem) {
|
||||||
|
if (currentRoot == null || currentXsdFile == null || selectedItem == null) {
|
||||||
|
Messages.showWarningDialog(project, "请先解析XSD并选择节点", "提示");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopTreeTableEditing();
|
||||||
|
File xomFile = chooseExistingXomFile();
|
||||||
|
if (xomFile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XomReverseDisplayResult reverseResult = xomReverseDisplayService.applyToCurrentTree(
|
||||||
|
xomFile,
|
||||||
|
resolveModuleBasePath(xomFile),
|
||||||
|
currentRoot,
|
||||||
|
selectedItem);
|
||||||
|
if (selectedItem == currentRoot) {
|
||||||
|
applyReplayedOptions(reverseResult, xomFile);
|
||||||
|
}
|
||||||
|
refreshTreeTableAfterReplay();
|
||||||
|
|
||||||
|
String nodeName = trimToNull(selectedItem.getXmlName());
|
||||||
|
Messages.showInfoMessage(project,
|
||||||
|
"XOM配置已应用到节点: " + (nodeName == null ? "(未命名节点)" : nodeName),
|
||||||
|
"完成");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Messages.showErrorDialog(project, "应用XOM失败: " + ex.getMessage(), "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File chooseExistingXomFile() {
|
||||||
|
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
|
||||||
|
descriptor.withFileFilter(virtualFile -> {
|
||||||
|
String extension = virtualFile.getExtension();
|
||||||
|
return "xml".equalsIgnoreCase(extension) || "xom".equalsIgnoreCase(extension);
|
||||||
|
});
|
||||||
|
descriptor.setTitle("选择已存在的XOM文件");
|
||||||
|
descriptor.setDescription("选择一个 .xml 或 .xom 文件,并将配置应用到当前选中节点");
|
||||||
|
|
||||||
|
File initialFile = resolveInitialXomSelection();
|
||||||
|
VirtualFile initialVirtualFile = initialFile == null
|
||||||
|
? null
|
||||||
|
: LocalFileSystem.getInstance().refreshAndFindFileByIoFile(initialFile);
|
||||||
|
VirtualFile selected = FileChooser.chooseFile(descriptor, project, initialVirtualFile);
|
||||||
|
return selected == null ? null : new File(selected.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private File resolveInitialXomSelection() {
|
||||||
|
String outputPath = valueOf(outputPathField);
|
||||||
|
if (outputPath != null) {
|
||||||
|
File outputDirectory = resolveOutputDirectory(outputPath);
|
||||||
|
if (outputDirectory != null && outputDirectory.exists()) {
|
||||||
|
return outputDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentXsdFile != null) {
|
||||||
|
File parent = currentXsdFile.getParentFile();
|
||||||
|
if (parent != null && parent.exists()) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File moduleBaseDir = moduleBaseDir();
|
||||||
|
if (moduleBaseDir != null && moduleBaseDir.exists()) {
|
||||||
|
return moduleBaseDir;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshTreeTableAfterReplay() {
|
||||||
|
if (treeTable != null) {
|
||||||
|
treeTable.revalidate();
|
||||||
|
treeTable.repaint();
|
||||||
|
}
|
||||||
|
if (currentRoot != null) {
|
||||||
|
refreshStats(currentRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void installOutsideClickEditTermination(JComponent rootComponent) {
|
private void installOutsideClickEditTermination(JComponent rootComponent) {
|
||||||
if (outsideClickEditStopper != null) {
|
if (outsideClickEditStopper != null) {
|
||||||
return;
|
return;
|
||||||
@@ -701,6 +794,23 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Dimension toggleCheckBoxSize(@Nullable Dimension preferredSize) {
|
||||||
|
int width = preferredSize == null ? 0 : preferredSize.width;
|
||||||
|
int height = preferredSize == null ? 0 : preferredSize.height;
|
||||||
|
int size = Math.max(TOGGLE_INPUT_CHECKBOX_MIN_SIZE, Math.max(width, height));
|
||||||
|
return new Dimension(size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyToggleCheckBoxSizing(JCheckBox checkBox) {
|
||||||
|
if (checkBox == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Dimension size = toggleCheckBoxSize(checkBox.getPreferredSize());
|
||||||
|
checkBox.setPreferredSize(size);
|
||||||
|
checkBox.setMinimumSize(size);
|
||||||
|
checkBox.setMaximumSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
private XsdConfigItem itemOfRow(JTable table, int row) {
|
private XsdConfigItem itemOfRow(JTable table, int row) {
|
||||||
if (!(table instanceof TreeTable)) {
|
if (!(table instanceof TreeTable)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -713,6 +823,22 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
return itemOf(path.getLastPathComponent());
|
return itemOf(path.getLastPathComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean shouldRenderConfigField(JTable table, int row) {
|
||||||
|
if (!(table instanceof TreeTable)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
JTree tree = ((TreeTable) table).getTree();
|
||||||
|
TreePath path = tree.getPathForRow(row);
|
||||||
|
if (path == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Object node = path.getLastPathComponent();
|
||||||
|
if (!(node instanceof DefaultMutableTreeNode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ((DefaultMutableTreeNode) node).getParent() != null;
|
||||||
|
}
|
||||||
|
|
||||||
private void installTextEditor(TableColumn column) {
|
private void installTextEditor(TableColumn column) {
|
||||||
column.setCellEditor(new InputLikeTextCellEditor());
|
column.setCellEditor(new InputLikeTextCellEditor());
|
||||||
}
|
}
|
||||||
@@ -794,25 +920,9 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String resolveDefaultOutputPath(File xsdFile) {
|
private String resolveDefaultOutputPath(File xsdFile) {
|
||||||
if (project == null || project.getBasePath() == null) {
|
File outputBaseDir = XomPathStrategy.resolveDefaultOutputBaseDir(xsdFile);
|
||||||
File parent = xsdFile.getParentFile();
|
File outputDir = XomPathStrategy.resolveNextMsgMapperDirectory(outputBaseDir);
|
||||||
return parent == null ? xsdFile.getAbsolutePath() : parent.getAbsolutePath();
|
return toModuleRelativePath(outputDir);
|
||||||
}
|
|
||||||
File resourcesDir = new File(project.getBasePath(), "src/main/resources");
|
|
||||||
File outputDir = resolveNextMsgMapperDirectory(resourcesDir);
|
|
||||||
return toProjectRelativePath(outputDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File resolveNextMsgMapperDirectory(File resourcesDir) {
|
|
||||||
int suffix = 0;
|
|
||||||
while (true) {
|
|
||||||
String dirName = suffix == 0 ? "msgmapper" : "msgmapper" + suffix;
|
|
||||||
File candidate = new File(resourcesDir, dirName);
|
|
||||||
if (!candidate.exists()) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
suffix++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateXomFile() {
|
private void generateXomFile() {
|
||||||
@@ -830,8 +940,8 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
File javaEntityFile = result.getJavaEntityFile();
|
File javaEntityFile = result.getJavaEntityFile();
|
||||||
Messages.showInfoMessage(project,
|
Messages.showInfoMessage(project,
|
||||||
"XOM生成成功:\n"
|
"XOM生成成功:\n"
|
||||||
+ "XML: " + toProjectRelativePath(result.getMappingXmlFile())
|
+ "XML: " + toModuleRelativePath(result.getMappingXmlFile())
|
||||||
+ "\nJAVA: " + (javaEntityFile == null ? "未生成(包名为空)" : toProjectRelativePath(javaEntityFile)),
|
+ "\nJAVA: " + (javaEntityFile == null ? "未生成(包名为空)" : toModuleRelativePath(javaEntityFile)),
|
||||||
"完成");
|
"完成");
|
||||||
} catch (XomGenerationException ex) {
|
} catch (XomGenerationException ex) {
|
||||||
Messages.showErrorDialog(project, "XOM生成失败: " + ex.getMessage(), "错误");
|
Messages.showErrorDialog(project, "XOM生成失败: " + ex.getMessage(), "错误");
|
||||||
@@ -886,8 +996,8 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
Messages.showInfoMessage(project,
|
Messages.showInfoMessage(project,
|
||||||
"节点XOM生成成功:\n"
|
"节点XOM生成成功:\n"
|
||||||
+ "节点: " + (nodeName == null ? "(未命名节点)" : nodeName)
|
+ "节点: " + (nodeName == null ? "(未命名节点)" : nodeName)
|
||||||
+ "\nXML: " + toProjectRelativePath(result.getMappingXmlFile())
|
+ "\nXML: " + toModuleRelativePath(result.getMappingXmlFile())
|
||||||
+ "\nJAVA: " + (javaEntityFile == null ? "未生成(包名为空)" : toProjectRelativePath(javaEntityFile)),
|
+ "\nJAVA: " + (javaEntityFile == null ? "未生成(包名为空)" : toModuleRelativePath(javaEntityFile)),
|
||||||
"完成");
|
"完成");
|
||||||
} catch (XomGenerationException ex) {
|
} catch (XomGenerationException ex) {
|
||||||
Messages.showErrorDialog(project, "节点XOM生成失败: " + ex.getMessage(), "错误");
|
Messages.showErrorDialog(project, "节点XOM生成失败: " + ex.getMessage(), "错误");
|
||||||
@@ -914,7 +1024,7 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
boolean hasJavaPackage = hasPackageName(packageName);
|
boolean hasJavaPackage = hasPackageName(packageName);
|
||||||
String outputPath = resolveOutputFilePath(outputDirectoryPath, outputFileName);
|
String outputPath = resolveOutputFilePath(outputDirectoryPath, outputFileName);
|
||||||
return new XomGenerateOptions(
|
return new XomGenerateOptions(
|
||||||
project == null ? null : project.getBasePath(),
|
currentModuleBasePath,
|
||||||
null,
|
null,
|
||||||
valueOf(msgTypeField),
|
valueOf(msgTypeField),
|
||||||
resultMapType,
|
resultMapType,
|
||||||
@@ -998,13 +1108,13 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
if (fileName == null) {
|
if (fileName == null) {
|
||||||
fileName = defaultOutputName();
|
fileName = defaultOutputName();
|
||||||
}
|
}
|
||||||
return toProjectRelativePath(new File(directory, fileName));
|
return toModuleRelativePath(new File(directory, fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private File resolveOutputDirectory(String outputDirectoryPath) {
|
private File resolveOutputDirectory(String outputDirectoryPath) {
|
||||||
File candidate = resolvePathAgainstProjectBase(outputDirectoryPath);
|
File candidate = resolvePathAgainstModuleBase(outputDirectoryPath);
|
||||||
if (candidate != null) {
|
if (candidate != null) {
|
||||||
if (isXmlFilePath(candidate)) {
|
if (XomPathStrategy.isXmlFilePath(candidate)) {
|
||||||
File parent = candidate.getParentFile();
|
File parent = candidate.getParentFile();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
return parent;
|
return parent;
|
||||||
@@ -1013,25 +1123,14 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
if (currentXsdFile != null) {
|
if (currentXsdFile != null) {
|
||||||
if (project != null && trimToNull(project.getBasePath()) != null) {
|
File outputBaseDir = XomPathStrategy.resolveDefaultOutputBaseDir(currentXsdFile);
|
||||||
File resourcesDir = new File(project.getBasePath(), "src/main/resources");
|
if (outputBaseDir != null) {
|
||||||
return resolveNextMsgMapperDirectory(resourcesDir);
|
return XomPathStrategy.resolveNextMsgMapperDirectory(outputBaseDir);
|
||||||
}
|
|
||||||
File parent = currentXsdFile.getParentFile();
|
|
||||||
if (parent != null) {
|
|
||||||
return parent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new File("msgmapper");
|
return new File("msgmapper");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isXmlFilePath(File file) {
|
|
||||||
if (file == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return file.getName().toLowerCase(Locale.ROOT).endsWith(".xml");
|
|
||||||
}
|
|
||||||
|
|
||||||
private String valueOf(TextFieldWithBrowseButton field) {
|
private String valueOf(TextFieldWithBrowseButton field) {
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1054,46 +1153,57 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
if (path == null) {
|
if (path == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
outputPathField.setText(toProjectRelativePath(resolveOutputDirectory(path)));
|
outputPathField.setText(toModuleRelativePath(resolveOutputDirectory(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private File resolvePathAgainstProjectBase(String path) {
|
private File resolvePathAgainstModuleBase(String path) {
|
||||||
String normalizedPath = trimToNull(path);
|
return XomPathStrategy.resolvePath(path, moduleBaseDir());
|
||||||
if (normalizedPath == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
File candidate = new File(normalizedPath);
|
|
||||||
if (candidate.isAbsolute()) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
String projectBasePath = project == null ? null : trimToNull(project.getBasePath());
|
|
||||||
if (projectBasePath == null) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
return new File(projectBasePath, normalizedPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toProjectRelativePath(File file) {
|
private String toModuleRelativePath(File file) {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
String projectBasePath = project == null ? null : trimToNull(project.getBasePath());
|
File moduleBaseDir = moduleBaseDir();
|
||||||
if (projectBasePath == null) {
|
if (moduleBaseDir == null) {
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
try {
|
return XomPathStrategy.toRelativePath(moduleBaseDir, file);
|
||||||
Path basePath = Paths.get(projectBasePath).toAbsolutePath().normalize();
|
}
|
||||||
Path targetPath = file.toPath().toAbsolutePath().normalize();
|
|
||||||
if (targetPath.startsWith(basePath)) {
|
private File moduleBaseDir() {
|
||||||
String relativePath = basePath.relativize(targetPath).toString();
|
String basePath = trimToNull(currentModuleBasePath);
|
||||||
if (!relativePath.isEmpty()) {
|
return basePath == null ? null : new File(basePath);
|
||||||
return relativePath.replace(File.separatorChar, '/');
|
}
|
||||||
}
|
|
||||||
}
|
private String resolveModuleBasePath(File file) {
|
||||||
} catch (Exception ignore) {
|
File moduleBaseDir = resolveModuleBaseDir(file);
|
||||||
return file.getAbsolutePath();
|
return moduleBaseDir == null ? null : moduleBaseDir.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File resolveModuleBaseDir(File file) {
|
||||||
|
File contentRoot = findContentRoot(file);
|
||||||
|
if (contentRoot != null) {
|
||||||
|
return contentRoot;
|
||||||
}
|
}
|
||||||
return file.getAbsolutePath();
|
return XomPathStrategy.inferModuleBaseDir(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File findContentRoot(File file) {
|
||||||
|
if (project == null || file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file);
|
||||||
|
if (virtualFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
VirtualFile contentRoot = ProjectRootManager.getInstance(project)
|
||||||
|
.getFileIndex()
|
||||||
|
.getContentRootForFile(virtualFile);
|
||||||
|
if (contentRoot == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new File(contentRoot.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String removeExtension(String fileName) {
|
private String removeExtension(String fileName) {
|
||||||
@@ -1388,6 +1498,12 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
int row,
|
int row,
|
||||||
int column) {
|
int column) {
|
||||||
setBackground(rowBackground(row, isSelected));
|
setBackground(rowBackground(row, isSelected));
|
||||||
|
boolean showField = shouldRenderConfigField(table, row);
|
||||||
|
fieldPanel.setVisible(showField);
|
||||||
|
if (!showField) {
|
||||||
|
textLabel.setText("");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
String text = value == null ? "" : value.toString();
|
String text = value == null ? "" : value.toString();
|
||||||
textLabel.setText(text);
|
textLabel.setText(text);
|
||||||
|
|
||||||
@@ -1441,6 +1557,12 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
int row,
|
int row,
|
||||||
int column) {
|
int column) {
|
||||||
setBackground(rowBackground(row, isSelected));
|
setBackground(rowBackground(row, isSelected));
|
||||||
|
boolean showField = shouldRenderConfigField(table, row);
|
||||||
|
fieldPanel.setVisible(showField);
|
||||||
|
if (!showField) {
|
||||||
|
textLabel.setText("");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
fieldPanel.setBackground(isSelected ? Palette.FIELD_BG_SELECTED : Palette.FIELD_BG);
|
fieldPanel.setBackground(isSelected ? Palette.FIELD_BG_SELECTED : Palette.FIELD_BG);
|
||||||
textLabel.setText(value == null ? "" : value.toString());
|
textLabel.setText(value == null ? "" : value.toString());
|
||||||
return this;
|
return this;
|
||||||
@@ -1627,14 +1749,14 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
ToggleInputCellRenderer() {
|
ToggleInputCellRenderer() {
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
setOpaque(true);
|
setOpaque(true);
|
||||||
setBorder(new EmptyBorder(4, 8, 4, 8));
|
setBorder(new EmptyBorder(4, TOGGLE_INPUT_OUTER_HORIZONTAL_PADDING, 4, TOGGLE_INPUT_OUTER_HORIZONTAL_PADDING));
|
||||||
|
|
||||||
fieldPanel = new JPanel(new GridBagLayout());
|
fieldPanel = new JPanel(new GridBagLayout());
|
||||||
fieldPanel.setOpaque(true);
|
fieldPanel.setOpaque(true);
|
||||||
fieldPanel.setBackground(Palette.FIELD_BG);
|
fieldPanel.setBackground(Palette.FIELD_BG);
|
||||||
fieldPanel.setBorder(BorderFactory.createCompoundBorder(
|
fieldPanel.setBorder(BorderFactory.createCompoundBorder(
|
||||||
new LineBorder(Palette.FIELD_BORDER, 1, true),
|
new LineBorder(Palette.FIELD_BORDER, 1, true),
|
||||||
new EmptyBorder(0, 6, 0, 6)));
|
new EmptyBorder(0, TOGGLE_INPUT_INNER_HORIZONTAL_PADDING, 0, TOGGLE_INPUT_INNER_HORIZONTAL_PADDING)));
|
||||||
|
|
||||||
checkBox = new JCheckBox();
|
checkBox = new JCheckBox();
|
||||||
checkBox.setEnabled(true);
|
checkBox.setEnabled(true);
|
||||||
@@ -1645,19 +1767,16 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
checkBox.setRolloverEnabled(false);
|
checkBox.setRolloverEnabled(false);
|
||||||
checkBox.setBorderPainted(false);
|
checkBox.setBorderPainted(false);
|
||||||
checkBox.setMargin(new Insets(0, 0, 0, 0));
|
checkBox.setMargin(new Insets(0, 0, 0, 0));
|
||||||
checkBox.setPreferredSize(new Dimension(18, 18));
|
applyToggleCheckBoxSizing(checkBox);
|
||||||
checkBox.setMinimumSize(new Dimension(18, 18));
|
|
||||||
checkBox.setMaximumSize(new Dimension(18, 18));
|
|
||||||
|
|
||||||
textField = new JTextField();
|
textField = new JTextField();
|
||||||
textField.setEditable(false);
|
textField.setEditable(false);
|
||||||
textField.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
textField.setBorder(BorderFactory.createEmptyBorder(0, TOGGLE_INPUT_TEXT_HORIZONTAL_PADDING, 0, TOGGLE_INPUT_TEXT_HORIZONTAL_PADDING));
|
||||||
textField.setBackground(Palette.FIELD_BG);
|
textField.setBackground(Palette.FIELD_BG);
|
||||||
textField.setForeground(Palette.TEXT_PRIMARY);
|
textField.setForeground(Palette.TEXT_PRIMARY);
|
||||||
textField.setFont(textField.getFont().deriveFont(Font.PLAIN, 13f));
|
textField.setFont(textField.getFont().deriveFont(Font.PLAIN, 13f));
|
||||||
textField.setPreferredSize(new Dimension(50, 20));
|
textField.setPreferredSize(new Dimension(TOGGLE_INPUT_TEXT_WIDTH, 20));
|
||||||
textField.setMinimumSize(new Dimension(50, 20));
|
textField.setMinimumSize(new Dimension(TOGGLE_INPUT_TEXT_WIDTH, 20));
|
||||||
textField.setMaximumSize(new Dimension(50, 20));
|
|
||||||
|
|
||||||
GridBagConstraints checkBoxConstraints = new GridBagConstraints();
|
GridBagConstraints checkBoxConstraints = new GridBagConstraints();
|
||||||
checkBoxConstraints.gridx = 0;
|
checkBoxConstraints.gridx = 0;
|
||||||
@@ -1671,19 +1790,11 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
textFieldConstraints.gridx = 1;
|
textFieldConstraints.gridx = 1;
|
||||||
textFieldConstraints.gridy = 0;
|
textFieldConstraints.gridy = 0;
|
||||||
textFieldConstraints.anchor = GridBagConstraints.WEST;
|
textFieldConstraints.anchor = GridBagConstraints.WEST;
|
||||||
|
textFieldConstraints.weightx = 1.0;
|
||||||
textFieldConstraints.weighty = 1.0;
|
textFieldConstraints.weighty = 1.0;
|
||||||
|
textFieldConstraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
textFieldConstraints.insets = new Insets(0, 0, 0, 0);
|
textFieldConstraints.insets = new Insets(0, 0, 0, 0);
|
||||||
fieldPanel.add(textField, textFieldConstraints);
|
fieldPanel.add(textField, textFieldConstraints);
|
||||||
|
|
||||||
JPanel filler = new JPanel();
|
|
||||||
filler.setOpaque(false);
|
|
||||||
GridBagConstraints fillerConstraints = new GridBagConstraints();
|
|
||||||
fillerConstraints.gridx = 2;
|
|
||||||
fillerConstraints.gridy = 0;
|
|
||||||
fillerConstraints.weightx = 1.0;
|
|
||||||
fillerConstraints.weighty = 1.0;
|
|
||||||
fillerConstraints.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
fieldPanel.add(filler, fillerConstraints);
|
|
||||||
add(fieldPanel, BorderLayout.CENTER);
|
add(fieldPanel, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1694,6 +1805,14 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
boolean hasFocus,
|
boolean hasFocus,
|
||||||
int row,
|
int row,
|
||||||
int column) {
|
int column) {
|
||||||
|
boolean showField = shouldRenderConfigField(table, row);
|
||||||
|
fieldPanel.setVisible(showField);
|
||||||
|
if (!showField) {
|
||||||
|
checkBox.setSelected(false);
|
||||||
|
textField.setText("");
|
||||||
|
setBackground(rowBackground(row, isSelected));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
XsdTreeTableModel.ToggleInputValue toggleInputValue = value instanceof XsdTreeTableModel.ToggleInputValue
|
XsdTreeTableModel.ToggleInputValue toggleInputValue = value instanceof XsdTreeTableModel.ToggleInputValue
|
||||||
? (XsdTreeTableModel.ToggleInputValue) value
|
? (XsdTreeTableModel.ToggleInputValue) value
|
||||||
: XsdTreeTableModel.ToggleInputValue.EMPTY;
|
: XsdTreeTableModel.ToggleInputValue.EMPTY;
|
||||||
@@ -1715,14 +1834,14 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
ToggleInputCellEditor() {
|
ToggleInputCellEditor() {
|
||||||
wrapper = new JPanel(new BorderLayout());
|
wrapper = new JPanel(new BorderLayout());
|
||||||
wrapper.setOpaque(true);
|
wrapper.setOpaque(true);
|
||||||
wrapper.setBorder(new EmptyBorder(4, 8, 4, 8));
|
wrapper.setBorder(new EmptyBorder(4, TOGGLE_INPUT_OUTER_HORIZONTAL_PADDING, 4, TOGGLE_INPUT_OUTER_HORIZONTAL_PADDING));
|
||||||
|
|
||||||
fieldPanel = new JPanel(new GridBagLayout());
|
fieldPanel = new JPanel(new GridBagLayout());
|
||||||
fieldPanel.setOpaque(true);
|
fieldPanel.setOpaque(true);
|
||||||
fieldPanel.setBackground(Palette.FIELD_BG);
|
fieldPanel.setBackground(Palette.FIELD_BG);
|
||||||
fieldPanel.setBorder(BorderFactory.createCompoundBorder(
|
fieldPanel.setBorder(BorderFactory.createCompoundBorder(
|
||||||
new LineBorder(Palette.FIELD_BORDER, 1, true),
|
new LineBorder(Palette.FIELD_BORDER, 1, true),
|
||||||
new EmptyBorder(0, 6, 0, 6)));
|
new EmptyBorder(0, TOGGLE_INPUT_INNER_HORIZONTAL_PADDING, 0, TOGGLE_INPUT_INNER_HORIZONTAL_PADDING)));
|
||||||
|
|
||||||
checkBox = new JCheckBox();
|
checkBox = new JCheckBox();
|
||||||
checkBox.setOpaque(false);
|
checkBox.setOpaque(false);
|
||||||
@@ -1732,18 +1851,15 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
checkBox.setRolloverEnabled(false);
|
checkBox.setRolloverEnabled(false);
|
||||||
checkBox.setBorderPainted(false);
|
checkBox.setBorderPainted(false);
|
||||||
checkBox.setMargin(new Insets(0, 0, 0, 0));
|
checkBox.setMargin(new Insets(0, 0, 0, 0));
|
||||||
checkBox.setPreferredSize(new Dimension(18, 18));
|
applyToggleCheckBoxSizing(checkBox);
|
||||||
checkBox.setMinimumSize(new Dimension(18, 18));
|
|
||||||
checkBox.setMaximumSize(new Dimension(18, 18));
|
|
||||||
|
|
||||||
textField = new JTextField();
|
textField = new JTextField();
|
||||||
textField.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));
|
textField.setBorder(BorderFactory.createEmptyBorder(0, TOGGLE_INPUT_TEXT_HORIZONTAL_PADDING, 0, TOGGLE_INPUT_TEXT_HORIZONTAL_PADDING));
|
||||||
textField.setBackground(Palette.FIELD_BG);
|
textField.setBackground(Palette.FIELD_BG);
|
||||||
textField.setForeground(Palette.TEXT_PRIMARY);
|
textField.setForeground(Palette.TEXT_PRIMARY);
|
||||||
textField.setFont(textField.getFont().deriveFont(Font.PLAIN, 13f));
|
textField.setFont(textField.getFont().deriveFont(Font.PLAIN, 13f));
|
||||||
textField.setPreferredSize(new Dimension(50, 20));
|
textField.setPreferredSize(new Dimension(TOGGLE_INPUT_TEXT_WIDTH, 20));
|
||||||
textField.setMinimumSize(new Dimension(50, 20));
|
textField.setMinimumSize(new Dimension(TOGGLE_INPUT_TEXT_WIDTH, 20));
|
||||||
textField.setMaximumSize(new Dimension(50, 20));
|
|
||||||
|
|
||||||
GridBagConstraints checkBoxConstraints = new GridBagConstraints();
|
GridBagConstraints checkBoxConstraints = new GridBagConstraints();
|
||||||
checkBoxConstraints.gridx = 0;
|
checkBoxConstraints.gridx = 0;
|
||||||
@@ -1757,19 +1873,11 @@ public class XsdConfigDialog extends DialogWrapper {
|
|||||||
textFieldConstraints.gridx = 1;
|
textFieldConstraints.gridx = 1;
|
||||||
textFieldConstraints.gridy = 0;
|
textFieldConstraints.gridy = 0;
|
||||||
textFieldConstraints.anchor = GridBagConstraints.WEST;
|
textFieldConstraints.anchor = GridBagConstraints.WEST;
|
||||||
|
textFieldConstraints.weightx = 1.0;
|
||||||
textFieldConstraints.weighty = 1.0;
|
textFieldConstraints.weighty = 1.0;
|
||||||
|
textFieldConstraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
textFieldConstraints.insets = new Insets(0, 0, 0, 0);
|
textFieldConstraints.insets = new Insets(0, 0, 0, 0);
|
||||||
fieldPanel.add(textField, textFieldConstraints);
|
fieldPanel.add(textField, textFieldConstraints);
|
||||||
|
|
||||||
JPanel filler = new JPanel();
|
|
||||||
filler.setOpaque(false);
|
|
||||||
GridBagConstraints fillerConstraints = new GridBagConstraints();
|
|
||||||
fillerConstraints.gridx = 2;
|
|
||||||
fillerConstraints.gridy = 0;
|
|
||||||
fillerConstraints.weightx = 1.0;
|
|
||||||
fillerConstraints.weighty = 1.0;
|
|
||||||
fillerConstraints.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
fieldPanel.add(filler, fillerConstraints);
|
|
||||||
wrapper.add(fieldPanel, BorderLayout.CENTER);
|
wrapper.add(fieldPanel, BorderLayout.CENTER);
|
||||||
installCellEditorKeyBindings(wrapper, this);
|
installCellEditorKeyBindings(wrapper, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<id>com.dmiki.metaplugin</id>
|
<id>com.dmiki.metaplugin</id>
|
||||||
<name>MetaPlugin</name>
|
<name>MetaPlugin</name>
|
||||||
<version>1.0.0</version>
|
<version>1.1.0</version>
|
||||||
<vendor email="support@example.com" url="https://example.com">RD5</vendor>
|
<vendor email="support@example.com" url="https://example.com">RD5</vendor>
|
||||||
|
|
||||||
<description><![CDATA[
|
<description><![CDATA[
|
||||||
|
|||||||
@@ -51,7 +51,9 @@
|
|||||||
### 目标
|
### 目标
|
||||||
|
|
||||||
将 XSD 结构转为“可执行的自定义 XML 映射配置”,用于后续 生成Java 实体与 XML 的双向/单向序列化流程。
|
将 XSD 结构转为“可执行的自定义 XML 映射配置”,用于后续 生成Java 实体与 XML 的双向/单向序列化流程。
|
||||||
1、支持生成xml映射文件,默认存放在resources下msgmapper目录下,如果msgmapper存在,则放在msgmapper1下,如果msgmapper1存在,则放在msgmapper2下,以此类推。
|
1、支持生成xml映射文件:
|
||||||
|
- 如果XSD位于某个resources目录下,则默认存放在该resources下的msgmapper目录;如果msgmapper存在,则放在msgmapper1下,如果msgmapper1存在,则放在msgmapper2下,以此类推。
|
||||||
|
- 如果XSD不在resources目录下,则默认存放在XSD父级目录的同级msgmapper目录;命名冲突时同样按msgmapper1、msgmapper2递增。
|
||||||
2、支持生成java实体类,与上一步的xml文件在同一个目录
|
2、支持生成java实体类,与上一步的xml文件在同一个目录
|
||||||
3、预留后续根据xml映射文件和xsd反向展示能里
|
3、预留后续根据xml映射文件和xsd反向展示能里
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,70 @@ public class XomGenerationServiceTest {
|
|||||||
assertTrue(expected.isFile());
|
assertTrue(expected.isFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generate_defaultsToResourcesMsgMapperWhenXsdIsUnderResources() throws Exception {
|
||||||
|
File moduleBase = Files.createTempDirectory("xom-service-module-resources").toFile();
|
||||||
|
File xsdFile = new File(moduleBase, "src/main/resources/xsd/demo.xsd");
|
||||||
|
File existingMsgmapper = new File(moduleBase, "src/main/resources/msgmapper");
|
||||||
|
assertTrue(xsdFile.getParentFile().mkdirs() || xsdFile.getParentFile().isDirectory());
|
||||||
|
assertTrue(existingMsgmapper.mkdirs() || existingMsgmapper.isDirectory());
|
||||||
|
assertTrue(xsdFile.createNewFile() || xsdFile.isFile());
|
||||||
|
|
||||||
|
XomGenerateOptions options = new XomGenerateOptions(
|
||||||
|
moduleBase.getAbsolutePath(),
|
||||||
|
"message",
|
||||||
|
"demo",
|
||||||
|
null,
|
||||||
|
xsdFile.getAbsolutePath(),
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
8
|
||||||
|
);
|
||||||
|
|
||||||
|
XomGenerationResult result = service.generate(simpleRoot(), options);
|
||||||
|
File expected = new File(moduleBase, "src/main/resources/msgmapper1/demo.xml");
|
||||||
|
|
||||||
|
assertEquals(expected.getCanonicalFile(), result.getMappingXmlFile().getCanonicalFile());
|
||||||
|
assertTrue(expected.isFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generate_defaultsToSiblingMsgMapperWhenXsdIsOutsideResources() throws Exception {
|
||||||
|
File moduleBase = Files.createTempDirectory("xom-service-module-sibling").toFile();
|
||||||
|
File xsdFile = new File(moduleBase, "schema/inbound/demo.xsd");
|
||||||
|
File existingMsgmapper = new File(moduleBase, "schema/msgmapper");
|
||||||
|
assertTrue(xsdFile.getParentFile().mkdirs() || xsdFile.getParentFile().isDirectory());
|
||||||
|
assertTrue(existingMsgmapper.mkdirs() || existingMsgmapper.isDirectory());
|
||||||
|
assertTrue(xsdFile.createNewFile() || xsdFile.isFile());
|
||||||
|
|
||||||
|
XomGenerateOptions options = new XomGenerateOptions(
|
||||||
|
moduleBase.getAbsolutePath(),
|
||||||
|
"message",
|
||||||
|
"demo",
|
||||||
|
null,
|
||||||
|
xsdFile.getAbsolutePath(),
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
8
|
||||||
|
);
|
||||||
|
|
||||||
|
XomGenerationResult result = service.generate(simpleRoot(), options);
|
||||||
|
File expected = new File(moduleBase, "schema/msgmapper1/demo.xml");
|
||||||
|
|
||||||
|
assertEquals(expected.getCanonicalFile(), result.getMappingXmlFile().getCanonicalFile());
|
||||||
|
assertTrue(expected.isFile());
|
||||||
|
}
|
||||||
|
|
||||||
private XomGenerateOptions options(File xsdFile, File outputFile, String resultMapType, boolean strictMode) {
|
private XomGenerateOptions options(File xsdFile, File outputFile, String resultMapType, boolean strictMode) {
|
||||||
return new XomGenerateOptions(
|
return new XomGenerateOptions(
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -106,10 +106,11 @@ public class XomXmlRendererTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void render_schemaUsesResourceRelativeXsdPathWhenProjectBaseProvided() throws Exception {
|
public void render_schemaUsesModuleRelativeXsdPathWhenXsdIsOutsideResources() throws Exception {
|
||||||
File projectBase = Files.createTempDirectory("xom-render-project").toFile();
|
File projectBase = Files.createTempDirectory("xom-render-project").toFile();
|
||||||
File xsdFile = new File(projectBase, "xsd/camt.053.001.01.xsd");
|
File xsdFile = new File(projectBase, "xsd/camt.053.001.01.xsd");
|
||||||
xsdFile.getParentFile().mkdirs();
|
xsdFile.getParentFile().mkdirs();
|
||||||
|
assertTrue(xsdFile.createNewFile() || xsdFile.isFile());
|
||||||
|
|
||||||
XsdConfigItem root = new XsdConfigItem("Root", false, false, false);
|
XsdConfigItem root = new XsdConfigItem("Root", false, false, false);
|
||||||
root.setJavaProperty("root");
|
root.setJavaProperty("root");
|
||||||
@@ -130,8 +131,7 @@ public class XomXmlRendererTest {
|
|||||||
8
|
8
|
||||||
);
|
);
|
||||||
String xml = renderer.render(root, options);
|
String xml = renderer.render(root, options);
|
||||||
Path resourcePath = projectBase.toPath().resolve("src/main/resources");
|
String expectedRelative = projectBase.toPath().relativize(xsdFile.toPath())
|
||||||
String expectedRelative = resourcePath.relativize(xsdFile.toPath())
|
|
||||||
.toString()
|
.toString()
|
||||||
.replace(File.separatorChar, '/');
|
.replace(File.separatorChar, '/');
|
||||||
|
|
||||||
@@ -139,6 +139,40 @@ public class XomXmlRendererTest {
|
|||||||
assertFalse(xml.contains("reverse-reserved"));
|
assertFalse(xml.contains("reverse-reserved"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void render_schemaUsesResourcesRelativeXsdPathWhenXsdIsUnderResources() throws Exception {
|
||||||
|
File projectBase = Files.createTempDirectory("xom-render-project-resources").toFile();
|
||||||
|
File xsdFile = new File(projectBase, "src/main/resources/xsd/demo.xsd");
|
||||||
|
xsdFile.getParentFile().mkdirs();
|
||||||
|
assertTrue(xsdFile.createNewFile() || xsdFile.isFile());
|
||||||
|
|
||||||
|
XsdConfigItem root = new XsdConfigItem("Root", false, false, false);
|
||||||
|
root.setJavaProperty("root");
|
||||||
|
|
||||||
|
XomGenerateOptions options = new XomGenerateOptions(
|
||||||
|
projectBase.getAbsolutePath(),
|
||||||
|
"message",
|
||||||
|
"schema",
|
||||||
|
"com.demo.Root",
|
||||||
|
xsdFile.getAbsolutePath(),
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
8
|
||||||
|
);
|
||||||
|
String xml = renderer.render(root, options);
|
||||||
|
Path resourcesPath = projectBase.toPath().resolve("src/main/resources");
|
||||||
|
String expectedRelative = resourcesPath.relativize(xsdFile.toPath())
|
||||||
|
.toString()
|
||||||
|
.replace(File.separatorChar, '/');
|
||||||
|
|
||||||
|
assertTrue(xml.contains("xsdPath=\"" + expectedRelative + "\""));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void render_rendersAttributeCarrierNodeWithAttributeChild() {
|
public void render_rendersAttributeCarrierNodeWithAttributeChild() {
|
||||||
XsdConfigItem root = new XsdConfigItem("Document", false, false, false);
|
XsdConfigItem root = new XsdConfigItem("Document", false, false, false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.dmiki.metaplugin.xom.reverse;
|
|||||||
|
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
||||||
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
import com.dmiki.metaplugin.xsd.model.XsdConfigMode;
|
||||||
|
import com.dmiki.metaplugin.xsd.parser.XsdSchemaParser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -16,6 +17,7 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
public class ReservedXomReverseDisplayServiceTest {
|
public class ReservedXomReverseDisplayServiceTest {
|
||||||
private final ReservedXomReverseDisplayService service = new ReservedXomReverseDisplayService();
|
private final ReservedXomReverseDisplayService service = new ReservedXomReverseDisplayService();
|
||||||
|
private final XsdSchemaParser parser = new XsdSchemaParser();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadForDisplay_replaysNodeConfigAndTopFields() throws Exception {
|
public void loadForDisplay_replaysNodeConfigAndTopFields() throws Exception {
|
||||||
@@ -112,6 +114,140 @@ public class ReservedXomReverseDisplayServiceTest {
|
|||||||
assertEquals("id", id.getJavaProperty());
|
assertEquals("id", id.getJavaProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadForDisplay_resolvesModuleRelativeXsdPathOutsideResources() throws Exception {
|
||||||
|
File moduleDir = Files.createTempDirectory("reverse-project-module-relative").toFile();
|
||||||
|
File xsdFile = new File(moduleDir, "schema/inbound/demo.xsd");
|
||||||
|
File xomFile = new File(moduleDir, "schema/msgmapper/demo.xml");
|
||||||
|
|
||||||
|
writeFile(xsdFile, simpleSchema());
|
||||||
|
writeFile(xomFile, moduleRelativeMapping());
|
||||||
|
|
||||||
|
XomReverseDisplayResult result = service.loadForDisplay(xomFile, moduleDir.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(xsdFile.getAbsolutePath(), result.getXsdFile().getAbsolutePath());
|
||||||
|
XsdConfigItem root = result.getRoot();
|
||||||
|
XsdConfigItem id = childByName(root, "Id");
|
||||||
|
assertNotNull(id);
|
||||||
|
assertEquals(XsdConfigMode.KEEP, id.getConfigMode());
|
||||||
|
assertEquals("id", id.getJavaProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToCurrentTree_replaysSelectedSubtreeFromPartialMapping() throws Exception {
|
||||||
|
File projectDir = Files.createTempDirectory("reverse-apply-subtree").toFile();
|
||||||
|
File resourcesDir = new File(projectDir, "src/main/resources");
|
||||||
|
File xsdFile = new File(resourcesDir, "xsd/demo.xsd");
|
||||||
|
File xomFile = new File(resourcesDir, "msgmapper/group.xml");
|
||||||
|
|
||||||
|
writeFile(xsdFile, schemaForReplay());
|
||||||
|
writeFile(xomFile, groupOnlyMapping());
|
||||||
|
|
||||||
|
XsdConfigItem currentRoot = parser.parse(xsdFile);
|
||||||
|
XsdConfigItem group = childByName(currentRoot, "Group");
|
||||||
|
XsdConfigItem msgId = childByName(group, "MsgId");
|
||||||
|
XsdConfigItem creDtTm = childByName(group, "CreDtTm");
|
||||||
|
group.setJavaProperty("staleGroup");
|
||||||
|
creDtTm.setJavaProperty("staleCreDtTm");
|
||||||
|
creDtTm.setConfigMode(XsdConfigMode.KEEP);
|
||||||
|
|
||||||
|
XomReverseDisplayResult result = service.applyToCurrentTree(
|
||||||
|
xomFile,
|
||||||
|
projectDir.getAbsolutePath(),
|
||||||
|
currentRoot,
|
||||||
|
group);
|
||||||
|
|
||||||
|
assertEquals(xsdFile.getAbsolutePath(), result.getXsdFile().getAbsolutePath());
|
||||||
|
assertEquals(XsdConfigMode.PRESERVE_HIERARCHY, group.getConfigMode());
|
||||||
|
assertNull(group.getJavaProperty());
|
||||||
|
assertEquals(XsdConfigMode.KEEP, msgId.getConfigMode());
|
||||||
|
assertEquals("msgId", msgId.getJavaProperty());
|
||||||
|
assertTrue(msgId.isCustomAttributeEnabled());
|
||||||
|
assertTrue(msgId.isPreProcessorEnabled());
|
||||||
|
assertEquals(XsdConfigMode.IGNORE, creDtTm.getConfigMode());
|
||||||
|
assertNull(creDtTm.getJavaProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToCurrentTree_usesClosestAncestorPoolForNestedSelection() throws Exception {
|
||||||
|
File projectDir = Files.createTempDirectory("reverse-apply-nested").toFile();
|
||||||
|
File resourcesDir = new File(projectDir, "src/main/resources");
|
||||||
|
File xsdFile = new File(resourcesDir, "xsd/demo.xsd");
|
||||||
|
File xomFile = new File(resourcesDir, "msgmapper/group-nested.xml");
|
||||||
|
|
||||||
|
writeFile(xsdFile, schemaForReplay());
|
||||||
|
writeFile(xomFile, nestedGroupMapping());
|
||||||
|
|
||||||
|
XsdConfigItem currentRoot = parser.parse(xsdFile);
|
||||||
|
XsdConfigItem group = childByName(currentRoot, "Group");
|
||||||
|
XsdConfigItem msgId = childByName(group, "MsgId");
|
||||||
|
msgId.setJavaProperty("staleMsgId");
|
||||||
|
msgId.setConfigMode(XsdConfigMode.IGNORE);
|
||||||
|
|
||||||
|
service.applyToCurrentTree(xomFile, projectDir.getAbsolutePath(), currentRoot, msgId);
|
||||||
|
|
||||||
|
assertEquals(XsdConfigMode.KEEP, msgId.getConfigMode());
|
||||||
|
assertEquals("msgId", msgId.getJavaProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToCurrentTree_supportsFlattenedLabelsGeneratedForSelectedNode() throws Exception {
|
||||||
|
File projectDir = Files.createTempDirectory("reverse-apply-flatten").toFile();
|
||||||
|
File resourcesDir = new File(projectDir, "src/main/resources");
|
||||||
|
File xsdFile = new File(resourcesDir, "xsd/demo.xsd");
|
||||||
|
File xomFile = new File(resourcesDir, "msgmapper/flat-parent.xml");
|
||||||
|
|
||||||
|
writeFile(xsdFile, schemaForReplay());
|
||||||
|
writeFile(xomFile, flattenedFlatParentMapping());
|
||||||
|
|
||||||
|
XsdConfigItem currentRoot = parser.parse(xsdFile);
|
||||||
|
XsdConfigItem flatParent = childByName(currentRoot, "FlatParent");
|
||||||
|
XsdConfigItem a = childByName(flatParent, "A");
|
||||||
|
XsdConfigItem b = childByName(flatParent, "B");
|
||||||
|
a.setJavaProperty("staleA");
|
||||||
|
b.setJavaProperty("staleB");
|
||||||
|
|
||||||
|
service.applyToCurrentTree(xomFile, projectDir.getAbsolutePath(), currentRoot, flatParent);
|
||||||
|
|
||||||
|
assertEquals(XsdConfigMode.FLATTEN, flatParent.getConfigMode());
|
||||||
|
assertEquals(XsdConfigMode.KEEP, a.getConfigMode());
|
||||||
|
assertEquals("a", a.getJavaProperty());
|
||||||
|
assertEquals(XsdConfigMode.KEEP, b.getConfigMode());
|
||||||
|
assertEquals("b", b.getJavaProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyToCurrentTree_preservesFlattenModeForNestedSelectedNodeInFullMapping() throws Exception {
|
||||||
|
File projectDir = Files.createTempDirectory("reverse-apply-nested-flatten").toFile();
|
||||||
|
File resourcesDir = new File(projectDir, "src/main/resources");
|
||||||
|
File xsdFile = new File(resourcesDir, "xsd/demo.xsd");
|
||||||
|
File xomFile = new File(resourcesDir, "msgmapper/nested-flat-parent.xml");
|
||||||
|
|
||||||
|
writeFile(xsdFile, schemaForReplay());
|
||||||
|
writeFile(xomFile, fullMappingWithFlattenedFlatParent());
|
||||||
|
|
||||||
|
XsdConfigItem currentRoot = parser.parse(xsdFile);
|
||||||
|
XsdConfigItem flatParent = childByName(currentRoot, "FlatParent");
|
||||||
|
XsdConfigItem a = childByName(flatParent, "A");
|
||||||
|
XsdConfigItem b = childByName(flatParent, "B");
|
||||||
|
|
||||||
|
flatParent.setJavaProperty("staleFlatParent");
|
||||||
|
flatParent.setConfigMode(XsdConfigMode.PRESERVE_HIERARCHY_AND_ATTRIBUTES);
|
||||||
|
a.setJavaProperty("staleA");
|
||||||
|
a.setConfigMode(XsdConfigMode.IGNORE);
|
||||||
|
b.setJavaProperty("staleB");
|
||||||
|
b.setConfigMode(XsdConfigMode.IGNORE);
|
||||||
|
|
||||||
|
service.applyToCurrentTree(xomFile, projectDir.getAbsolutePath(), currentRoot, flatParent);
|
||||||
|
|
||||||
|
assertEquals(XsdConfigMode.FLATTEN, flatParent.getConfigMode());
|
||||||
|
assertNull(flatParent.getJavaProperty());
|
||||||
|
assertEquals(XsdConfigMode.KEEP, a.getConfigMode());
|
||||||
|
assertEquals("a", a.getJavaProperty());
|
||||||
|
assertEquals(XsdConfigMode.KEEP, b.getConfigMode());
|
||||||
|
assertEquals("b", b.getJavaProperty());
|
||||||
|
}
|
||||||
|
|
||||||
private void writeFile(File file, String content) throws Exception {
|
private void writeFile(File file, String content) throws Exception {
|
||||||
File parent = file.getParentFile();
|
File parent = file.getParentFile();
|
||||||
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
||||||
@@ -223,4 +359,70 @@ public class ReservedXomReverseDisplayServiceTest {
|
|||||||
+ " </resultMap>\n"
|
+ " </resultMap>\n"
|
||||||
+ "</message>\n";
|
+ "</message>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String moduleRelativeMapping() {
|
||||||
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
+ "<message>\n"
|
||||||
|
+ " <schema id=\"demo\" xsdPath=\"schema/inbound/demo.xsd\"/>\n"
|
||||||
|
+ " <resultMap id=\"demo\" type=\"com.demo.Document\">\n"
|
||||||
|
+ " <result label=\"Document\">\n"
|
||||||
|
+ " <result property=\"id\" label=\"Id\"/>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " </resultMap>\n"
|
||||||
|
+ "</message>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String groupOnlyMapping() {
|
||||||
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
+ "<message>\n"
|
||||||
|
+ " <schema id=\"demo\" xsdPath=\"xsd/demo.xsd\"/>\n"
|
||||||
|
+ " <resultMap id=\"demo\" type=\"com.demo.Document\">\n"
|
||||||
|
+ " <result lable=\"Group\">\n"
|
||||||
|
+ " <result property=\"msgId\" lable=\"MsgId\" attribute=\"sign\" pre-processor=\"trim\"/>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " </resultMap>\n"
|
||||||
|
+ "</message>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String nestedGroupMapping() {
|
||||||
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
+ "<message>\n"
|
||||||
|
+ " <schema id=\"demo\" xsdPath=\"xsd/demo.xsd\"/>\n"
|
||||||
|
+ " <resultMap id=\"demo\" type=\"com.demo.Document\">\n"
|
||||||
|
+ " <result lable=\"Group\">\n"
|
||||||
|
+ " <result property=\"msgId\" lable=\"MsgId\"/>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " </resultMap>\n"
|
||||||
|
+ "</message>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String flattenedFlatParentMapping() {
|
||||||
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
+ "<message>\n"
|
||||||
|
+ " <schema id=\"demo\" xsdPath=\"xsd/demo.xsd\"/>\n"
|
||||||
|
+ " <resultMap id=\"demo\" type=\"com.demo.Document\">\n"
|
||||||
|
+ " <result property=\"a\" lable=\"FlatParent:A\"/>\n"
|
||||||
|
+ " <result property=\"b\" lable=\"FlatParent:B\"/>\n"
|
||||||
|
+ " </resultMap>\n"
|
||||||
|
+ "</message>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fullMappingWithFlattenedFlatParent() {
|
||||||
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
+ "<message>\n"
|
||||||
|
+ " <schema id=\"demo\" xsdPath=\"xsd/demo.xsd\"/>\n"
|
||||||
|
+ " <resultMap id=\"demo\" type=\"com.demo.Document\">\n"
|
||||||
|
+ " <result property=\"document\" lable=\"Document\">\n"
|
||||||
|
+ " <result label=\"Group\">\n"
|
||||||
|
+ " <result property=\"msgId\" label=\"MsgId\"/>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " <result property=\"a\" lable=\"FlatParent:A\"/>\n"
|
||||||
|
+ " <result property=\"b\" lable=\"FlatParent:B\"/>\n"
|
||||||
|
+ " <result property=\"amt\" label=\"Amt\">\n"
|
||||||
|
+ " <result property=\"ccy\" label-attribute=\"Ccy\"/>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " </result>\n"
|
||||||
|
+ " </resultMap>\n"
|
||||||
|
+ "</message>\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.dmiki.metaplugin.xsd.ui;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
public class JavaPackageResolverTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveDefaultPackage_prefersEntityThenDomainThenMessage() throws Exception {
|
||||||
|
File moduleDir = temporaryFolder.newFolder("module-entity-priority");
|
||||||
|
new File(moduleDir, "src/main/java/com/demo/message").mkdirs();
|
||||||
|
new File(moduleDir, "src/main/java/com/demo/domain").mkdirs();
|
||||||
|
new File(moduleDir, "src/main/java/com/demo/entity").mkdirs();
|
||||||
|
|
||||||
|
assertEquals("com.demo.entity", JavaPackageResolver.resolveDefaultPackage(moduleDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveDefaultPackage_infersCommonPackageWhenPreferredPackagesAreMissing() throws Exception {
|
||||||
|
File moduleDir = temporaryFolder.newFolder("module-common-package");
|
||||||
|
writeJavaFile(moduleDir, "src/main/java/com/acme/payment/service/PaymentService.java");
|
||||||
|
writeJavaFile(moduleDir, "src/main/java/com/acme/payment/model/PaymentModel.java");
|
||||||
|
|
||||||
|
assertEquals("com.acme.payment", JavaPackageResolver.resolveDefaultPackage(moduleDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveDefaultPackage_returnsNullWhenModuleIsNotJavaProject() throws Exception {
|
||||||
|
File moduleDir = temporaryFolder.newFolder("module-no-java");
|
||||||
|
new File(moduleDir, "src/main/resources").mkdirs();
|
||||||
|
|
||||||
|
assertNull(JavaPackageResolver.resolveDefaultPackage(moduleDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeJavaFile(File moduleDir, String relativePath) throws Exception {
|
||||||
|
File javaFile = new File(moduleDir, relativePath);
|
||||||
|
File parent = javaFile.getParentFile();
|
||||||
|
if (parent != null) {
|
||||||
|
parent.mkdirs();
|
||||||
|
}
|
||||||
|
Files.write(javaFile.toPath(), "class Test {}".getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.dmiki.metaplugin.xsd.ui;
|
||||||
|
|
||||||
|
import com.dmiki.metaplugin.xsd.model.XsdConfigItem;
|
||||||
|
import com.intellij.ui.treeStructure.treetable.TreeTable;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class XsdConfigDialogTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldRenderConfigField_hidesRendererShellForRootRow() {
|
||||||
|
XsdConfigItem root = new XsdConfigItem("Document", false, false, false);
|
||||||
|
root.getChildren().add(new XsdConfigItem("GrpHdr", true, false, false));
|
||||||
|
|
||||||
|
DefaultMutableTreeNode swingRoot = XsdTreeTableModel.toSwingTree(root);
|
||||||
|
TreeTable table = new TreeTable(new XsdTreeTableModel(swingRoot));
|
||||||
|
table.getTree().setRootVisible(true);
|
||||||
|
table.getTree().expandRow(0);
|
||||||
|
|
||||||
|
assertFalse(XsdConfigDialog.shouldRenderConfigField(table, 0));
|
||||||
|
assertTrue(XsdConfigDialog.shouldRenderConfigField(table, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toggleCheckBoxSize_usesLargestAxisAndMinimumFloor() {
|
||||||
|
assertEquals(new Dimension(20, 20), XsdConfigDialog.toggleCheckBoxSize(new Dimension(18, 18)));
|
||||||
|
assertEquals(new Dimension(22, 22), XsdConfigDialog.toggleCheckBoxSize(new Dimension(22, 18)));
|
||||||
|
assertEquals(new Dimension(20, 20), XsdConfigDialog.toggleCheckBoxSize(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user