${database.allClassCopyright}package ${glPackagePluginBuri};

import java.lang.reflect.Method;
import java.util.List;

import org.escafe.buri.common.util.BuriConfiguration;
import org.escafe.buri.compiler.util.impl.rules.AbstractBuriDataFieldProcRule;
import org.escafe.buri.event.util.caller.DataAccessRuleEventCaller;
import org.escafe.buri.oouo.internal.structure.BuriDataFieldType;
import org.seasar.dbflute.Entity;
import org.seasar.dbflute.dbmeta.DBMeta;
import org.seasar.dbflute.dbmeta.info.ColumnInfo;
import org.seasar.dbflute.dbmeta.info.UniqueInfo;
import org.seasar.dbflute.helper.beans.DfBeanDesc;
import org.seasar.dbflute.helper.beans.factory.DfBeanDescFactory;
import org.seasar.dbflute.util.DfReflectionUtil;
import org.seasar.dbflute.util.DfTypeUtil;
import org.seasar.dbflute.util.Srl;
import org.seasar.framework.container.S2Container;

/**
 * The data access rule for Buri by behavior of DBFlute. <br />
 * It is precondition that the entity of DBFlute is not enhanced.
 * @author ${database.classAuthor}
 */
public class ${glBehaviorToDataAccessRule} extends AbstractBuriDataFieldProcRule {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    protected static final String BEHAVIOR_KEY = "bhv";

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    private S2Container container;

    private BuriConfiguration configuration;

    private DataAccessRuleEventCaller dataAccessRuleEventCaller;

    // ===================================================================================
    //                                                                       Required Rule
    //                                                                       =============
    @Override
    public boolean getRequiredRule(BuriDataFieldType src) {
        if (hasName(src, "preprocess")) {
            return false;
        }
        return true;
    }

    // ===================================================================================
    //                                                                       Check Process
    //                                                                       =============
    @Override
    public boolean fstCheckProcess(BuriDataFieldType src) {
        if (!isUseDBFlute()) {
            return false;
        }
        if (!isRequiredNegotiate(src)) {
            return false;
        }
        dataAccessRuleEventCaller.determinedRule(this, src);
        return true;
    }

    private boolean isUseDBFlute() {
        return true;
    }

    public boolean isRequiredNegotiate(BuriDataFieldType src) {
        if (src.getKeys().size() == 0) {
            return true;
        }
        if (!hasName(src, "select")) {
            return true;
        }
        if (!hasName(src, "insert")) {
            return true;
        }
        if (!hasName(src, "update")) {
            return true;
        }
        if (!hasName(src, "delete")) {
            return true;
        }
        if (!hasName(src, "tableName")) {
            return true;
        }
        return false;
    }

    // ===================================================================================
    //                                                                             Process
    //                                                                             =======
    @Override
    public boolean process(BuriDataFieldType src) {
        final String keyEnd = BEHAVIOR_KEY + "_end";
        if (src.getCache().containsKey(keyEnd)) {
            return false;
        }
        negotiateBehavior(src);
        dataAccessRuleEventCaller.endNegotiateDao(this, src);
        putToSrcCache(src, keyEnd, Boolean.TRUE);
        return false;
    }

    // ===================================================================================
    //                                                                     Behavior Set up
    //                                                                     ===============
    protected void negotiateBehavior(BuriDataFieldType src) {
        final String dtoClassName = src.getId();
        final String behaviorName = getBehaviorName(src, dtoClassName);
        if (behaviorName == null) {
            return;
        }
        putToSrcCache(src, BEHAVIOR_KEY, behaviorName);
        final Class<?> beanClass = DfReflectionUtil.forName(src.getId());
        final Class<?> behaviorClass = container.getRoot().getComponentDef(behaviorName).getComponentClass();
        final Entity entity = (Entity) DfReflectionUtil.newInstance(beanClass);
        final DBMeta dbmeta = entity.getDBMeta();
        final DBFluteResource resource = new DBFluteResource();
        resource.setBehaviorClass(behaviorClass);
        resource.setBeanClass(beanClass);
        resource.setEntity(entity);
        resource.setDBMeta(dbmeta);
        pkeySetup(src, resource);
        if (src.getKeys().size() != 1) {
            return;
        }
        final String keyName = src.getKeys().keySet().toArray()[0].toString();
        final DfBeanDesc beanDesc = DfBeanDescFactory.getBeanDesc(DfReflectionUtil.forName(dtoClassName));
        final Class<?> tgtClass = beanDesc.getPropertyDesc(keyName).getPropertyType();
        putToSrcCache(src, BEHAVIOR_KEY + "_KeyType", tgtClass);
        putToSrcCache(src, BEHAVIOR_KEY + "_KeyName", keyName);
        findAndSetupAllMethod(src, beanDesc, resource);
    }

    protected static class DBFluteResource {
        protected Class<?> behaviorClass;
        protected Class<?> beanClass;
        protected Entity entity;
        protected DBMeta dbmeta;

        public Class<?> getBehaviorClass() {
            return behaviorClass;
        }

