/*
 * Copyright (c) 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.*;
import com.itfsw.mybatis.generator.plugins.utils.hook.ISelectOneByExamplePluginHook;
import com.itfsw.mybatis.generator.plugins.utils.hook.ISelectSelectivePluginHook;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;

import java.util.List;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
 * ---------------------------------------------------------------------------
 *
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2017/6/29 13:34
 * ---------------------------------------------------------------------------
 */
public class SelectSelectivePlugin extends BasePlugin implements ISelectOneByExamplePluginHook {
    public static final String METHOD_SELECT_BY_EXAMPLE_SELECTIVE = "selectByExampleSelective";
    public static final String METHOD_SELECT_BY_PRIMARY_KEY_SELECTIVE = "selectByPrimaryKeySelective";
    public static final String METHOD_SELECT_ONE_BY_EXAMPLE_SELECTIVE = "selectOneByExampleSelective";
    public static final String ID_FOR_PROPERTY_BASED_RESULT_MAP = "BasePropertyResultMap";
    private XmlElement selectByExampleSelectiveEle;
    private XmlElement selectByPrimaryKeySelectiveEle;

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param warnings
     * @return
     */
    @Override
    public boolean validate(List<String> warnings) {

        // 插件使用前提是使用了ModelColumnPlugin插件
        if (!PluginTools.checkDependencyPlugin(getContext(), ModelColumnPlugin.class)) {
            warnings.add("itfsw:插件" + this.getClass().getTypeName() + "插件需配合com.itfsw.mybatis.generator.plugins.ModelColumnPlugin插件使用！");
            return false;
        }

        return super.validate(warnings);
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param introspectedTable
     */
    @Override
    public void initialized(IntrospectedTable introspectedTable) {
        super.initialized(introspectedTable);

        // bug:26,27
        this.selectByExampleSelectiveEle = null;
        this.selectByPrimaryKeySelectiveEle = null;
    }

    // =========================================== client 方法生成 ===================================================

    @Override
    public boolean clientSelectByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        FormatTools.addMethodWithBestPosition(interfaze, this.replaceMethodWithSelective(
                method,
                METHOD_SELECT_BY_EXAMPLE_SELECTIVE,
                "@Param(\"example\")",
                introspectedTable
        ));
        return super.clientSelectByExampleWithBLOBsMethodGenerated(method, interfaze, introspectedTable);
    }

    @Override
    public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        if (!introspectedTable.hasBLOBColumns()) {
            FormatTools.addMethodWithBestPosition(interfaze, this.replaceMethodWithSelective(
                    method,
                    METHOD_SELECT_BY_EXAMPLE_SELECTIVE,
                    "@Param(\"example\")",
                    introspectedTable
            ));
        }
        return super.clientSelectByExampleWithoutBLOBsMethodGenerated(method, interfaze, introspectedTable);
    }

    @Override
    public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        if (introspectedTable.getRules().generatePrimaryKeyClass()) {
            FormatTools.addMethodWithBestPosition(interfaze, this.replaceMethodWithSelective(
                    method,
                    METHOD_SELECT_BY_PRIMARY_KEY_SELECTIVE,
                    "@Param(\"record\")",
                    introspectedTable
            ));
        } else {
            Method withSelective = JavaElementTools.clone(method);
            FormatTools.replaceGeneralMethodComment(commentGenerator, withSelective, introspectedTable);

            withSelective.setName(METHOD_SELECT_BY_PRIMARY_KEY_SELECTIVE);

            withSelective.getParameters().clear();
            for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                withSelective.addParameter(new Parameter(introspectedColumn.getFullyQualifiedJavaType(), introspectedColumn.getJavaProperty(), "@Param(\"" + introspectedColumn.getJavaProperty() + "\")"));
            }
            // selective
            withSelective.addParameter(
                    new Parameter(this.getModelColumnFullyQualifiedJavaType(introspectedTable), "selective", "@Param(\"selective\")", true)
            );

            FormatTools.addMethodWithBestPosition(interfaze, withSelective);
        }
        return super.clientSelectByPrimaryKeyMethodGenerated(method, interfaze, introspectedTable);
    }

    // ============================================== sqlMap 生成 ===================================================

    @Override
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.selectByExampleSelectiveEle = this.generateSelectSelectiveElement(METHOD_SELECT_BY_EXAMPLE_SELECTIVE, introspectedTable, false, true);
        return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element, introspectedTable);
    }

    @Override
    public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        if (!introspectedTable.hasBLOBColumns()) {
            this.selectByExampleSelectiveEle = this.generateSelectSelectiveElement(METHOD_SELECT_BY_EXAMPLE_SELECTIVE, introspectedTable, false, true);
        }
        return super.sqlMapSelectByExampleWithBLOBsElementGenerated(element, introspectedTable);
    }

    @Override
    public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.selectByPrimaryKeySelectiveEle = this.generateSelectSelectiveElement(METHOD_SELECT_BY_PRIMARY_KEY_SELECTIVE, introspectedTable, false, false);
        return super.sqlMapSelectByPrimaryKeyElementGenerated(element, introspectedTable);
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param document
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        // issues#16
        if (introspectedTable.isConstructorBased()) {
            XmlElement resultMapEle = new XmlElement("resultMap");
            resultMapEle.addAttribute(new Attribute("id", ID_FOR_PROPERTY_BASED_RESULT_MAP));
            resultMapEle.addAttribute(new Attribute("type", introspectedTable.getRules().calculateAllFieldsClass().getFullyQualifiedName()));
            commentGenerator.addComment(resultMapEle);

            for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                resultMapEle.addElement(XmlElementGeneratorTools.generateResultMapResultElement("id", introspectedColumn));
            }
            for (IntrospectedColumn introspectedColumn : introspectedTable.getNonPrimaryKeyColumns()) {
                resultMapEle.addElement(XmlElementGeneratorTools.generateResultMapResultElement("result", introspectedColumn));
            }
            document.getRootElement().addElement(0, resultMapEle);
        }

        // 1. selectByExampleSelective 方法
        if (this.selectByExampleSelectiveEle != null) {
            FormatTools.addElementWithBestPosition(document.getRootElement(), this.selectByExampleSelectiveEle);
            PluginTools.getHook(ISelectSelectivePluginHook.class).sqlMapSelectByExampleSelectiveElementGenerated(document, this.selectByExampleSelectiveEle, introspectedTable);
        }

        // 2. selectByPrimaryKeySelective
        if (this.selectByPrimaryKeySelectiveEle != null) {
            FormatTools.addElementWithBestPosition(document.getRootElement(), this.selectByPrimaryKeySelectiveEle);
        }

        return true;
    }

    // ===================================== ISelectOneByExamplePluginHook =========================================

    @Override
    public boolean clientSelectOneByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        FormatTools.addMethodWithBestPosition(interfaze, this.replaceMethodWithSelective(
                method,
                METHOD_SELECT_ONE_BY_EXAMPLE_SELECTIVE,
                "@Param(\"example\")",
                introspectedTable
        ));
        return true;
    }

    @Override
    public boolean clientSelectOneByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        if (!introspectedTable.hasBLOBColumns()) {
            FormatTools.addMethodWithBestPosition(interfaze, this.replaceMethodWithSelective(
                    method,
                    METHOD_SELECT_ONE_BY_EXAMPLE_SELECTIVE,
                    "@Param(\"example\")",
                    introspectedTable
            ));
        }
        return true;
    }

    @Override
    public boolean sqlMapSelectOneByExampleWithoutBLOBsElementGenerated(Document document, XmlElement element, IntrospectedTable introspectedTable) {
        if (!introspectedTable.hasBLOBColumns()) {
            FormatTools.addElementWithBestPosition(document.getRootElement(), this.generateSelectOneByExampleSelectiveElement(introspectedTable));
        }
        return true;
    }

    @Override
    public boolean sqlMapSelectOneByExampleWithBLOBsElementGenerated(Document document, XmlElement element, IntrospectedTable introspectedTable) {
        FormatTools.addElementWithBestPosition(document.getRootElement(), this.generateSelectOneByExampleSelectiveElement(introspectedTable));
        return true;
    }

    // =========================================== 一些私有方法 =====================================================

    /**
     * 生成selectOneByExampleSelective
     * @param introspectedTable
     * @return
     */
    private XmlElement generateSelectOneByExampleSelectiveElement(IntrospectedTable introspectedTable) {
        return this.generateSelectSelectiveElement(METHOD_SELECT_ONE_BY_EXAMPLE_SELECTIVE, introspectedTable, true, true);
    }

    /**
     * 生成selectOneByExampleSelective
     * @param introspectedTable
     * @return
     */
    private XmlElement generateSelectSelectiveElement(String id, IntrospectedTable introspectedTable, boolean selectOne, boolean byExample) {
        XmlElement selectSelectiveEle = new XmlElement("select");
        commentGenerator.addComment(selectSelectiveEle);

        selectSelectiveEle.addAttribute(new Attribute("id", id));
        // issues#16
        if (introspectedTable.isConstructorBased()) {
            selectSelectiveEle.addAttribute(new Attribute("resultMap", ID_FOR_PROPERTY_BASED_RESULT_MAP));
        } else if (introspectedTable.hasBLOBColumns()) {
            selectSelectiveEle.addAttribute(new Attribute("resultMap", introspectedTable.getResultMapWithBLOBsId()));
        } else {
            selectSelectiveEle.addAttribute(new Attribute("resultMap", introspectedTable.getBaseResultMapId()));
        }
        selectSelectiveEle.addAttribute(new Attribute("parameterType", "map"));

        if (byExample) {
            selectSelectiveEle.addElement(new TextElement("select"));
            if (stringHasValue(introspectedTable.getSelectByExampleQueryId())) {
                selectSelectiveEle.addElement(new TextElement("'" + introspectedTable.getSelectByExampleQueryId() + "' as QUERYID,"));
            }

            if (!selectOne) {
                // issues#20
                XmlElement ifDistinctElement = new XmlElement("if");
                ifDistinctElement.addAttribute(new Attribute("test", "example.distinct"));
                ifDistinctElement.addElement(new TextElement("distinct"));
                selectSelectiveEle.addElement(ifDistinctElement);
            }
        } else {
            selectSelectiveEle.addElement(new TextElement("select"));
            if (stringHasValue(introspectedTable.getSelectByPrimaryKeyQueryId())) {
                selectSelectiveEle.addElement(new TextElement("'" + introspectedTable.getSelectByPrimaryKeyQueryId() + "' as QUERYID,"));
            }
        }

        selectSelectiveEle.addElement(this.generateSelectiveElement(introspectedTable));
        selectSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));

        if (byExample) {
            selectSelectiveEle.addElement(XmlElementGeneratorTools.getUpdateByExampleIncludeElement(introspectedTable));

            XmlElement ifElement1 = new XmlElement("if");
            ifElement1.addAttribute(new Attribute("test", "example.orderByClause != null"));
            ifElement1.addElement(new TextElement("order by ${example.orderByClause}"));
            selectSelectiveEle.addElement(ifElement1);
        } else {
            boolean and = false;
            StringBuffer sb = new StringBuffer();
            for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                sb.setLength(0);
                if (and) {
                    sb.append("  and ");
                } else {
                    sb.append("where ");
                    and = true;
                }

                sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(introspectedColumn));
                sb.append(" = ");
                sb.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, introspectedTable.getRules().generatePrimaryKeyClass() ? "record." : null));
                selectSelectiveEle.addElement(new TextElement(sb.toString()));
            }
        }

        if (selectOne) {
            // 只查询一条
            selectSelectiveEle.addElement(new TextElement("limit 1"));
        }

        return selectSelectiveEle;
    }

    /**
     * 生成Selective xml节点
     * @param introspectedTable
     * @return
     */
    private XmlElement generateSelectiveElement(IntrospectedTable introspectedTable) {
        XmlElement chooseEle = new XmlElement("choose");

        XmlElement whenEle = new XmlElement("when");
        whenEle.addAttribute(new Attribute("test", "selective != null and selective.length > 0"));
        chooseEle.addElement(whenEle);

        // 生成返回字段节点
        XmlElement keysEle = new XmlElement("foreach");
        whenEle.addElement(keysEle);
        keysEle.addAttribute(new Attribute("collection", "selective"));
        keysEle.addAttribute(new Attribute("item", "column"));
        keysEle.addAttribute(new Attribute("separator", ","));
        keysEle.addElement(new TextElement("${column.aliasedEscapedColumnName}"));

        XmlElement otherwiseEle = new XmlElement("otherwise");
        chooseEle.addElement(otherwiseEle);
        // 存在关键词column或者table定义了alias属性,这里直接使用对应的ColumnListElement
        if (introspectedTable.getRules().generateSelectByExampleWithBLOBs()) {
            otherwiseEle.addElement(XmlElementGeneratorTools.getBaseColumnListElement(introspectedTable));
            otherwiseEle.addElement(new TextElement(","));
            otherwiseEle.addElement(XmlElementGeneratorTools.getBlobColumnListElement(introspectedTable));
        } else {
            otherwiseEle.addElement(XmlElementGeneratorTools.getBaseColumnListElement(introspectedTable));
        }

        return chooseEle;
    }

    /**
     * 替换方法成withSelective
     * @param method
     * @param name
     * @param firstAnnotation
     * @param introspectedTable
     * @return
     */
    private Method replaceMethodWithSelective(Method method, String name, String firstAnnotation, IntrospectedTable introspectedTable) {
        Method withSelective = JavaElementTools.clone(method);
        FormatTools.replaceGeneralMethodComment(commentGenerator, withSelective, introspectedTable);

        withSelective.setName(name);
        // example
        withSelective.getParameters().get(0).addAnnotation(firstAnnotation);
        // selective
        withSelective.addParameter(
                new Parameter(this.getModelColumnFullyQualifiedJavaType(introspectedTable), "selective", "@Param(\"selective\")", true)
        );

        return withSelective;
    }

    /**
     * 获取ModelColumn type
     * @param introspectedTable
     * @return
     */
    private FullyQualifiedJavaType getModelColumnFullyQualifiedJavaType(IntrospectedTable introspectedTable) {
        return new FullyQualifiedJavaType(introspectedTable.getRules().calculateAllFieldsClass().getShortName() + "." + ModelColumnPlugin.ENUM_NAME);
    }
}


