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

分类: 工作记录 0人评论 selfly 1年前发布

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

20140221105907

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

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

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

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

下面是XmlParser类的源码:

/**
 * XML配置文件解析类
 *
 * User: liyd
 * Date: 13-11-20
 * Time: 下午2:53
 */
public class XmlParser {
    /** 日志对象 */
    private static final Logger    LOG = LoggerFactory.getLogger(XmlParser.class);
    /** 文档构建对象 */
    private static DocumentBuilder documentBuilder;
    /**
     * 获取doc builder
     */
    private synchronized static DocumentBuilder getDocBuilder() {
        try {
            if (documentBuilder == null) {
                //创建xml解析doc对象
                documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            }
            return documentBuilder;
        } catch (ParserConfigurationException e) {
            LOG.error("创建xml解析对象失败", e);
            throw new EasyCodeException("创建xml解析对象失败");
        }
    }
    /**
     * 解析配置文件
     *
     * @param configFile
     */
    public static void parseConfigXml(String configFile) {
        LOG.info("开始解析配置文件{}", configFile);
        Document doc;
        try {
            //创建xml解析doc对象
            doc = getDocBuilder().parse(
                Thread.currentThread().getContextClassLoader().getResourceAsStream(configFile));
            //解析常量
            Map<String, String> constantMap = parseConstant(doc);
            EasyCodeContext.addConstant(constantMap);
            //解析配置文件中的常量使用
            String context = VelocityUtils.parseTemplate(configFile,
                EasyCodeContext.getAllConstant());
            //重新构建处理过的配置文件doc对象
            doc = getDocBuilder().parse(new ByteArrayInputStream(context.getBytes()));
            NodeList childNodes = doc.getDocumentElement().getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node node = childNodes.item(i);
                if (node.getNodeType() == Node.TEXT_NODE) {
                    continue;
                }
                if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.JDBC_TAG)) {
                    //解析数据库配置
                    String[] strings = parseJdbcConfig(node);
                    EasyCodeContext.addJdbcConfig(strings[0], strings[1]);
                } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.CONVERT_TAG)) {
                    //解析数据转换配置
                    String[] strings = parseConvertConfig(node);
                    ConvertType convertType = new ConvertType();
                    convertType.setDbType(strings[0]);
                    convertType.setJdbcType(strings[1]);
                    convertType.setJavaClass(strings[2]);
                    EasyCodeContext.addDataConvertType(strings[0], convertType);
                } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.INCLUDE_TAG)) {
                    //解析include标签
                    parseIncludeFile(node);
                } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TABLE_TAG)) {
                    //解析表配置
                    Table table = parseTableConfig(node);
                    EasyCodeContext.addTable(table.getName(), table);
                } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TASKS_TAG)) {
                    //解析任务配置
                    Map<String, Task> taskMap = parseTaskConfig(node);
                    EasyCodeContext.addTask(taskMap);
                }
            }
        } catch (Exception e) {
            LOG.error("配置文件解析失败", e);
            throw new EasyCodeException("配置文件解析失败");
        }
    }
    /**
     * 解析任务配置
     *
     * @param node the node
     * @return the map
     */
    private static Map<String, Task> parseTaskConfig(Node node) {
        NodeList taskList = node.getChildNodes();
        if (taskList == null || taskList.getLength() == 0) {
            LOG.info("没有定义代码生成任务");
            throw new EasyCodeException("没有定义代码生成任务");
        }
        Map<String, Task> taskMap = new HashMap<String, Task>();
        for (int i = 0; i < taskList.getLength(); i++) {
            Node taskNode = taskList.item(i);
            if (taskNode.getNodeType() == Node.TEXT_NODE) {
                continue;
            }
            NamedNodeMap namedNodeMap = taskNode.getAttributes();
            String taskName = namedNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
            String clazz = namedNodeMap.getNamedItem(XmlTag.CLASS_ATTR).getNodeValue();
            Task task = new Task();
            task.setName(taskName);
            task.setClazz(clazz);
            task.setClassInstance(ClassUtils.getGeneratorInstance(clazz));
            //解析子标签配置信息
            parseChildConfig(taskNode, task);
            taskMap.put(taskName, task);
        }
        return taskMap;
    }
    /**
     * 解析属性
     *
     * @param node the node
     * @param task the task
     */
    private static void parseChildConfig(Node node, Task task) {
        NodeList nodeList = node.getChildNodes();
        Map<String, Property> propertyMap = new HashMap<String, Property>();
        Map<String, EasyCodePlugin> pluginMap = new HashMap<String, EasyCodePlugin>();
        task.setProperties(propertyMap);
        task.setPluginMap(pluginMap);
        if (nodeList == null || nodeList.getLength() == 0) {
            return;
        }
        //处理属性配置信息
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node childNode = nodeList.item(i);
            if (childNode.getNodeType() == Node.TEXT_NODE) {
                continue;
            }
            NamedNodeMap propertyNodeMap = childNode.getAttributes();
            //属性名称
            String propertyName = propertyNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
            //属性值
            String propertyValue = propertyNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
            if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PLUGIN_TAG)) {
                //插件对象
                EasyCodePlugin pluginInstance = ClassUtils.getPluginInstance(propertyValue);
                pluginMap.put(propertyName, pluginInstance);
            } else if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PROPERTY_TAG)) {
                //属性对象
                Property property = new Property();
                property.setName(propertyName);
                property.setValue(propertyValue);
                propertyMap.put(propertyName, property);
            }
        }
    }
    /**
     * 解析表配置
     *
     * @param node the node
     * @return the table
     */
    private static Table parseTableConfig(Node node) {
        NamedNodeMap tableNodeMap = node.getAttributes();
        //表名称
        String tableName = tableNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
        String tableDesc = tableNodeMap.getNamedItem(XmlTag.DESC_ATTR).getNodeValue();
        if (StringUtils.isBlank(tableName)) {
            LOG.info("没有指定表名");
            throw new EasyCodeException("没有指定表名");
        }
        Table table = new Table();
        table.setName(tableName);
        table.setDesc(tableDesc);
        //解析表的子属性配置信息
        parseTableChildConfig(node, table);
        return table;
    }
    /**
     * 解析表的子属性配置信息
     * 
     * @param tableNode
     * @param table
     */
    private static void parseTableChildConfig(Node tableNode, Table table) {
        //表的子标签列表
        NodeList childNodes = tableNode.getChildNodes();
        if (childNodes == null || childNodes.getLength() == 0) {
            LOG.info("没有配置表的具体生成信息");
            return;
        }
        for (int i = 0; i < childNodes.getLength(); i++) {
            //子节点
            Node node = childNodes.item(i);
            String nodeName = node.getNodeName();
            if (StringUtils.equalsIgnoreCase(XmlTag.TASKS_TAG, nodeName)) {
                parseTasks(node, table);
            }
        }
    }
    /**
     * 解析表的tasks
     * 
     * @param tasksNode
     * @param table
     */
    private static void parseTasks(Node tasksNode, Table table) {
        //task子标签
        NodeList taskNodes = tasksNode.getChildNodes();
        if (taskNodes == null || taskNodes.getLength() == 0) {
            LOG.info("没有配置表的任务信息");
            return;
        }
        for (int i = 0; i < taskNodes.getLength(); i++) {
            //节点
            Node node = taskNodes.item(i);
            if (node.getNodeType() == Node.TEXT_NODE) {
                continue;
            }
            String nodeName = node.getNodeName();
            NamedNodeMap columnNodeMap = node.getAttributes();
            String taskName = columnNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
            if (StringUtils.equalsIgnoreCase(XmlTag.TASK_TAG, nodeName)) {
                table.addTask(taskName);
            }
        }
    }
    /**
     * 解析数据库配置
     *
     * @param node the node
     */
    private static String[] parseJdbcConfig(Node node) {
        NamedNodeMap dbNodeMap = node.getAttributes();
        String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
        String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
        return new String[] { name, value };
    }
    /**
     * 解析数据转换配置
     * 
     * @param node
     * @return
     */
    private static String[] parseConvertConfig(Node node) {
        NamedNodeMap dbNodeMap = node.getAttributes();
        String dbType = dbNodeMap.getNamedItem(XmlTag.DB_TYPE_ATTR).getNodeValue();
        String jdbcType = dbNodeMap.getNamedItem(XmlTag.JDBC_TYPE_ATTR).getNodeValue();
        String javaType = dbNodeMap.getNamedItem(XmlTag.JAVA_TYPE_ATTR).getNodeValue();
        return new String[] { dbType, jdbcType, javaType };
    }
    /**
     * 解析常量配置
     *
     * @param doc
     */
    private static Map<String, String> parseConstant(Document doc) {
        Map<String, String> constantMap = new HashMap<String, String>();
        NodeList jdbcConfigList = doc.getElementsByTagName(XmlTag.CONSTANT_TAG);
        if (jdbcConfigList == null || jdbcConfigList.getLength() == 0) {
            return constantMap;
        }
        for (int i = 0; i < jdbcConfigList.getLength(); i++) {
            Node dbNode = jdbcConfigList.item(i);
            NamedNodeMap dbNodeMap = dbNode.getAttributes();
            String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue();
            String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue();
            constantMap.put(name, value);
        }
        return constantMap;
    }
    /**
     * 解析include的xml
     *
     * @param node
     */
    private static void parseIncludeFile(Node node) {
        //处理include文件
        NamedNodeMap includeNodeMap = node.getAttributes();
        String includeFile = includeNodeMap.getNamedItem(XmlTag.FILE_ATTR).getNodeValue();
        //递归解析
        parseConfigXml(includeFile);
    }
}

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

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

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