应用背景
Java开发中,由于项目模式为常见的贫血模型,由于业务性质,实体类数量与内部属性都十分多。如果手动定义实体类,以及手动写set方法,十分麻烦。由于项目的详设文档中,实体类的定义用word文档的表格进行了汇总,我们可以根据表格结构作为输入,通过代码,将表格结构转为代码结构。由于实体类内部的定义有层级划分,在注入实体属性值的时候,注入的过程是一个栈递归的模型。依据此性质,我编写了如下的程序代码,帮助我减轻无聊且重复的开发内容。
代码组装逻辑类
package BinaryTree;
import java.io.*;
import java.util.Stack;
/**
* 开发项目中,自动化生成setter的方法
*
* @author cqf
* @date 2021/11/9 19:24:22
*/
public class Setter {
// 属性栈
private Stack<AttrInf> stack = new Stack<>();
public void getSetters(String inputPath, String outputPath, String bo) {
File file = new File(inputPath);
try (BufferedReader reader = new BufferedReader(new FileReader(file));
FileWriter fileWriter = new FileWriter(outputPath)) {
fileWriter.append(bo).append("BO ").append(bo.toLowerCase()).append(" = new ").append(bo).append("BO();\n");
String line = reader.readLine();
String nextLine = reader.readLine();
// 按行读取文件
while (line != null && !"".equals(line)) {
stack.push(getAttrInf(line));
fileWriter.append(getContentFromAttrInf(nextLine, bo));
line = nextLine;
nextLine = reader.readLine();
}
// 清空栈,处理残余的set代码
fileWriter.append(popStack(new AttrInf.AttrInfBuilder().level(0).build(), bo));
} catch (IOException e) {
e.printStackTrace();
}
}
// 保证输入字符串非空情况下,将其解析为AttrInf
private AttrInf getAttrInf(String line) {
AttrInf attrInf = new AttrInf();
String chinese = line.substring(0, line.indexOf('\t'));
int level = 0;
while (chinese.contains("--")) {
level++;
chinese = chinese.substring(2);
}
attrInf.setChinese(chinese);
attrInf.setLevel(level);
String english = line.substring(line.indexOf('\t') + 1);
english = english.substring(1, english.length() - 2);
String _english = (char) (english.charAt(0) - 'A' + 'a') + english.substring(1);
attrInf.setEnglish(english);
attrInf.setEnglish(_english);
return attrInf;
}
private String getContentFromAttrInf(String nextLine, String bo) {
StringBuilder sb = new StringBuilder();
AttrInf attrInf = stack.pop();
String parentAttr;
// 根据不同情况,组装代码行
if (stack.isEmpty()) {
// 当前属性是首个属性行时(默认首个入栈元素是有层级结构的属性)
sb.append(attrInf.getEnglish()).append("BO ").append(attrInf.get_english()).append(" = new ")
.append(attrInf.getEnglish()).append("BO();// ").append(attrInf.getChinese()).append("\n");
} else if (stack.peek().getLevel() == attrInf.getLevel()) {
// 当前属性与上一属性的层级深度相同
parentAttr = stack.peek().getParentAttr();
attrInf.setParentAttr(parentAttr);
sb.append(parentAttr).append(".set").append(attrInf.getEnglish())
.append("();// ").append(attrInf.getChinese()).append("\n");
} else if (stack.peek().getLevel() + 1 == attrInf.getLevel()) {
// 当前属性是上一属性的直系下级属性。需通过nextLine信息,判断当前属性是否是叶子属性
if (isNewAttr(attrInf, nextLine)) {
attrInf.setParentAttr(stack.peek().getEnglish());
sb.append(attrInf.getEnglish()).append("BO ").append(attrInf.get_english()).append(" = new ")
.append(attrInf.getEnglish()).append("BO();// ").append(attrInf.getChinese()).append("\n");
} else {
parentAttr = stack.peek().get_english();
attrInf.setParentAttr(parentAttr);
sb.append(parentAttr).append(".set").append(attrInf.getEnglish()).
append("();// ").append(attrInf.getChinese()).append("\n");
}
} else if (stack.peek().getLevel() > attrInf.getLevel()) {
// 当前属性层级深度低于栈顶元素,栈顶元素需要出栈
sb.append(popStack(attrInf, bo));
String parent = stack.isEmpty() ? bo : stack.peek().getParentAttr();
attrInf.setParentAttr(parent);
sb.append(attrInf.getEnglish()).append("BO ").append(attrInf.get_english()).append(" = new ")
.append(attrInf.getEnglish()).append("BO();// ").append(attrInf.getChinese()).append("\n");
}
stack.push(attrInf);
return sb.toString();
}
// 判断attrInf对应的属性,层级高度是否正好比下一属性层级高一级
private boolean isNewAttr(AttrInf attrInf, String nextLine) {
return nextLine != null && attrInf.getLevel() + 1
== (nextLine.contains("-") ? (nextLine.lastIndexOf("-") + 1) / 2 : 0);
}
// 出栈,输出祖先的set方法
private String popStack(AttrInf attrInf, String bo) {
StringBuilder sb = new StringBuilder();
AttrInf tempAttrInf = stack.pop();
while (!stack.isEmpty() && tempAttrInf.getLevel() == stack.peek().getLevel()) { tempAttrInf = stack.pop(); }
if (!stack.isEmpty()) {
// 经过上面的循环出栈处理后,当前栈顶元素是attrInf属性的直系上级属性
AttrInf parent = stack.peek();
String parentAttr = parent.getParentAttr() == null ? bo.toLowerCase() : parent.getParentAttr();
sb.append(parentAttr).append(".set").append(parent.getEnglish()).append("(")
.append(parent.get_english()).append(");\n");
sb.append(bo.toLowerCase().equals(parentAttr) ? "\n" : "");
}
return !stack.isEmpty() && stack.peek().getLevel() > attrInf.getLevel()
? sb.append(popStack(attrInf, bo)).toString() : sb.toString();
}
}
AttrInf类的定义
package BinaryTree;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 属性信息数据结构
*
* @author cqf
* @date 2021/11/9 19:25:59
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AttrInf {
private String english; // 大写字母开头的英文属性
private String _english; // 小写字母开头的英文属性
private String chinese; // 中文属性
private String parentAttr; // 当前属性上级属性的变量名
private int level; // 当前属性所属层级结构中的层的深度
}
input.txt
这是详设文档中复制出来的实体类结构示例。
报文标识 <MsgId/>
--报文标识号 <Id/>
--报文时间 <CreDtTm/>
登记信息 <RgtInf/>
--登记机构参与者代码 <RgtBrId/>
--登记申请日期 <RgtDt/>
票据信息集合 <CdInfs/>
--票据信息 <CdInf/>
----票据种类 <CdType/>
----票据号码 <CdNo/>
----票据金额 <CdAmt/>
----票据到期日 <DueDt/>
----承兑人开户行行号 <AcptAcctSvcr/>
--解除止付信息 <RlvStpPayInf/>
----解除止付日期 <RlvStpPayDt/>
----解除止付原因 <RlvStpPayRsn/>
----解除止付类型 <RlvStpPayType/>
----备注 <Note/>
--影像信息集合 <ImgInfs/>
----影像信息 <ImgInf/>
------影像批次号 <ImgBtNo/>
output.txt
这是程序输出结果。针对输出结果,仅需要将对应的值填入空缺处即可,极大减轻了重复工作量,且防止出现手动设定过程中的误差。但对于实体类内部是List<T>类型的成员,仍需要手动处理。
NCP001BO ncp001 = new NCP001BO();
msgIdBO null = new msgIdBO();// 报文标识
null.setid();// 报文标识号
null.setcreDtTm();// 报文时间
ncp001.setmsgId(null);
rgtInfBO null = new rgtInfBO();// 登记信息
null.setrgtBrId();// 登记机构参与者代码
null.setrgtDt();// 登记申请日期
ncp001.setrgtInf(null);
cdInfsBO null = new cdInfsBO();// 票据信息集合
cdInfBO null = new cdInfBO();// 票据信息
null.setcdType();// 票据种类
null.setcdNo();// 票据号码
null.setcdAmt();// 票据金额
null.setdueDt();// 票据到期日
null.setacptAcctSvcr();// 承兑人开户行行号
cdInfs.setcdInf(null);
rlvStpPayInfBO null = new rlvStpPayInfBO();// 解除止付信息
null.setrlvStpPayDt();// 解除止付日期
null.setrlvStpPayRsn();// 解除止付原因
null.setrlvStpPayType();// 解除止付类型
null.setnote();// 备注
cdInfs.setrlvStpPayInf(null);
imgInfsBO null = new imgInfsBO();// 影像信息集合
imgInfBO null = new imgInfBO();// 影像信息
null.setimgBtNo();// 影像批次号
imgInfs.setimgInf(null);
cdInfs.setimgInfs(null);
ncp001.setcdInfs(null);