/*
 * Copyright (c) 2018.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.BasePlugin;
import com.itfsw.mybatis.generator.plugins.utils.JavaElementGeneratorTools;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;

/**
 * ---------------------------------------------------------------------------
 * Cloneable
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2018/11/7 15:26
 * ---------------------------------------------------------------------------
 */
public class ModelCloneablePlugin extends BasePlugin {
    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.supportCloneable(topLevelClass, introspectedTable);
        return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.supportCloneable(topLevelClass, introspectedTable);
        return super.modelPrimaryKeyClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.supportCloneable(topLevelClass, introspectedTable);
        return super.modelRecordWithBLOBsClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 支持Cloneable
     * @param topLevelClass
     * @param introspectedTable
     */
    private void supportCloneable(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        // implement
        topLevelClass.addSuperInterface(new FullyQualifiedJavaType("java.lang.Cloneable"));
        // clone
        Method cloneMethod = JavaElementGeneratorTools.generateMethod(
                "clone",
                JavaVisibility.PUBLIC,
                topLevelClass.getType()
        );
        commentGenerator.addGeneralMethodComment(cloneMethod, introspectedTable);
        cloneMethod.addAnnotation("@Override");
        cloneMethod.addException(new FullyQualifiedJavaType("java.lang.CloneNotSupportedException"));
        cloneMethod.addBodyLine("return (" + topLevelClass.getType().getShortName() + ") super.clone();");
        topLevelClass.addMethod(cloneMethod);
    }
}


/*
 * Copyright (c) 2018.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.*;
import com.itfsw.mybatis.generator.plugins.utils.hook.ILogicalDeletePluginHook;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.internal.util.StringUtility;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ---------------------------------------------------------------------------
 * type or status enum 插件
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2018/11/27 20:36
 * ---------------------------------------------------------------------------
 */
public class EnumTypeStatusPlugin extends BasePlugin implements ILogicalDeletePluginHook {

    /**
     * 自动扫描
     */
    public final static String PRO_AUTO_SCAN = "autoScan";

