编写自己的代码生成工具二:解析配置文件

在开始具体的编码之前先来看一下项目的主要结构图:

代码的主要结构如上图所示,下面对每一部分逐一介绍:

  • EasyCodeStarter 工具启动的入口,main方法声明类,主要调用XmlParser解析配置文件,GenerationOrganizer进行代码生成。
  • XmlParser 配置文件的解析类,所有解析后的信息都将被存放到EasyCodeContext类中保存。
  • GenerationOrganizer具体的代码生成工具组织者,外围的一些操作如数据库连接的获取、关闭等统一由该类完成。
  • EasyCodeContext 存放配置文件解析后的信息,供代码生成时获取使用,该类没有任务业务逻辑。
  • DBUtils 主要负责数据库连接的获取和关闭。
  • DatabaseProvider 数据库提供者接口,实现表的元信息查询,可以有不同的数据库实现。
  • EasyCodeGenerator 具体的代码生成接口,里面只有一个方法doGenerate。
  • AbstractEasyCodeGenerator 代码生成接口的抽象实现,使用了模板模式。例如每一份代码的生成都需要在最后创建文件,像这类通用的操作在这里完成,具体的代码内容构建则由继承它的子类完成,实现它的抽象方法generate。另外在这里进行代码构建之前会先调用插件(虽然这功能比较鸡肋)。
  • DefaultCodeGenerator 默认提供的代码生成内容构建实现,常规的代码生成需求一般都可以满足。
  • FileUtils 代码文件的创建。

了解了它的结构,对于下一步要进行什么应该很清楚了吧。当然是进行配置文件的解析,创建我们的XmlParser类,因为一切任务的开始都要依赖于它。

在进行解析之前有必要说明一下,配置文件是xml格式的,目前对于xml的解析强大的第三方实现有很多,但是鉴于代码生成工具本身的特殊性以及它的使用场景,我们并不需要多么强大的功能,相反我们希望它依赖的包越少越好,因此这里使用了jdk自带的一些简单工具类,并没有使用第三方的实现如dom4j、Digester等。