        public void setBehaviorClass(Class<?> behaviorClass) {
            this.behaviorClass = behaviorClass;
        }

        public Class<?> getBeanClass() {
            return beanClass;
        }

        public void setBeanClass(Class<?> beanClass) {
            this.beanClass = beanClass;
        }

        public Entity getEntity() {
            return entity;
        }

        public void setEntity(Entity entity) {
            this.entity = entity;
        }

        public DBMeta getDBMeta() {
            return dbmeta;
        }

        public void setDBMeta(DBMeta dbmeta) {
            this.dbmeta = dbmeta;
        }
    }

    protected String getBehaviorName(BuriDataFieldType src, String dtoClassName) {
        String behaviorName = null;
        final String shtName = createBehaviorName(src, dtoClassName);
        final String shtName2 = shtName.substring(0, 1).toLowerCase() + shtName.substring(1);
        if (container.getRoot().hasComponentDef(shtName)) {
            behaviorName = shtName;
        } else if (container.getRoot().hasComponentDef(shtName2)) {
            behaviorName = shtName2;
        }
        return behaviorName;
    }

    protected String createBehaviorName(BuriDataFieldType src, String dtoClassName) {
        final Class<?> tgtClass = DfReflectionUtil.forName(dtoClassName);
        final String shtName = DfTypeUtil.toClassTitle(tgtClass) + "Bhv";
        return shtName;
    }

    @SuppressWarnings("unchecked")
    protected void putToSrcCache(BuriDataFieldType src, String key, Object value) {
        src.getCache().put(key, value);
    }

    // ===================================================================================
    //                                                                  Primary Key Set up
    //                                                                  ==================
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void pkeySetup(BuriDataFieldType src, DBFluteResource resource) {
        if ((src.getKeys().size() > 0) || hasName(src, "pkey")) {
            return;
        }
        final DBMeta dbmeta = resource.getDBMeta();
        final UniqueInfo primaryUniqueInfo = dbmeta.getPrimaryUniqueInfo();
        final ColumnInfo primaryColumn = primaryUniqueInfo.getFirstColumn();
        final String propertyName = primaryColumn.getPropertyName();
        final Class<?> propertyType = primaryColumn.getPropertyType();
        final String condition = createPkeyCondition(propertyName, propertyType);
        putToSrcKeys(src, propertyName, condition);
    }

    /**
     * @param primaryPropertyName The property name of the primary. (NotNull)
     * @param primaryPropertType The property type of the primary. (NotNull)
     * @return The OGNL condition of primary key. (NotNull)
     */
    protected String createPkeyCondition(String primaryPropertyName, Class<?> primaryPropertType) {
        String condition = null;
        if (primaryPropertType.equals(Long.TYPE)) {
            condition = primaryPropertyName + " != 0";
        } else if (primaryPropertType.equals(Long.class)) {
            condition = primaryPropertyName + " != null" + " && " + primaryPropertyName + " != 0";
        } else if (primaryPropertType.isAssignableFrom(Number.class)) {
            condition = primaryPropertyName + " != 0";
        } else {
            condition = primaryPropertyName + " != null";
        }
        return condition;
    }

    @SuppressWarnings("unchecked")
    protected void putToSrcKeys(BuriDataFieldType src, String key, String value) {
        src.getKeys().put(key, value);
    }

    // ===================================================================================
    //                                                                       Method Set up
    //                                                                       =============
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void findAndSetupAllMethod(BuriDataFieldType src, DfBeanDesc beanDesc, DBFluteResource resource) {
        final Method methods[] = resource.getBehaviorClass().getMethods();
        for (Method method : methods) {
            selectSetup(src, method, beanDesc, resource);
            selectManySetup(src, method, beanDesc, resource);
            updateSetup(src, method, beanDesc, resource);
            deleteSetup(src, method, beanDesc, resource);
            insertSetup(src, method, beanDesc, resource);
            tableNameSetup(src, method, beanDesc, resource);
            selectNextValSetup(src, method, beanDesc, resource);
        }
    }