    /**
     * 需要生成Enum的Column
     */
    public final static String PRO_ENUM_COLUMNS = "enumColumns";
    public final static String REMARKS_PATTERN = ".*\\s*\\[\\s*(\\w+\\s*\\(\\s*[\\u4e00-\\u9fa5_-a-zA-Z0-9]+\\s*\\)\\s*:\\s*[\\u4e00-\\u9fa5_-a-zA-Z0-9]+\\s*\\,?\\s*)+\\s*\\]\\s*.*";
    public final static String NEED_PATTERN = "\\[\\s*((\\w+\\s*\\(\\s*[\\u4e00-\\u9fa5_-a-zA-Z0-9]+\\s*\\)\\s*:\\s*[\\u4e00-\\u9fa5_-a-zA-Z0-9]+\\s*\\,?\\s*)+)\\s*\\]";
    public final static String ITEM_PATTERN = "(\\w+)\\s*\\(\\s*([\\u4e00-\\u9fa5_-a-zA-Z0-9]+)\\s*\\)\\s*:\\s*([\\u4e00-\\u9fa5_-a-zA-Z0-9]+)";
    private Map<String, EnumInfo> enumColumns;

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param introspectedTable
     */
    @Override
    public void initialized(IntrospectedTable introspectedTable) {
        super.initialized(introspectedTable);
        this.enumColumns = new LinkedHashMap<>();
        String autoScan = this.getProperties().getProperty(PRO_AUTO_SCAN);
        // 是否开启了自动扫描
        if (StringUtility.stringHasValue(autoScan) && !StringUtility.isTrue(autoScan)) {
            // 获取全局配置
            String enumColumns = this.getProperties().getProperty(EnumTypeStatusPlugin.PRO_ENUM_COLUMNS);
            // 如果有局部配置，则附加上去
            String tableEnumColumns = introspectedTable.getTableConfigurationProperty(EnumTypeStatusPlugin.PRO_ENUM_COLUMNS);
            if (tableEnumColumns != null) {
                enumColumns = enumColumns == null ? "" : (enumColumns + ",");

                enumColumns += introspectedTable.getTableConfigurationProperty(EnumTypeStatusPlugin.PRO_ENUM_COLUMNS);
            }

            if (StringUtility.stringHasValue(enumColumns)) {
                // 切分
                String[] enumColumnsStrs = enumColumns.split(",");
                for (String enumColumnsStr : enumColumnsStrs) {
                    IntrospectedColumn column = IntrospectedTableTools.safeGetColumn(introspectedTable, enumColumnsStr);
                    if (column != null) {
                        try {
                            EnumInfo enumInfo = new EnumInfo(column);
                            // 解析注释
                            enumInfo.parseRemarks(column.getRemarks());
                            if (enumInfo.hasItems()) {
                                this.enumColumns.put(column.getJavaProperty(), enumInfo);
                            }
                        } catch (EnumInfo.CannotParseException e) {
                            warnings.add("itfsw:插件" + EnumTypeStatusPlugin.class.getTypeName() + "没有找到column为" + enumColumnsStr.trim() + "对应格式的注释的字段！");
                        } catch (EnumInfo.NotSupportTypeException e) {
                            warnings.add("itfsw:插件" + EnumTypeStatusPlugin.class.getTypeName() + "找到column为" + enumColumnsStr.trim() + "对应Java类型不在支持范围内！");
                        }
                    }
                }
            }
        } else {
            for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
                try {
                    EnumInfo enumInfo = new EnumInfo(column);
                    // 解析注释
                    enumInfo.parseRemarks(column.getRemarks());
                    if (enumInfo.hasItems()) {
                        this.enumColumns.put(column.getJavaProperty(), enumInfo);
                    }
                } catch (Exception e) {
                    // nothing
                }
            }
        }
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.generateModelEnum(topLevelClass, introspectedTable);
        return super.modelPrimaryKeyClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * Model 生成
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.generateModelEnum(topLevelClass, introspectedTable);
        return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
    }

    // ======================================= ILogicalDeletePluginHook ======================================

    @Override
    public boolean clientLogicalDeleteByExampleMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return true;
    }

    @Override
    public boolean clientLogicalDeleteByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        return true;
    }

    @Override
    public boolean sqlMapLogicalDeleteByExampleElementGenerated(Document document, XmlElement element, IntrospectedColumn logicalDeleteColumn, String logicalDeleteValue, IntrospectedTable introspectedTable) {
        return true;
    }

    @Override
    public boolean sqlMapLogicalDeleteByPrimaryKeyElementGenerated(Document document, XmlElement element, IntrospectedColumn logicalDeleteColumn, String logicalDeleteValue, IntrospectedTable introspectedTable) {
        return true;
    }

    @Override
    public boolean logicalDeleteEnumGenerated(IntrospectedColumn logicalDeleteColumn) {
        return this.enumColumns.containsKey(logicalDeleteColumn.getJavaProperty());
    }

    /**
     * 生成对应enum
     * @param topLevelClass
     * @param introspectedTable
     */
    private void generateModelEnum(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        // 枚举跟随字段走
        for (Field field : topLevelClass.getFields()) {
            if (this.enumColumns.get(field.getName()) != null) {
                InnerEnum innerEnum = this.enumColumns.get(field.getName()).generateEnum(commentGenerator, introspectedTable);
                topLevelClass.addInnerEnum(innerEnum);
            }
        }
    }

    public static class EnumInfo {
        private List<EnumItemInfo> items = new ArrayList<>();
        private IntrospectedColumn column;

        public EnumInfo(IntrospectedColumn column) throws NotSupportTypeException, CannotParseException {
            String javaType = column.getFullyQualifiedJavaType().getFullyQualifiedName();
            if (!(Short.class.getTypeName().equals(javaType)
                    || Integer.class.getTypeName().equals(javaType)
                    || Long.class.getTypeName().equals(javaType)
                    || Boolean.class.getTypeName().equals(javaType)
                    || Double.class.getTypeName().equals(javaType)
                    || Float.class.getTypeName().equals(javaType)
                    || BigDecimal.class.getTypeName().equals(javaType)
                    || Byte.class.getTypeName().equals(javaType)
                    || String.class.getTypeName().equals(javaType))) {
                throw new NotSupportTypeException();
            } else {
                this.column = column;
            }
        }

        /**
         * 添加Enum Item
         * @param name
         * @param comment
         * @param value
         * @return
         */
        public void addItem(String name, String comment, String value) {
            items.add(new EnumItemInfo(this.column, name, comment, value));
        }

        /**
         * 判断是否有节点
         * @return
         */
        public boolean hasItems() {
            return items.size() > 0;
        }

        /**
         * 解析注释
         * @param remarks
         */
        public void parseRemarks(String remarks) throws CannotParseException {
            if (!StringUtility.stringHasValue(remarks) || !remarks.matches(REMARKS_PATTERN)) {
                throw new CannotParseException();
            } else {
                // 提取信息
                Pattern pattern = Pattern.compile(NEED_PATTERN);
                Matcher matcher = pattern.matcher(remarks);
                if (matcher.find() && matcher.groupCount() == 2) {
                    String enumInfoStr = matcher.group(1);
                    // 根据逗号切分
                    String[] enumInfoStrs = enumInfoStr.split(",");

                    // 提取每个节点信息
                    for (String enumInfoItemStr : enumInfoStrs) {
                        pattern = Pattern.compile(ITEM_PATTERN);
                        matcher = pattern.matcher(enumInfoItemStr.trim());
                        if (matcher.find() && matcher.groupCount() == 3) {
                            this.addItem(matcher.group(1), matcher.group(3), matcher.group(2));
                        }
                    }
                }
            }
        }

        /**
         * Getter method for property <tt>items</tt>.
         * @return property value of items
         * @author hewei
         */
        public List<EnumItemInfo> getItems() {
            return items;
        }

        public class NotSupportTypeException extends Exception {
        }

        public class CannotParseException extends Exception {
        }

        public InnerEnum generateEnum(CommentGenerator commentGenerator, IntrospectedTable introspectedTable) {
            String enumName = FormatTools.upFirstChar(column.getJavaProperty());

            InnerEnum innerEnum = new InnerEnum(new FullyQualifiedJavaType(enumName));
            commentGenerator.addEnumComment(innerEnum, introspectedTable);
            innerEnum.setVisibility(JavaVisibility.PUBLIC);
            innerEnum.setStatic(true);

            // 生成枚举
            for (EnumItemInfo item : this.items) {
                innerEnum.addEnumConstant(item.getName() + "(" + item.getValue() + ", " + item.getComment() + ")");
            }

            // 生成属性和构造函数
            Field fValue = new Field("value", column.getFullyQualifiedJavaType());
            fValue.setVisibility(JavaVisibility.PRIVATE);
            fValue.setFinal(true);
            commentGenerator.addFieldComment(fValue, introspectedTable);
            innerEnum.addField(fValue);

            Field fName = new Field("name", FullyQualifiedJavaType.getStringInstance());
            fName.setVisibility(JavaVisibility.PRIVATE);
            fName.setFinal(true);
            commentGenerator.addFieldComment(fName, introspectedTable);
            innerEnum.addField(fName);

            Method mInc = new Method(enumName);
            mInc.setConstructor(true);
            mInc.addBodyLine("this.value = value;");
            mInc.addBodyLine("this.name = name;");
            mInc.addParameter(new Parameter(fValue.getType(), "value"));
            mInc.addParameter(new Parameter(fName.getType(), "name"));
            commentGenerator.addGeneralMethodComment(mInc, introspectedTable);
            FormatTools.addMethodWithBestPosition(innerEnum, mInc);

            // 获取value的方法
            Method mValue = JavaElementGeneratorTools.generateGetterMethod(fValue);
            commentGenerator.addGeneralMethodComment(mValue, introspectedTable);
            FormatTools.addMethodWithBestPosition(innerEnum, mValue);

            Method mValue1 = JavaElementGeneratorTools.generateGetterMethod(fValue);
            mValue1.setName("value");
            commentGenerator.addGeneralMethodComment(mValue1, introspectedTable);
            FormatTools.addMethodWithBestPosition(innerEnum, mValue1);

            // 获取name的方法
            Method mName = JavaElementGeneratorTools.generateGetterMethod(fName);
            commentGenerator.addGeneralMethodComment(mName, introspectedTable);
            FormatTools.addMethodWithBestPosition(innerEnum, mName);

            return innerEnum;
        }

        public class EnumItemInfo {
            private IntrospectedColumn column;
            private String name;
            private String comment;
            private String value;

            public EnumItemInfo(IntrospectedColumn column, String name, String comment, String value) {
                this.column = column;
                this.name = name.trim();
                this.comment = comment.trim();
                this.value = value.trim();
            }

            /**
             * Getter method for property <tt>comment</tt>.
             * @return property value of comment
             * @author hewei
             */
            public String getComment() {
                return "\"" + comment + "\"";
            }

            /**
             * Getter method for property <tt>name</tt>.
             * @return property value of name
             * @author hewei
             */
            public String getName() {
                return name.toUpperCase();
            }

            /**
             * Getter method for property <tt>value</tt>.
             * @return property value of value
             * @author hewei
             */
            public String getValue() {
                String javaType = this.column.getFullyQualifiedJavaType().getShortName();
                if ("NULL".equalsIgnoreCase(value)) {
                    return "null";
                } else {
                    return "new " + javaType + "(\"" + value + "\")";
                }
            }

            /**
             * Getter method for property <tt>value</tt>.
             * @return property value of value
             * @author hewei
             */
            public String getOriginalValue() {
                return value;
            }
        }
    }
}