下面是XmlParser类的源码:

   
   
   
   
  1. /**
  2. * XML配置文件解析类
  3. *
  4. * User: liyd
  5. * Date: 13-11-20
  6. * Time: 下午2:53
  7. */
  8. public class XmlParser {
  9. /** 日志对象 */
  10. private static final Logger LOG = LoggerFactory.getLogger(XmlParser.class);
  11. /** 文档构建对象 */
  12. private static DocumentBuilder documentBuilder;
  13. /**
  14. * 获取doc builder
  15. */
  16. private synchronized static DocumentBuilder getDocBuilder() {
  17. try {
  18. if (documentBuilder == null) {
  19. //创建xml解析doc对象
  20. documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  21. }
  22. return documentBuilder;
  23. } catch (ParserConfigurationException e) {
  24. LOG.error("创建xml解析对象失败", e);
  25. throw new EasyCodeException("创建xml解析对象失败");
  26. }
  27. }
  28. /**
  29. * 解析配置文件
  30. *
  31. * @param configFile
  32. */
  33. public static void parseConfigXml(String configFile) {
  34. LOG.info("开始解析配置文件{}", configFile);
  35. Document doc;
  36. try {
  37. //创建xml解析doc对象
  38. doc = getDocBuilder().parse(
  39. Thread.currentThread().getContextClassLoader().getResourceAsStream(configFile));
  40. //解析常量
  41. Map<String, String> constantMap = parseConstant(doc);
  42. EasyCodeContext.addConstant(constantMap);
  43. //解析配置文件中的常量使用
  44. String context = VelocityUtils.parseTemplate(configFile,
  45. EasyCodeContext.getAllConstant());
  46. //重新构建处理过的配置文件doc对象
  47. doc = getDocBuilder().parse(new ByteArrayInputStream(context.getBytes()));
  48. NodeList childNodes = doc.getDocumentElement().getChildNodes();
  49. for (int i = 0; i < childNodes.getLength(); i++) {
  50. Node node = childNodes.item(i);
  51. if (node.getNodeType() == Node.TEXT_NODE) {
  52. continue;
  53. }
  54. if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.JDBC_TAG)) {
  55. //解析数据库配置
  56. String[] strings = parseJdbcConfig(node);
  57. EasyCodeContext.addJdbcConfig(strings[0], strings[1]);
  58. } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.CONVERT_TAG)) {
  59. //解析数据转换配置
  60. String[] strings = parseConvertConfig(node);
  61. ConvertType convertType = new ConvertType();
  62. convertType.setDbType(strings[0]);
  63. convertType.setJdbcType(strings[1]);
  64. convertType.setJavaClass(strings[2]);
  65. EasyCodeContext.addDataConvertType(strings[0], convertType);
  66. } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.INCLUDE_TAG)) {
  67. //解析include标签
  68. parseIncludeFile(node);
  69. } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TABLE_TAG)) {
  70. //解析表配置
  71. Table table = parseTableConfig(node);
  72. EasyCodeContext.addTable(table.getName(), table);
  73. } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TASKS_TAG)) {
  74. //解析任务配置
  75. Map<String, Task> taskMap = parseTaskConfig(node);
  76. EasyCodeContext.addTask(taskMap);
  77. }
  78. }
  79. } catch (Exception e) {
  80. LOG.error("配置文件解析失败", e);
  81. throw new EasyCodeException("配置文件解析失败");
  82. }
  83. }
  84. /**
  85. * 解析任务配置
  86. *
  87. * @param node the node
  88. * @return the map
  89. */
  90. private static Map<String, Task> parseTaskConfig(Node node) {
  91. NodeList taskList = node.getChildNodes();
  92. if (taskList == null || taskList.getLength() == 0) {
  93. LOG.info("没有定义代码生成任务");
  94. throw new EasyCodeException("没有定义代码生成任务");
  95. }
  96. Map<String, Task> taskMap = new HashMap<String, Task>();
  97. for (int i = 0; i < taskList.getLength(); i++) {
  98. Node taskNode = taskList.item(i);
  99. if (taskNode.getNodeType() == Node.TEXT_NODE) {
  100. continue;
  101. }
  102. NamedNodeMap namedNodeMap = taskNode.getAttributes();
  103. String taskName = namedNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  104. String clazz = namedNodeMap.getNamedItem(XmlTag.CLASS_ATTR).getNodeValue();
  105. Task task = new Task();
  106. task.setName(taskName);
  107. task.setClazz(clazz);
  108. task.setClassInstance(ClassUtils.getGeneratorInstance(clazz));
  109. //解析子标签配置信息
  110. parseChildConfig(taskNode, task);
  111. taskMap.put(taskName, task);
  112. }
  113. return taskMap;
  114. }
  115. /**
  116. * 解析属性
  117. *
  118. * @param node the node
  119. * @param task the task
  120. */
  121. private static void parseChildConfig(Node node, Task task) {
  122. NodeList nodeList = node.getChildNodes();
  123. Map<String, Property> propertyMap = new HashMap<String, Property>();
  124. Map<String, EasyCodePlugin> pluginMap = new HashMap<String, EasyCodePlugin>();
  125. task.setProperties(propertyMap);
  126. task.setPluginMap(pluginMap);
  127. if (nodeList == null || nodeList.getLength() == 0) {
  128. return;
  129. }
  130. //处理属性配置信息
  131. for (int i = 0; i < nodeList.getLength(); i++) {
  132. Node childNode = nodeList.item(i);
  133. if (childNode.getNodeType() == Node.TEXT_NODE) {
  134. continue;
  135. }
  136. NamedNodeMap propertyNodeMap = childNode.getAttributes();
  137. //属性名称
  138. String propertyName = propertyNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  139. //属性值
  140. String propertyValue = propertyNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
  141. if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PLUGIN_TAG)) {
  142. //插件对象
  143. EasyCodePlugin pluginInstance = ClassUtils.getPluginInstance(propertyValue);
  144. pluginMap.put(propertyName, pluginInstance);
  145. } else if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PROPERTY_TAG)) {
  146. //属性对象
  147. Property property = new Property();
  148. property.setName(propertyName);
  149. property.setValue(propertyValue);
  150. propertyMap.put(propertyName, property);
  151. }
  152. }
  153. }
  154. /**
  155. * 解析表配置
  156. *
  157. * @param node the node
  158. * @return the table
  159. */
  160. private static Table parseTableConfig(Node node) {
  161. NamedNodeMap tableNodeMap = node.getAttributes();
  162. //表名称
  163. String tableName = tableNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  164. String tableDesc = tableNodeMap.getNamedItem(XmlTag.DESC_ATTR).getNodeValue();
  165. if (StringUtils.isBlank(tableName)) {
  166. LOG.info("没有指定表名");
  167. throw new EasyCodeException("没有指定表名");
  168. }
  169. Table table = new Table();
  170. table.setName(tableName);
  171. table.setDesc(tableDesc);
  172. //解析表的子属性配置信息
  173. parseTableChildConfig(node, table);
  174. return table;
  175. }
  176. /**
  177. * 解析表的子属性配置信息
  178. *
  179. * @param tableNode
  180. * @param table
  181. */
  182. private static void parseTableChildConfig(Node tableNode, Table table) {
  183. //表的子标签列表
  184. NodeList childNodes = tableNode.getChildNodes();
  185. if (childNodes == null || childNodes.getLength() == 0) {
  186. LOG.info("没有配置表的具体生成信息");
  187. return;
  188. }
  189. for (int i = 0; i < childNodes.getLength(); i++) {
  190. //子节点
  191. Node node = childNodes.item(i);
  192. String nodeName = node.getNodeName();
  193. if (StringUtils.equalsIgnoreCase(XmlTag.TASKS_TAG, nodeName)) {
  194. parseTasks(node, table);
  195. }
  196. }
  197. }
  198. /**
  199. * 解析表的tasks
  200. *
  201. * @param tasksNode
  202. * @param table
  203. */
  204. private static void parseTasks(Node tasksNode, Table table) {
  205. //task子标签
  206. NodeList taskNodes = tasksNode.getChildNodes();
  207. if (taskNodes == null || taskNodes.getLength() == 0) {
  208. LOG.info("没有配置表的任务信息");
  209. return;
  210. }
  211. for (int i = 0; i < taskNodes.getLength(); i++) {
  212. //节点
  213. Node node = taskNodes.item(i);
  214. if (node.getNodeType() == Node.TEXT_NODE) {
  215. continue;
  216. }
  217. String nodeName = node.getNodeName();
  218. NamedNodeMap columnNodeMap = node.getAttributes();
  219. String taskName = columnNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  220. if (StringUtils.equalsIgnoreCase(XmlTag.TASK_TAG, nodeName)) {
  221. table.addTask(taskName);
  222. }
  223. }
  224. }
  225. /**
  226. * 解析数据库配置
  227. *
  228. * @param node the node
  229. */
  230. private static String[] parseJdbcConfig(Node node) {
  231. NamedNodeMap dbNodeMap = node.getAttributes();
  232. String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  233. String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
  234. return new String[] { name, value };
  235. }
  236. /**
  237. * 解析数据转换配置
  238. *
  239. * @param node
  240. * @return
  241. */
  242. private static String[] parseConvertConfig(Node node) {
  243. NamedNodeMap dbNodeMap = node.getAttributes();
  244. String dbType = dbNodeMap.getNamedItem(XmlTag.DB_TYPE_ATTR).getNodeValue();
  245. String jdbcType = dbNodeMap.getNamedItem(XmlTag.JDBC_TYPE_ATTR).getNodeValue();
  246. String javaType = dbNodeMap.getNamedItem(XmlTag.JAVA_TYPE_ATTR).getNodeValue();
  247. return new String[] { dbType, jdbcType, javaType };
  248. }
  249. /**
  250. * 解析常量配置
  251. *
  252. * @param doc
  253. */
  254. private static Map<String, String> parseConstant(Document doc) {
  255. Map<String, String> constantMap = new HashMap<String, String>();
  256. NodeList jdbcConfigList = doc.getElementsByTagName(XmlTag.CONSTANT_TAG);
  257. if (jdbcConfigList == null || jdbcConfigList.getLength() == 0) {
  258. return constantMap;
  259. }
  260. for (int i = 0; i < jdbcConfigList.getLength(); i++) {
  261. Node dbNode = jdbcConfigList.item(i);
  262. NamedNodeMap dbNodeMap = dbNode.getAttributes();
  263. String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
  264. String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
  265. constantMap.put(name, value);
  266. }
  267. return constantMap;
  268. }
  269. /**
  270. * 解析include的xml
  271. *
  272. * @param node
  273. */
  274. private static void parseIncludeFile(Node node) {
  275. //处理include文件
  276. NamedNodeMap includeNodeMap = node.getAttributes();
  277. String includeFile = includeNodeMap.getNamedItem(XmlTag.FILE_ATTR).getNodeValue();
  278. //递归解析
  279. parseConfigXml(includeFile);
  280. }
  281. }

需要说明的是常量的解析因为要支持常量的定义使用,所以这里是先解析出常量,之后用velocity引擎对xml进行一次常量解析并返回解析后的内容,把该内容构建成Document对象后进行具体的任务解析。

最终的解析结果都存放在EasyCodeContext类中,以供后面代码生成时取用,这中间建立了几个实体对象Table、Column、Property、Task、ConvertType用来方便保存数据。

解析的工作完成,下面就要编写代码生成的组织者GenerationOrganizer了,待续,,,

你可能感兴趣的:(java,代码生成)