    // -----------------------------------------------------
    //                                    getEntityForBuri()
    //                                    ------------------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void selectSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (!Srl.is_Null_or_Empty(src.getSelect())) {
            return;
        }
        if (isSelectMethod(src, method)) {
            final String keyName = src.getCache().get(BEHAVIOR_KEY + "_KeyName").toString();
            final String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            src.setSelect(daoName + "." + method.getName() + "(#data." + keyName + ")");
        }
    }

    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @return Is the method for select entity?
     */
    protected boolean isSelectMethod(BuriDataFieldType src, Method method) {
        final String methodName = method.getName();
        if (methodName.startsWith("xgetEntityForBuri") && method.getParameterTypes().length == 1) {
            final Class<?> tgtClass = (Class<?>) src.getCache().get(BEHAVIOR_KEY + "_KeyType");
            if (method.getParameterTypes()[0].equals(tgtClass)) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------
    //                                getEntityListForBuri()
    //                                ----------------------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void selectManySetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (!Srl.is_Null_or_Empty(src.getSelectMany())) {
            return;
        }
        if (isSelectManyMethod(src, method)) {
            final String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            src.setSelectMany(daoName + "." + method.getName() + "(#data)");
        }
    }

    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @return Is the method for select list?
     */
    protected boolean isSelectManyMethod(BuriDataFieldType src, Method method) {
        if (method.getName().startsWith("xgetEntitiesForBuri") && method.getParameterTypes().length == 1) {
            if (method.getParameterTypes()[0].isAssignableFrom(List.class)) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------
    //                                              insert()
    //                                              --------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void insertSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (isInsertMethod(src, method)) {
            String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            String insertOgnl = daoName + "." + method.getName() + "(#data)";
            if (!Srl.is_Null_or_Empty(src.getInsert())) {
                String insertStr = src.getInsert();
                StringBuffer strBuff = new StringBuffer();
                strBuff.append(insertStr);
                strBuff.append("\n");
                strBuff.append(insertOgnl);
                src.setInsert(strBuff.toString());
            } else {
                src.setInsert(insertOgnl);
            }
        }
    }

    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @return Is the method for insert?
     */
    protected boolean isInsertMethod(BuriDataFieldType src, Method method) {
        if (method.getName().equals("insert") && method.getParameterTypes().length == 1) {
            final String clazzName = method.getParameterTypes()[0].getName();
            if (clazzName.equals(src.getId())) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------
    //                                              update()
    //                                              --------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void updateSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (!Srl.is_Null_or_Empty(src.getUpdate())) {
            return;
        }
        if (isUpdateMethod(src, method)) {
            String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            src.setUpdate(daoName + "." + method.getName() + "(#data)");
        }
    }

    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @return Is the method for update?
     */
    protected boolean isUpdateMethod(BuriDataFieldType src, Method method) {
        if (method.getName().equals("update") && method.getParameterTypes().length == 1) {
            final String clazzName = method.getParameterTypes()[0].getName();
            if (clazzName.equals(src.getId())) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------
    //                                              delete()
    //                                              --------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void deleteSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (!Srl.is_Null_or_Empty(src.getDelete())) {
            return;
        }
        if (isDeleteMethod(src, method)) {
            String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            src.setDelete(daoName + "." + method.getName() + "(#data)");
        }
    }

    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method. (NotNull)
     * @return Is the method for delete?
     */
    protected boolean isDeleteMethod(BuriDataFieldType src, Method method) {
        if (method.getName().equals("delete") && method.getParameterTypes().length == 1) {
            final String clazzName = method.getParameterTypes()[0].getName();
            if (clazzName.equals(src.getId())) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------
    //                                      getTableDbName()
    //                                      ----------------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void tableNameSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (!Srl.is_Null_or_Empty(src.getTableName())) {
            return;
        }
        src.setTableName(resource.getDBMeta().getTableDbName());
    }

    // -----------------------------------------------------
    //                                       selectNextVal()
    //                                       ---------------
    /**
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @param beanDesc The description of the bean of the behavior. (NotNull)
     * @param resource The resource of DBFlute. (NotNull)
     */
    protected void selectNextValSetup(BuriDataFieldType src, Method method, DfBeanDesc beanDesc, DBFluteResource resource) {
        if (isSelectNextValMethod(src, method)) {
            String daoName = src.getCache().get(BEHAVIOR_KEY).toString();
            String pkey = (String) src.getCache().get(BEHAVIOR_KEY + "_KeyName");
            String selectNextVal = "#data." + pkey + " = " + daoName + "." + method.getName() + "()";
            if (!Srl.is_Null_or_Empty(src.getInsert())) {
                final String insertStr = src.getInsert();
                final StringBuilder sb = new StringBuilder();
                sb.append(selectNextVal.toString());
                sb.append("\n");
                sb.append(insertStr);
                src.setInsert(sb.toString());
            } else {
                src.setInsert(selectNextVal.toString());
            }
        }
    }

    /**
     * Is the method for selecting sequence next value?
     * @param src The field type of BURI data. (NotNull)
     * @param method The current method of the behavior. (NotNull)
     * @return Determination.
     */
    protected boolean isSelectNextValMethod(BuriDataFieldType src, Method method) {
        if (method.getName().startsWith("selectNextVal") && method.getParameterTypes().length == 0) {
            return true;
        }
        return false;
    }

    // ===================================================================================
    //                                                                            Accessor
    //                                                                            ========
    public S2Container getContainer() {
        return container;
    }

    public void setContainer(S2Container container) {
        this.container = container;
    }

    public BuriConfiguration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(BuriConfiguration configuration) {
        this.configuration = configuration;
    }

    public DataAccessRuleEventCaller getDataAccessRuleEventCaller() {
        return dataAccessRuleEventCaller;
    }

    public void setDataAccessRuleEventCaller(DataAccessRuleEventCaller dataAccessRuleEventCaller) {
        this.dataAccessRuleEventCaller = dataAccessRuleEventCaller;
    }
}