/*
 * Copyright (c) 2018.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins.utils.hook;

import com.itfsw.mybatis.generator.plugins.utils.BasePlugin;
import com.itfsw.mybatis.generator.plugins.utils.BeanUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.dom.java.InnerClass;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * ---------------------------------------------------------------------------
 *
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2018/4/27 11:33
 * ---------------------------------------------------------------------------
 */
public class HookAggregator implements IUpsertPluginHook,
        IModelBuilderPluginHook,
        IIncrementsPluginHook,
        IOptimisticLockerPluginHook,
        ISelectOneByExamplePluginHook,
        ITableConfigurationHook,
        ILombokPluginHook,
        ILogicalDeletePluginHook,
        ISelectSelectivePluginHook {

    protected static final Logger logger = LoggerFactory.getLogger(BasePlugin.class);
    private final static HookAggregator instance = new HookAggregator();
    private Context context;

    /**
     * constructor
     */
    public HookAggregator() {
    }

    /**
     * Getter method for property <tt>instance</tt>.
     * @return property value of instance
     * @author hewei
     */
    public static HookAggregator getInstance() {
        return instance;
    }

    /**
     * Setter method for property <tt>context</tt>.
     * @param context value to be assigned to property context
     * @author hewei
     */
    public void setContext(Context context) {
        this.context = context;
    }

    /**
     * 获取插件
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> List<T> getPlugins(Class<T> clazz) {
        List list = new ArrayList();
        // 反射获取插件列表，不能用单例去弄，不然因为类释放的问题而导致测试用例出问题
        try {
            List<Plugin> plugins = (List<Plugin>) BeanUtils.getProperty(this.context.getPlugins(), "plugins");
            for (Plugin plugin : plugins) {
                if (clazz.isInstance(plugin)) {
                    list.add(plugin);
                }
            }
        } catch (Exception e) {
            logger.error("获取插件列表失败！", e);
        }
        return list;
    }

    // ============================================= IIncrementsPluginHook ==============================================

    @Override
    public List<Element> incrementSetElementGenerated(IntrospectedColumn introspectedColumn, String prefix, boolean hasComma) {
        if (this.getPlugins(IIncrementsPluginHook.class).isEmpty()) {
            return new ArrayList<>();
        } else {
            return this.getPlugins(IIncrementsPluginHook.class).get(0).incrementSetElementGenerated(introspectedColumn, prefix, hasComma);
        }
    }

    @Override
    public Element incrementSetsWithSelectiveEnhancedPluginElementGenerated(IntrospectedColumn versionColumn) {
        if (this.getPlugins(IIncrementsPluginHook.class).isEmpty()) {
            return null;
        } else {
            return this.getPlugins(IIncrementsPluginHook.class).get(0).incrementSetsWithSelectiveEnhancedPluginElementGenerated(versionColumn);
        }
    }

    // ============================================ IModelBuilderPluginHook =============================================

    @Override
    public boolean modelBuilderClassGenerated(TopLevelClass topLevelClass, InnerClass builderClass, List<IntrospectedColumn> columns, IntrospectedTable introspectedTable) {
        for (IModelBuilderPluginHook plugin : this.getPlugins(IModelBuilderPluginHook.class)) {
            if (!plugin.modelBuilderClassGenerated(topLevelClass, builderClass, columns, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean modelBuilderSetterMethodGenerated(Method method, TopLevelClass topLevelClass, InnerClass builderClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable) {
        for (IModelBuilderPluginHook plugin : this.getPlugins(IModelBuilderPluginHook.class)) {
            if (!plugin.modelBuilderSetterMethodGenerated(method, topLevelClass, builderClass, introspectedColumn, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    // ================================================= IUpsertPluginHook ===============================================

    @Override
    public boolean clientUpsertSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (IUpsertPluginHook plugin : this.getPlugins(IUpsertPluginHook.class)) {
            if (!plugin.clientUpsertSelectiveMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean clientUpsertByExampleSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (IUpsertPluginHook plugin : this.getPlugins(IUpsertPluginHook.class)) {
            if (!plugin.clientUpsertByExampleSelectiveMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapUpsertSelectiveElementGenerated(XmlElement element, List<IntrospectedColumn> columns, XmlElement insertColumnsEle, XmlElement insertValuesEle, XmlElement setsEle, IntrospectedTable introspectedTable) {
        for (IUpsertPluginHook plugin : this.getPlugins(IUpsertPluginHook.class)) {
            if (!plugin.sqlMapUpsertSelectiveElementGenerated(element, columns, insertColumnsEle, insertValuesEle, setsEle, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapUpsertByExampleSelectiveElementGenerated(XmlElement element, List<IntrospectedColumn> columns, XmlElement insertColumnsEle, XmlElement insertValuesEle, XmlElement setsEle, IntrospectedTable introspectedTable) {
        for (IUpsertPluginHook plugin : this.getPlugins(IUpsertPluginHook.class)) {
            if (!plugin.sqlMapUpsertByExampleSelectiveElementGenerated(element, columns, insertColumnsEle, insertValuesEle, setsEle, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    // ================================================= IOptimisticLockerPluginHook ===============================================

    @Override
    public boolean clientUpdateWithVersionByExampleSelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (IOptimisticLockerPluginHook plugin : this.getPlugins(IOptimisticLockerPluginHook.class)) {
            if (!plugin.clientUpdateWithVersionByExampleSelectiveMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean clientUpdateWithVersionByPrimaryKeySelectiveMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (IOptimisticLockerPluginHook plugin : this.getPlugins(IOptimisticLockerPluginHook.class)) {
            if (!plugin.clientUpdateWithVersionByPrimaryKeySelectiveMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean generateSetsSelectiveElement(List<IntrospectedColumn> columns, IntrospectedColumn versionColumn, XmlElement setsElement) {
        if (this.getPlugins(IOptimisticLockerPluginHook.class).isEmpty()) {
            return false;
        } else {
            return this.getPlugins(IOptimisticLockerPluginHook.class).get(0).generateSetsSelectiveElement(columns, versionColumn, setsElement);
        }
    }

    // ============================================= ISelectOneByExamplePluginHook ==============================================

    @Override
    public boolean clientSelectOneByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (ISelectOneByExamplePluginHook plugin : this.getPlugins(ISelectOneByExamplePluginHook.class)) {
            if (!plugin.clientSelectOneByExampleWithBLOBsMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean clientSelectOneByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (ISelectOneByExamplePluginHook plugin : this.getPlugins(ISelectOneByExamplePluginHook.class)) {
            if (!plugin.clientSelectOneByExampleWithoutBLOBsMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapSelectOneByExampleWithoutBLOBsElementGenerated(Document document, XmlElement element, IntrospectedTable introspectedTable) {
        for (ISelectOneByExamplePluginHook plugin : this.getPlugins(ISelectOneByExamplePluginHook.class)) {
            if (!plugin.sqlMapSelectOneByExampleWithoutBLOBsElementGenerated(document, element, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapSelectOneByExampleWithBLOBsElementGenerated(Document document, XmlElement element, IntrospectedTable introspectedTable) {
        for (ISelectOneByExamplePluginHook plugin : this.getPlugins(ISelectOneByExamplePluginHook.class)) {
            if (!plugin.sqlMapSelectOneByExampleWithBLOBsElementGenerated(document, element, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    // ============================================= ITableConfigurationHook ==============================================

    @Override
    public void tableConfiguration(IntrospectedTable introspectedTable) {
        if (!this.getPlugins(ITableConfigurationHook.class).isEmpty()) {
            this.getPlugins(ITableConfigurationHook.class).get(0).tableConfiguration(introspectedTable);
        }
    }

    // ============================================= ILogicalDeletePluginHook ==============================================

    @Override
    public boolean clientLogicalDeleteByExampleMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (ILogicalDeletePluginHook plugin : this.getPlugins(ILogicalDeletePluginHook.class)) {
            if (!plugin.clientLogicalDeleteByExampleMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean clientLogicalDeleteByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        for (ILogicalDeletePluginHook plugin : this.getPlugins(ILogicalDeletePluginHook.class)) {
            if (!plugin.clientLogicalDeleteByPrimaryKeyMethodGenerated(method, interfaze, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapLogicalDeleteByExampleElementGenerated(Document document, XmlElement element, IntrospectedColumn logicalDeleteColumn, String logicalDeleteValue, IntrospectedTable introspectedTable) {
        for (ILogicalDeletePluginHook plugin : this.getPlugins(ILogicalDeletePluginHook.class)) {
            if (!plugin.sqlMapLogicalDeleteByExampleElementGenerated(document, element, logicalDeleteColumn, logicalDeleteValue, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean sqlMapLogicalDeleteByPrimaryKeyElementGenerated(Document document, XmlElement element, IntrospectedColumn logicalDeleteColumn, String logicalDeleteValue, IntrospectedTable introspectedTable) {
        for (ILogicalDeletePluginHook plugin : this.getPlugins(ILogicalDeletePluginHook.class)) {
            if (!plugin.sqlMapLogicalDeleteByPrimaryKeyElementGenerated(document, element, logicalDeleteColumn, logicalDeleteValue, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean logicalDeleteEnumGenerated(IntrospectedColumn logicalDeleteColumn) {
        for (ILogicalDeletePluginHook plugin : this.getPlugins(ILogicalDeletePluginHook.class)) {
            if (plugin.logicalDeleteEnumGenerated(logicalDeleteColumn)) {
                return true;
            }
        }
        return false;
    }

    // ============================================= ILombokPluginHook ==============================================

    @Override
    public boolean modelBaseRecordBuilderClassGenerated(TopLevelClass topLevelClass, List<IntrospectedColumn> columns, IntrospectedTable introspectedTable) {
        for (ILombokPluginHook plugin : this.getPlugins(ILombokPluginHook.class)) {
            if (!plugin.modelBaseRecordBuilderClassGenerated(topLevelClass, columns, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean modelPrimaryKeyBuilderClassGenerated(TopLevelClass topLevelClass, List<IntrospectedColumn> columns, IntrospectedTable introspectedTable) {
        for (ILombokPluginHook plugin : this.getPlugins(ILombokPluginHook.class)) {
            if (!plugin.modelPrimaryKeyBuilderClassGenerated(topLevelClass, columns, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean modelRecordWithBLOBsBuilderClassGenerated(TopLevelClass topLevelClass, List<IntrospectedColumn> columns, IntrospectedTable introspectedTable) {
        for (ILombokPluginHook plugin : this.getPlugins(ILombokPluginHook.class)) {
            if (!plugin.modelRecordWithBLOBsBuilderClassGenerated(topLevelClass, columns, introspectedTable)) {
                return false;
            }
        }
        return true;
    }

    // ============================================= ISelectSelectivePluginHook ==============================================

    @Override
    public boolean sqlMapSelectByExampleSelectiveElementGenerated(Document document, XmlElement element, IntrospectedTable introspectedTable) {
        for (ISelectSelectivePluginHook plugin : this.getPlugins(ISelectSelectivePluginHook.class)) {
            if (!plugin.sqlMapSelectByExampleSelectiveElementGenerated(document, element, introspectedTable)) {
                return false;
            }
        }
        return true;
    }
}


/*
 * Copyright (c) 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins.utils;

import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;

import static org.mybatis.generator.internal.util.messages.Messages.getString;

/**
 * ---------------------------------------------------------------------------
 * Java ele 生成工具
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2017/4/21 16:22
 * ---------------------------------------------------------------------------
 */
public class JavaElementGeneratorTools {

    /**
     * 生成静态常量
     * @param fieldName  常量名称
     * @param javaType   类型
     * @param initString 初始化字段
     * @return
     */
    public static Field generateStaticFinalField(String fieldName, FullyQualifiedJavaType javaType, String initString) {
        Field field = new Field(fieldName, javaType);
        field.setVisibility(JavaVisibility.PUBLIC);
        field.setStatic(true);
        field.setFinal(true);
        if (initString != null) {
            field.setInitializationString(initString);
        }
        return field;
    }

    /**
     * 生成属性
     * @param fieldName  常量名称
     * @param visibility 可见性
     * @param javaType   类型
     * @param initString 初始化字段
     * @return
     */
    public static Field generateField(String fieldName, JavaVisibility visibility, FullyQualifiedJavaType javaType, String initString) {
        Field field = new Field(fieldName, javaType);
        field.setVisibility(visibility);
        if (initString != null) {
            field.setInitializationString(initString);
        }
        return field;
    }

    /**
     * 生成方法
     * @param methodName 方法名
     * @param visibility 可见性
     * @param returnType 返回值类型
     * @param parameters 参数列表
     * @return
     */
    public static Method generateMethod(String methodName, JavaVisibility visibility, FullyQualifiedJavaType returnType, Parameter... parameters) {
        Method method = new Method(methodName);
        method.setVisibility(visibility);
        method.setReturnType(returnType);
        if (parameters != null) {
            for (Parameter parameter : parameters) {
                method.addParameter(parameter);
            }
        }

        return method;
    }

    /**
     * 生成方法实现体
     * @param method    方法
     * @param bodyLines 方法实现行
     * @return
     */
    public static Method generateMethodBody(Method method, String... bodyLines) {
        if (bodyLines != null) {
            for (String bodyLine : bodyLines) {
                method.addBodyLine(bodyLine);
            }
        }
        return method;
    }

    /**
     * 生成Filed的Set方法
     * @param field field
     * @return
     */
    public static Method generateSetterMethod(Field field) {
        Method method = generateMethod(
                "set" + FormatTools.upFirstChar(field.getName()),
                JavaVisibility.PUBLIC,
                null,
                new Parameter(field.getType(), field.getName())
        );
        return generateMethodBody(method, "this." + field.getName() + " = " + field.getName() + ";");
    }

    /**
     * 生成Filed的Get方法
     * @param field field
     * @return
     */
    public static Method generateGetterMethod(Field field) {
        Method method = generateMethod(
                "get" + FormatTools.upFirstChar(field.getName()),
                JavaVisibility.PUBLIC,
                field.getType()
        );
        return generateMethodBody(method, "return this." + field.getName() + ";");
    }

    /**
     * 获取Model没有BLOBs类时的类型
     * @param introspectedTable
     * @return
     */
    public static FullyQualifiedJavaType getModelTypeWithoutBLOBs(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType type;
        if (introspectedTable.getRules().generateBaseRecordClass()) {
            type = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
        } else if (introspectedTable.getRules().generatePrimaryKeyClass()) {
            type = new FullyQualifiedJavaType(introspectedTable.getPrimaryKeyType());
        } else {
            throw new RuntimeException(getString("RuntimeError.12"));
        }
        return type;
    }

    /**
     * 获取Model有BLOBs类时的类型
     * @param introspectedTable
     * @return
     */
    public static FullyQualifiedJavaType getModelTypeWithBLOBs(IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType type;
        if (introspectedTable.getRules().generateRecordWithBLOBsClass()) {
            type = new FullyQualifiedJavaType(introspectedTable.getRecordWithBLOBsType());
        } else {
            // the blob fields must be rolled up into the base class
            type = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
        }
        return type;
    }
}


/*
 * Copyright (c) 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.BasePlugin;
import com.itfsw.mybatis.generator.plugins.utils.FormatTools;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;

import java.util.List;

/**
 * ---------------------------------------------------------------------------
 * constructorBased 官方 bug 修正插件
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2017/7/28 11:05
 * ---------------------------------------------------------------------------
 */
public class ConstructorBasedBugFixPlugin extends BasePlugin {

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {

        // 有种情况下ModelBaseRecordClass不会生成不包含BLOBs的构造方法
        if (introspectedTable.isConstructorBased() && introspectedTable.hasBLOBColumns() && introspectedTable.getBLOBColumns().size() == 1) {
            // 判断是否已经生成了对应构造函数
            String version = Plugin.class.getPackage().getImplementationVersion();
            if (version != null){
                String[] strs = version.split("\\.");
                if (strs.length == 3 && strs[0].equals("1") && strs[1].equals("3") && Integer.parseInt(strs[2]) < 6){
                    // 添加没有BLOBs的构造方法
                    Method method = new Method();
                    method.setVisibility(JavaVisibility.PUBLIC);
                    method.setConstructor(true);
                    method.setName(topLevelClass.getType().getShortName());
                    commentGenerator.addGeneralMethodComment(method, introspectedTable);

                    // 使用没有blobs的字段
                    List<IntrospectedColumn> constructorColumns = introspectedTable.getNonBLOBColumns();

                    for (IntrospectedColumn introspectedColumn : constructorColumns) {
                        method.addParameter(new Parameter(introspectedColumn.getFullyQualifiedJavaType(), introspectedColumn.getJavaProperty()));
                        topLevelClass.addImportedType(introspectedColumn.getFullyQualifiedJavaType());
                    }

                    StringBuilder sb = new StringBuilder();
                    if (introspectedTable.getRules().generatePrimaryKeyClass()) {
                        boolean comma = false;
                        sb.append("super(");
                        for (IntrospectedColumn introspectedColumn : introspectedTable.getPrimaryKeyColumns()) {
                            if (comma) {
                                sb.append(", ");
                            } else {
                                comma = true;
                            }
                            sb.append(introspectedColumn.getJavaProperty());
                        }
                        sb.append(");");
                        method.addBodyLine(sb.toString());
                    }

                    List<IntrospectedColumn> introspectedColumns;
                    if (!introspectedTable.getRules().generatePrimaryKeyClass() && introspectedTable.hasPrimaryKeyColumns()) {
                        introspectedColumns = introspectedTable.getNonBLOBColumns();
                    } else {
                        introspectedColumns = introspectedTable.getBaseColumns();
                    }

                    for (IntrospectedColumn introspectedColumn : introspectedColumns) {
                        sb.setLength(0);
                        sb.append("this.");
                        sb.append(introspectedColumn.getJavaProperty());
                        sb.append(" = ");
                        sb.append(introspectedColumn.getJavaProperty());
                        sb.append(';');
                        method.addBodyLine(sb.toString());
                    }

                    FormatTools.addMethodWithBestPosition(topLevelClass, method);
                }
            }
        }

        return true;
    }
}


package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.BasePlugin;
import com.itfsw.mybatis.generator.plugins.utils.FormatTools;
import com.itfsw.mybatis.generator.plugins.utils.JavaElementGeneratorTools;
import com.itfsw.mybatis.generator.plugins.utils.PluginTools;
import com.itfsw.mybatis.generator.plugins.utils.enhanced.InnerInterface;
import com.itfsw.mybatis.generator.plugins.utils.enhanced.InnerInterfaceWrapperToInnerClass;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.internal.util.StringUtility;

import java.util.List;

/**
 * ---------------------------------------------------------------------------
 * Example 增强插件
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2017/1/16 16:28
 * ---------------------------------------------------------------------------
 */
public class ExampleEnhancedPlugin extends BasePlugin {
    // newAndCreateCriteria 方法
    public static final String METHOD_NEW_AND_CREATE_CRITERIA = "newAndCreateCriteria";
    // 逻辑删除列-Key
    public static final String PRO_ENABLE_AND_IF = "enableAndIf";
    // 是否启用column的操作
    private boolean enableColumnOperate = false;
    // 是否启用了过期的andIf
    private boolean enableAndIf;

    /**
     * {@inheritDoc}
     * @param introspectedTable
     */
    @Override
    public void initialized(IntrospectedTable introspectedTable) {
        super.initialized(introspectedTable);
        this.enableColumnOperate = PluginTools.checkDependencyPlugin(context, ModelColumnPlugin.class);
        String enableAndIf = properties.getProperty(PRO_ENABLE_AND_IF);
        this.enableAndIf = enableAndIf == null ? true : StringUtility.isTrue(enableAndIf);
    }

    /**
     * ModelExample Methods 生成
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        List<InnerClass> innerClasses = topLevelClass.getInnerClasses();
        for (int i = 0; i < innerClasses.size(); i++) {
            InnerClass innerClass = innerClasses.get(i);
            if ("Criteria".equals(innerClass.getType().getShortName())) {
                // 工厂方法
                addFactoryMethodToCriteria(topLevelClass, innerClass, introspectedTable);
                // andIf
                if (this.enableAndIf) {
                    addAndIfMethodToCriteria(topLevelClass, innerClass, introspectedTable);
                }
                // when
                addWhenToCriteria(topLevelClass, innerClass, introspectedTable);
            } else if ("GeneratedCriteria".equals(innerClass.getType().getShortName())) {
                // column 方法
                if (this.enableColumnOperate) {
                    addColumnMethodToCriteria(topLevelClass, innerClass, introspectedTable);
                }
            }
        }

        List<Method> methods = topLevelClass.getMethods();
        for (Method method : methods) {
            if (!"createCriteriaInternal".equals(method.getName())) {
                continue;
            } else {
                method.getBodyLines().set(0, "Criteria criteria = new Criteria(this);");
                logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "修改createCriteriaInternal方法，修改构造Criteria时传入Example对象");
            }
        }

        // orderBy方法
        this.addOrderByMethodToExample(topLevelClass, introspectedTable);

        // createCriteria 静态方法
        this.addStaticCreateCriteriaMethodToExample(topLevelClass, introspectedTable);

        // when
        addWhenToExample(topLevelClass, introspectedTable);

        return true;
    }

    /**
     * 添加 createCriteria 静态方法
     * @param topLevelClass
     * @param introspectedTable
     */
    private void addStaticCreateCriteriaMethodToExample(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        Method createCriteriaMethod = JavaElementGeneratorTools.generateMethod(
                METHOD_NEW_AND_CREATE_CRITERIA,
                JavaVisibility.PUBLIC,
                FullyQualifiedJavaType.getCriteriaInstance()
        );
        commentGenerator.addGeneralMethodComment(createCriteriaMethod, introspectedTable);

        createCriteriaMethod.setStatic(true);
        createCriteriaMethod.addBodyLine(topLevelClass.getType().getShortName() + " example = new " + topLevelClass.getType().getShortName() + "();");
        createCriteriaMethod.addBodyLine("return example.createCriteria();");

        FormatTools.addMethodWithBestPosition(topLevelClass, createCriteriaMethod);
    }

    /**
     * 添加列操作方法
     * @param topLevelClass
     * @param innerClass
     * @param introspectedTable
     */
    private void addColumnMethodToCriteria(TopLevelClass topLevelClass, InnerClass innerClass, IntrospectedTable introspectedTable) {
        // !!!!! Column import比较特殊引入的是外部类
        topLevelClass.addImportedType(introspectedTable.getRules().calculateAllFieldsClass());
        for (IntrospectedColumn introspectedColumn : introspectedTable.getNonBLOBColumns()) {
            topLevelClass.addImportedType(introspectedColumn.getFullyQualifiedJavaType());
            // EqualTo
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "EqualTo", "="));
            // NotEqualTo
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "NotEqualTo", "<>"));
            // GreaterThan
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "GreaterThan", ">"));
            // GreaterThanOrEqualTo
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "GreaterThanOrEqualTo", ">="));
            // LessThan
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "LessThan", "<"));
            // LessThanOrEqualTo
            FormatTools.addMethodWithBestPosition(innerClass, this.generateSingleValueMethod(introspectedTable, introspectedColumn, "LessThanOrEqualTo", "<="));
        }
    }

    /**
     * 生成column操作的具体方法
     * @param introspectedTable
     * @param introspectedColumn
     * @param nameFragment
     * @param operator
     * @return
     */
    private Method generateSingleValueMethod(IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, String nameFragment, String operator) {
        // 方法名
        StringBuilder sb = new StringBuilder();
        sb.append(introspectedColumn.getJavaProperty());
        sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
        sb.insert(0, "and");
        sb.append(nameFragment);
        sb.append("Column");

        Method method = JavaElementGeneratorTools.generateMethod(
                sb.toString(),
                JavaVisibility.PUBLIC,
                FullyQualifiedJavaType.getCriteriaInstance(),
                new Parameter(
                        new FullyQualifiedJavaType(introspectedTable.getRules().calculateAllFieldsClass().getShortName() + "." + ModelColumnPlugin.ENUM_NAME),
                        "column"
                )
        );
        commentGenerator.addGeneralMethodComment(method, introspectedTable);

        // 方法体
        sb.setLength(0);
        sb.append("addCriterion(");
        sb.append("new StringBuilder(\"");
        sb.append(MyBatis3FormattingUtilities.getAliasedActualColumnName(introspectedColumn));
        sb.append(" ");
        sb.append(operator);
        sb.append(" \").append(");
        sb.append("column.");
        sb.append(ModelColumnPlugin.METHOD_GET_ESCAPED_COLUMN_NAME);
        sb.append("()).toString());");

        JavaElementGeneratorTools.generateMethodBody(
                method,
                sb.toString(),
                "return (Criteria) this;"
        );

        return method;
    }

    /**
     * 添加工厂方法
     * @param topLevelClass
     * @param innerClass
     * @param introspectedTable
     */
    private void addFactoryMethodToCriteria(TopLevelClass topLevelClass, InnerClass innerClass, IntrospectedTable introspectedTable) {
        // example field
        Field exampleField = JavaElementGeneratorTools.generateField(
                "example",
                JavaVisibility.PRIVATE,
                topLevelClass.getType(),
                null
        );
        commentGenerator.addFieldComment(exampleField, introspectedTable);
        innerClass.addField(exampleField);

        // overwrite constructor
        List<Method> methods = innerClass.getMethods();
        for (Method method : methods) {
            if (method.isConstructor()) {
                method.addParameter(new Parameter(topLevelClass.getType(), "example"));
                method.addBodyLine("this.example = example;");
                commentGenerator.addGeneralMethodComment(method, introspectedTable);
                logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "修改构造方法，增加example参数");
            }
        }

        // 添加example工厂方法
        Method exampleMethod = JavaElementGeneratorTools.generateMethod(
                "example",
                JavaVisibility.PUBLIC,
                topLevelClass.getType()
        );
        commentGenerator.addGeneralMethodComment(exampleMethod, introspectedTable);
        exampleMethod = JavaElementGeneratorTools.generateMethodBody(
                exampleMethod,
                "return this.example;"
        );
        FormatTools.addMethodWithBestPosition(innerClass, exampleMethod);
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "." + innerClass.getType().getShortName() + "增加工厂方法example");
    }

    /**
     * 增强Criteria的链式调用(when)
     * @param topLevelClass
     * @param innerClass
     * @param introspectedTable
     */
    private void addWhenToCriteria(TopLevelClass topLevelClass, InnerClass innerClass, IntrospectedTable introspectedTable) {
        this.addWhenToClass(topLevelClass, innerClass, introspectedTable, "criteria");
    }

    /**
     * 增强Example的链式调用(when)
     * @param topLevelClass
     * @param introspectedTable
     */
    private void addWhenToExample(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        this.addWhenToClass(topLevelClass, topLevelClass, introspectedTable, "example");
    }

    /**
     * 增强链式调用(when)
     * @param topLevelClass
     * @param clazz
     * @param introspectedTable
     */
    private void addWhenToClass(TopLevelClass topLevelClass, InnerClass clazz, IntrospectedTable introspectedTable, String type) {
        // 添加接口When
        InnerInterface whenInterface = new InnerInterface("I" + FormatTools.upFirstChar(type) + "When");
        whenInterface.setVisibility(JavaVisibility.PUBLIC);

        // ICriteriaAdd增加接口add
        Method addMethod = JavaElementGeneratorTools.generateMethod(
                type,
                JavaVisibility.DEFAULT,
                null,
                new Parameter(clazz.getType(), type)
        );
        commentGenerator.addGeneralMethodComment(addMethod, introspectedTable);
        whenInterface.addMethod(addMethod);

        InnerClass innerClassWrapper = new InnerInterfaceWrapperToInnerClass(whenInterface);
        // 添加注释
        commentGenerator.addClassComment(innerClassWrapper, introspectedTable);
        topLevelClass.addInnerClass(innerClassWrapper);

        // 添加when方法
        Method whenMethod = JavaElementGeneratorTools.generateMethod(
                "when",
                JavaVisibility.PUBLIC,
                clazz.getType(),
                new Parameter(FullyQualifiedJavaType.getBooleanPrimitiveInstance(), "condition"),
                new Parameter(whenInterface.getType(), "then")
        );
        commentGenerator.addGeneralMethodComment(whenMethod, introspectedTable);
        whenMethod = JavaElementGeneratorTools.generateMethodBody(
                whenMethod,
                "if (condition) {",
                "then." + type + "(this);",
                "}",
                "return this;"
        );
        FormatTools.addMethodWithBestPosition(clazz, whenMethod);
        Method whenOtherwiseMethod = JavaElementGeneratorTools.generateMethod(
                "when",
                JavaVisibility.PUBLIC,
                clazz.getType(),
                new Parameter(FullyQualifiedJavaType.getBooleanPrimitiveInstance(), "condition"),
                new Parameter(whenInterface.getType(), "then"),
                new Parameter(whenInterface.getType(), "otherwise")
        );
        commentGenerator.addGeneralMethodComment(whenOtherwiseMethod, introspectedTable);
        whenOtherwiseMethod = JavaElementGeneratorTools.generateMethodBody(
                whenOtherwiseMethod,
                "if (condition) {",
                "then." + type + "(this);",
                "} else {",
                "otherwise." + type + "(this);",
                "}",
                "return this;"
        );
        FormatTools.addMethodWithBestPosition(clazz, whenOtherwiseMethod);
    }

    /**
     * 增强Criteria的链式调用，添加andIf(boolean addIf, CriteriaAdd add)方法，实现链式调用中按条件增加查询语句
     * @param topLevelClass
     * @param innerClass
     * @param introspectedTable
     */
    @Deprecated
    private void addAndIfMethodToCriteria(TopLevelClass topLevelClass, InnerClass innerClass, IntrospectedTable introspectedTable) {
        // 添加接口CriteriaAdd
        InnerInterface criteriaAddInterface = new InnerInterface("ICriteriaAdd");
        criteriaAddInterface.setVisibility(JavaVisibility.PUBLIC);
        criteriaAddInterface.addAnnotation("@Deprecated");
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "." + innerClass.getType().getShortName() + "增加接口ICriteriaAdd");

        // ICriteriaAdd增加接口add
        Method addMethod = JavaElementGeneratorTools.generateMethod(
                "add",
                JavaVisibility.DEFAULT,
                innerClass.getType(),
                new Parameter(innerClass.getType(), "add")
        );
        commentGenerator.addGeneralMethodComment(addMethod, introspectedTable);
        FormatTools.addMethodWithBestPosition(criteriaAddInterface, addMethod);
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "." + innerClass.getType().getShortName() + "." + criteriaAddInterface.getType().getShortName() + "增加方法add");

        InnerClass innerClassWrapper = new InnerInterfaceWrapperToInnerClass(criteriaAddInterface);
        // 添加注释
        commentGenerator.addClassComment(innerClassWrapper, introspectedTable);
        innerClass.addInnerClass(innerClassWrapper);

        // 添加andIf方法
        Method andIfMethod = JavaElementGeneratorTools.generateMethod(
                "andIf",
                JavaVisibility.PUBLIC,
                innerClass.getType(),
                new Parameter(FullyQualifiedJavaType.getBooleanPrimitiveInstance(), "ifAdd"),
                new Parameter(criteriaAddInterface.getType(), "add")
        );
        andIfMethod.addAnnotation("@Deprecated");
        commentGenerator.addGeneralMethodComment(andIfMethod, introspectedTable);
        andIfMethod = JavaElementGeneratorTools.generateMethodBody(
                andIfMethod,
                "if (ifAdd) {",
                "add.add(this);",
                "}",
                "return this;"
        );
        FormatTools.addMethodWithBestPosition(innerClass, andIfMethod);
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "." + innerClass.getType().getShortName() + "增加方法andIf");
    }

    /**
     * Example增强了setOrderByClause方法，新增orderBy(String orderByClause)方法直接返回example，增强链式调用，可以一路.下去了。
     * @param topLevelClass
     * @param introspectedTable
     */
    private void addOrderByMethodToExample(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        // 添加orderBy
        Method orderByMethod = JavaElementGeneratorTools.generateMethod(
                "orderBy",
                JavaVisibility.PUBLIC,
                topLevelClass.getType(),
                new Parameter(FullyQualifiedJavaType.getStringInstance(), "orderByClause")
        );
        commentGenerator.addGeneralMethodComment(orderByMethod, introspectedTable);
        orderByMethod = JavaElementGeneratorTools.generateMethodBody(
                orderByMethod,
                "this.setOrderByClause(orderByClause);",
                "return this;"
        );
        FormatTools.addMethodWithBestPosition(topLevelClass, orderByMethod);
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "增加方法orderBy");

        // 添加orderBy
        Method orderByMethod1 = JavaElementGeneratorTools.generateMethod(
                "orderBy",
                JavaVisibility.PUBLIC,
                topLevelClass.getType(),
                new Parameter(FullyQualifiedJavaType.getStringInstance(), "orderByClauses", true)
        );
        commentGenerator.addGeneralMethodComment(orderByMethod1, introspectedTable);
        orderByMethod1 = JavaElementGeneratorTools.generateMethodBody(
                orderByMethod1,
                "StringBuffer sb = new StringBuffer();",
                "for (int i = 0; i < orderByClauses.length; i++) {",
                "sb.append(orderByClauses[i]);",
                "if (i < orderByClauses.length - 1) {",
                "sb.append(\" , \");",
                "}",
                "}",
                "this.setOrderByClause(sb.toString());",
                "return this;"
        );

        FormatTools.addMethodWithBestPosition(topLevelClass, orderByMethod1);
        logger.debug("itfsw(Example增强插件):" + topLevelClass.getType().getShortName() + "增加方法orderBy(String ... orderByClauses)");
    }
}


/*
 * Copyright (c) 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itfsw.mybatis.generator.plugins;

import com.itfsw.mybatis.generator.plugins.utils.BasePlugin;
import com.itfsw.mybatis.generator.plugins.utils.FormatTools;
import com.itfsw.mybatis.generator.plugins.utils.JavaElementGeneratorTools;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.internal.util.JavaBeansUtil;
import org.mybatis.generator.internal.util.StringUtility;

/**
 * ---------------------------------------------------------------------------
 * 数据Model属性对应Column获取插件
 * ---------------------------------------------------------------------------
 * @author: hewei
 * @time:2017/1/17 11:20
 * ---------------------------------------------------------------------------
 */
public class ModelColumnPlugin extends BasePlugin {
    /**
     * 内部Enum名
     */
    public static final String ENUM_NAME = "Column";

    /**
     * 自定义方法
     */
    public static final String METHOD_EXCLUDES = "excludes";
    public static final String METHOD_GET_ESCAPED_COLUMN_NAME = "getEscapedColumnName";
    public static final String METHOD_GET_ALIASED_ESCAPED_COLUMN_NAME = "getAliasedEscapedColumnName";

    public static final String CONST_BEGINNING_DELIMITER = "BEGINNING_DELIMITER";   // const
    public static final String CONST_ENDING_DELIMITER = "ENDING_DELIMITER";   // const

    /**
     * Model Methods 生成
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        topLevelClass.addInnerEnum(this.generateColumnEnum(topLevelClass, introspectedTable));
        return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * Model Methods 生成
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        topLevelClass.addInnerEnum(this.generateColumnEnum(topLevelClass, introspectedTable));
        return super.modelRecordWithBLOBsClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        topLevelClass.addInnerEnum(this.generateColumnEnum(topLevelClass, introspectedTable));
        return super.modelPrimaryKeyClassGenerated(topLevelClass, introspectedTable);
    }

    /**
     * 生成Column字段枚举
     * @param topLevelClass
     * @param introspectedTable
     * @return
     */
    private InnerEnum generateColumnEnum(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        // 生成内部枚举
        InnerEnum innerEnum = new InnerEnum(new FullyQualifiedJavaType(ENUM_NAME));
        innerEnum.setVisibility(JavaVisibility.PUBLIC);
        innerEnum.setStatic(true);
        commentGenerator.addEnumComment(innerEnum, introspectedTable);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + "增加内部Builder类。");

        // 生成常量
        Field beginningDelimiterField = JavaElementGeneratorTools.generateField(
                CONST_BEGINNING_DELIMITER,
                JavaVisibility.PRIVATE,
                FullyQualifiedJavaType.getStringInstance(),
                "\"" + StringUtility.escapeStringForJava(context.getBeginningDelimiter()) + "\""
        );
        beginningDelimiterField.setStatic(true);
        beginningDelimiterField.setFinal(true);
        commentGenerator.addFieldComment(beginningDelimiterField, introspectedTable);
        innerEnum.addField(beginningDelimiterField);

        Field endingDelimiterField = JavaElementGeneratorTools.generateField(
                CONST_ENDING_DELIMITER,
                JavaVisibility.PRIVATE,
                FullyQualifiedJavaType.getStringInstance(),
                "\"" + StringUtility.escapeStringForJava(context.getEndingDelimiter()) + "\""
        );
        endingDelimiterField.setStatic(true);
        endingDelimiterField.setFinal(true);
        commentGenerator.addFieldComment(endingDelimiterField, introspectedTable);
        innerEnum.addField(endingDelimiterField);

        // 生成属性和构造函数
        Field columnField = new Field("column", FullyQualifiedJavaType.getStringInstance());
        columnField.setVisibility(JavaVisibility.PRIVATE);
        columnField.setFinal(true);
        commentGenerator.addFieldComment(columnField, introspectedTable);
        innerEnum.addField(columnField);

        Field isColumnNameDelimitedField = new Field("isColumnNameDelimited", FullyQualifiedJavaType.getBooleanPrimitiveInstance());
        isColumnNameDelimitedField.setVisibility(JavaVisibility.PRIVATE);
        isColumnNameDelimitedField.setFinal(true);
        commentGenerator.addFieldComment(isColumnNameDelimitedField, introspectedTable);
        innerEnum.addField(isColumnNameDelimitedField);

        Field javaPropertyField = new Field("javaProperty", FullyQualifiedJavaType.getStringInstance());
        javaPropertyField.setVisibility(JavaVisibility.PRIVATE);
        javaPropertyField.setFinal(true);
        commentGenerator.addFieldComment(javaPropertyField, introspectedTable);
        innerEnum.addField(javaPropertyField);

        Field jdbcTypeField = new Field("jdbcType", FullyQualifiedJavaType.getStringInstance());
        jdbcTypeField.setVisibility(JavaVisibility.PRIVATE);
        jdbcTypeField.setFinal(true);
        commentGenerator.addFieldComment(jdbcTypeField, introspectedTable);
        innerEnum.addField(jdbcTypeField);

        Method mValue = new Method("value");
        mValue.setVisibility(JavaVisibility.PUBLIC);
        mValue.setReturnType(FullyQualifiedJavaType.getStringInstance());
        mValue.addBodyLine("return this.column;");
        commentGenerator.addGeneralMethodComment(mValue, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, mValue);

        Method mGetValue = new Method("getValue");
        mGetValue.setVisibility(JavaVisibility.PUBLIC);
        mGetValue.setReturnType(FullyQualifiedJavaType.getStringInstance());
        mGetValue.addBodyLine("return this.column;");
        commentGenerator.addGeneralMethodComment(mGetValue, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, mGetValue);

        Method mGetJavaProperty = JavaElementGeneratorTools.generateGetterMethod(javaPropertyField);
        commentGenerator.addGeneralMethodComment(mGetJavaProperty, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, mGetJavaProperty);

        Method mGetJdbcType = JavaElementGeneratorTools.generateGetterMethod(jdbcTypeField);
        commentGenerator.addGeneralMethodComment(mGetJdbcType, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, mGetJdbcType);

        Method constructor = new Method(ENUM_NAME);
        constructor.setConstructor(true);
        constructor.addBodyLine("this.column = column;");
        constructor.addBodyLine("this.javaProperty = javaProperty;");
        constructor.addBodyLine("this.jdbcType = jdbcType;");
        constructor.addBodyLine("this.isColumnNameDelimited = isColumnNameDelimited;");
        constructor.addParameter(new Parameter(FullyQualifiedJavaType.getStringInstance(), "column"));
        constructor.addParameter(new Parameter(FullyQualifiedJavaType.getStringInstance(), "javaProperty"));
        constructor.addParameter(new Parameter(FullyQualifiedJavaType.getStringInstance(), "jdbcType"));
        constructor.addParameter(new Parameter(FullyQualifiedJavaType.getBooleanPrimitiveInstance(), "isColumnNameDelimited"));
        commentGenerator.addGeneralMethodComment(constructor, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, constructor);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加构造方法和column属性。");

        // Enum枚举
        for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
            Field field = JavaBeansUtil.getJavaBeansField(introspectedColumn, context, introspectedTable);

            StringBuffer sb = new StringBuffer();
            sb.append(field.getName());
            sb.append("(\"");
            sb.append(introspectedColumn.getActualColumnName());
            sb.append("\", \"");
            sb.append(introspectedColumn.getJavaProperty());
            sb.append("\", \"");
            sb.append(introspectedColumn.getJdbcTypeName());
            sb.append("\", ");
            sb.append(introspectedColumn.isColumnNameDelimited());
            sb.append(")");

            innerEnum.addEnumConstant(sb.toString());
            logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加" + field.getName() + "枚举。");
        }

        // asc 和 desc 方法
        Method desc = new Method("desc");
        desc.setVisibility(JavaVisibility.PUBLIC);
        desc.setReturnType(FullyQualifiedJavaType.getStringInstance());
        desc.addBodyLine("return this." + METHOD_GET_ESCAPED_COLUMN_NAME + "() + \" DESC\";");
        commentGenerator.addGeneralMethodComment(desc, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, desc);

        Method asc = new Method("asc");
        asc.setVisibility(JavaVisibility.PUBLIC);
        asc.setReturnType(FullyQualifiedJavaType.getStringInstance());
        asc.addBodyLine("return this." + METHOD_GET_ESCAPED_COLUMN_NAME + "() + \" ASC\";");
        commentGenerator.addGeneralMethodComment(asc, introspectedTable);
        FormatTools.addMethodWithBestPosition(innerEnum, asc);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加asc()和desc()方法。");

        // excludes
        topLevelClass.addImportedType("java.util.Arrays");
        topLevelClass.addImportedType(FullyQualifiedJavaType.getNewArrayListInstance());
        Method mExcludes = JavaElementGeneratorTools.generateMethod(
                METHOD_EXCLUDES,
                JavaVisibility.PUBLIC,
                new FullyQualifiedJavaType(ENUM_NAME + "[]"),
                new Parameter(innerEnum.getType(), "excludes", true)
        );
        commentGenerator.addGeneralMethodComment(mExcludes, introspectedTable);
        mExcludes.setStatic(true);
        JavaElementGeneratorTools.generateMethodBody(
                mExcludes,
                "ArrayList<Column> columns = new ArrayList<>(Arrays.asList(Column.values()));",
                "if (excludes != null && excludes.length > 0) {",
                "columns.removeAll(new ArrayList<>(Arrays.asList(excludes)));",
                "}",
                "return columns.toArray(new Column[]{});"
        );
        FormatTools.addMethodWithBestPosition(innerEnum, mExcludes);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加excludes方法。");

        // getEscapedColumnName
        Method mGetEscapedColumnName = JavaElementGeneratorTools.generateMethod(
                METHOD_GET_ESCAPED_COLUMN_NAME,
                JavaVisibility.PUBLIC,
                FullyQualifiedJavaType.getStringInstance()
        );
        commentGenerator.addGeneralMethodComment(mGetEscapedColumnName, introspectedTable);
        JavaElementGeneratorTools.generateMethodBody(
                mGetEscapedColumnName,
                "if (this.isColumnNameDelimited) {",
                "return new StringBuilder().append(" + CONST_BEGINNING_DELIMITER + ").append(this.column).append(" + CONST_ENDING_DELIMITER + ").toString();",
                "} else {",
                "return this.column;",
                "}"
        );
        FormatTools.addMethodWithBestPosition(innerEnum, mGetEscapedColumnName);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加getEscapedColumnName方法。");

        // getAliasedEscapedColumnName
        Method mGetAliasedEscapedColumnName = JavaElementGeneratorTools.generateMethod(
                METHOD_GET_ALIASED_ESCAPED_COLUMN_NAME,
                JavaVisibility.PUBLIC,
                FullyQualifiedJavaType.getStringInstance()
        );
        commentGenerator.addGeneralMethodComment(mGetAliasedEscapedColumnName, introspectedTable);
        if (StringUtility.stringHasValue(introspectedTable.getTableConfiguration().getAlias())) {
            String alias = introspectedTable.getTableConfiguration().getAlias();
            mGetAliasedEscapedColumnName.addBodyLine("StringBuilder sb = new StringBuilder();");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append(\"" + alias + ".\");");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append(this." + METHOD_GET_ESCAPED_COLUMN_NAME + "());");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append(\" as \");");
            mGetAliasedEscapedColumnName.addBodyLine("if (this.isColumnNameDelimited) {");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append("+CONST_BEGINNING_DELIMITER+");");
            mGetAliasedEscapedColumnName.addBodyLine("}");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append(\""+alias+"_\");");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append(this.column);");
            mGetAliasedEscapedColumnName.addBodyLine("if (this.isColumnNameDelimited) {");
            mGetAliasedEscapedColumnName.addBodyLine("sb.append("+CONST_BEGINNING_DELIMITER+");");
            mGetAliasedEscapedColumnName.addBodyLine("}");
            mGetAliasedEscapedColumnName.addBodyLine("return sb.toString();");
        } else {
            mGetAliasedEscapedColumnName.addBodyLine("return this." + METHOD_GET_ESCAPED_COLUMN_NAME + "();");
        }
        FormatTools.addMethodWithBestPosition(innerEnum, mGetAliasedEscapedColumnName);
        logger.debug("itfsw(数据Model属性对应Column获取插件):" + topLevelClass.getType().getShortName() + ".Column增加getAliasedEscapedColumnName方法。");

        return innerEnum;
    }
}