/* $Id: MySQLRoleManagerDAO.java,v 1.2 2007/12/06 07:47:15 nito Exp $
 *
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow Core.
 * 
 *  Nautica Workflow Core is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow Core is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Nautica Workflow Core; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.workflow.dataaccess.mysql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import jp.co.argo21.nautica.workflow.dataaccess.AbstractDAO;
import jp.co.argo21.nautica.workflow.dataaccess.DAOException;
import jp.co.argo21.nautica.workflow.dataaccess.OrganizationBean;
import jp.co.argo21.nautica.workflow.dataaccess.RoleBean;
import jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO;
import jp.co.argo21.nautica.workflow.dataaccess.UserBean;
import jp.co.argo21.nautica.workflow.engine.LogManager;

/**
 * RoleBeanをデータとするMySQL用DAOクラスである。
 * <pre><tt>
 * ROLEテーブル
 * +-------------+--------------+------+-----+---------+-------+
 * | Field       | Type         | Null | Key | Default | Extra |
 * +-------------+--------------+------+-----+---------+-------+
 * | ROLE_ID     | varchar(64)  |      | PRI |         |       |
 * | DESCRIPTION | varchar(128) | YES  |     |         |       |
 * +-------------+--------------+------+-----+---------+-------+
 * </tt><tt>
 * ORGROLEMAPテーブル
 * +---------+-------------+------+-----+---------+-------+
 * | Field   | Type        | Null | Key | Default | Extra |
 * +---------+-------------+------+-----+---------+-------+
 * | ROLE_ID | varchar(64) |      | PRI |         |       |
 * | ORG_ID  | varchar(64) |      | PRI |         |       |
 * +---------+-------------+------+-----+---------+-------+
 * </tt><tt>
 * USERROLEMAPテーブル
 * +---------+-------------+------+-----+---------+-------+
 * | Field   | Type        | Null | Key | Default | Extra |
 * +---------+-------------+------+-----+---------+-------+
 * | ROLE_ID | varchar(64) |      | PRI |         |       |
 * | USER_ID | varchar(64) |      | PRI |         |       |
 * +---------+-------------+------+-----+---------+-------+
 * </tt>
 * </pre>
 * 属性は、カラム名をキー値としている。
 * カラム名の大文字小文字に関わらず、属性のキー値は大文字で設定される。
 * 
 * @author  mmanabe(Argo 21, Corp.)
 * @version $Revision: 1.2 $
 * @since   Nautica Workflow 1.0
 */
public class MySQLRoleManagerDAO extends AbstractDAO
implements RoleManagerDAO {
	/** エンジンログ */
	protected static Logger eLog = LogManager.getEngineLogger();

	 /** ロールIDのカラム名 */
    private static final String ROLE_ID = "ROLE_ID";
    
    /** 備考のカラム名 */
    private static final String DESCRIPTION = "DESCRIPTION";
    
    private static final String SQL_SELECT_BY_ROLE_ID
        ="select * from ROLE where ROLE_ID = ?";
    
    private static final String SQL_SELECT_BY_ORG_ID
        = "select ROLE.* from ROLE, ORGROLEMAP" +
          " where ORGROLEMAP.ORG_ID = ? and ROLE.ROLE_ID = ORGROLEMAP.ROLE_ID;";
    
    private static final String SQL_SELECT_BY_USER_ID
        ="select ROLE.* from ROLE, USERROLEMAP" +
         " where USERROLEMAP.USER_ID = ? and ROLE.ROLE_ID = USERROLEMAP.ROLE_ID";
    
    private static final String SQL_SELECT_ORGS_BY_ROLE_ID
        = "select ORGANIZATION.* from ORGANIZATION, ORGROLEMAP" +
          " where ORGROLEMAP.ROLE_ID = ? and ORGANIZATION.ORG_ID = ORGROLEMAP.ORG_ID";
    
    private static final String SQL_SELECT_USERS_BY_ROLE_ID
        = "select USER.* from USER, USERROLEMAP" +
          " where USERROLEMAP.ROLE_ID = ? and USER.USER_ID = USERROLEMAP.USER_ID";
    
    private static final String SQL_SELECT_SYSTEM_ROLE_COUNT_BY_USER_ID
        ="select count(*) from USERROLEMAP where USER_ID = ? and ROLE_ID = 'system'";
    
    /**
     * 権限関連テーブルに対するアクセスオブジェクトを生成する。
     * 
     * @param conn コネクション
     */
    MySQLRoleManagerDAO(Connection conn) {
        super(conn);
    }

    /**
     * 指定された権限IDの権限情報を返す。
     * 該当する権限情報が存在しない場合は null を返す。
     * 
     * @param  rid 権限ID
     * @return 指定された権限IDの権限情報を持つRoleBean
	 * @throws SQLException 任意のSQL例外
     * @see jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO#findByRoleID(java.lang.String)
     */
    public RoleBean findByRoleID(String rid) throws SQLException {
        
        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_BY_ROLE_ID);
		try {
			st.setString(1, rid);			

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			RoleBean bean = null;
			if (rs.next()) {
			    bean = createBean(rs);
			}
			
			return bean;
			
		} finally {
			st.close();
		}
    }

    /**
     * 指定された組織の権限情報の配列を返す。
     * 該当する権限情報が存在しない場合は空の配列を返す。
     * 
     * @param  oid 組織ID
     * @return 指定された組織の権限情報を持つRoleBeanの配列
	 * @throws SQLException 任意のSQL例外
     * @see jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO#findByOrganizationID(java.lang.String)
     */
    public RoleBean[] findByOrganizationID(String oid) throws SQLException {
        
        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_BY_ORG_ID);
		try {
			st.setString(1, oid);

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			List<RoleBean> beanList = new ArrayList<RoleBean>();
			while (rs.next()) {
				beanList.add(createBean(rs));
			}
						
			return (RoleBean[]) beanList.toArray(new RoleBean[beanList.size()]);
			
		} finally {
			st.close();
		}
    }

    /**
     * 指定されたユーザの権限情報の配列を返す。
     * 該当する権限情報が存在しない場合は空の配列を返す。
     * 
     * @param  uid ユーザID
     * @return 指定されたユーザの権限情報を持つRoleBeanの配列
     * @throws SQLException
	 * @throws SQLException 任意のSQL例外
     * @see jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO#findByUserID(java.lang.String)
     */
    public RoleBean[] findByUserID(String uid) throws SQLException {

        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_BY_USER_ID);
		try {
			st.setString(1, uid);

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			List<RoleBean> beanList = new ArrayList<RoleBean>();
			while (rs.next()) {
				beanList.add(createBean(rs));
			}
			
			return (RoleBean[]) beanList.toArray(new RoleBean[beanList.size()]);
			
		} finally {
			st.close();
		}
    }

    /**
     * 指定された権限を割り当てられている組織情報の配列を返す。
     * 該当する組織情報が存在しない場合は空の配列を返す。
     * 
     * @param  rid 権限ID
     * @return 権限を割り当てられている組織の情報を持つOrganizationBeanの配列
	 * @throws SQLException 任意のSQL例外
     * @see jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO#findOrganizationsByRoleID(java.lang.String)
     */
    public OrganizationBean[] findOrganizationsByRoleID(String rid)
    throws SQLException {
        
        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_ORGS_BY_ROLE_ID);
		try {
			st.setString(1, rid);

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			List<OrganizationBean> beanList = new ArrayList<OrganizationBean>();
			while (rs.next()) {
				beanList.add(MySQLOrganizationManagerDAO.createBean(rs));
			}
						
			return (OrganizationBean[]) beanList.toArray(new OrganizationBean[beanList.size()]);
			
		} finally {
			st.close();
		}
    }

    /**
     * 指定された権限を割り当てられているユーザ情報を返す。
     * 該当するユーザ情報が存在しない場合は空の配列を返す。
     * 
     * @param  rid 権限ID
     * @return 権限を割り当てられているユーザの情報を持つUserBeanの配列
	 * @throws SQLException 任意のSQL例外
     * @see jp.co.argo21.nautica.workflow.dataaccess.RoleManagerDAO#findUsersByRoleID(java.lang.String)
     */
    public UserBean[] findUsersByRoleID(String rid) throws SQLException {
        
        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_USERS_BY_ROLE_ID);
		try {
			st.setString(1, rid);

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			List<UserBean> beanList = new ArrayList<UserBean>();
			while (rs.next()) {
				beanList.add(MySQLUserManagerDAO.createBean(rs));
			}
						
			return (UserBean[]) beanList.toArray(new UserBean[beanList.size()]);
			
		} finally {
			st.close();
		}
    }
    
    /**
     * ResultSet中の情報からRoleBeanを作成する。
     * 
     * @param  rs ResultSet
     * @return ResultSetから取得した情報を持つRoleBean
	 * @throws SQLException 任意のSQL例外
     */
    static RoleBean createBean(ResultSet rs) throws SQLException {

        /* カラム名取得 */
        List<String> columnNames = new ArrayList<String>();
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            columnNames.add(rs.getMetaData().getColumnName(i));
        }

        RoleBean bean = new RoleBean();
        for (int i = 0; i < columnNames.size(); i++) {
            String columnName = ((String) columnNames.get(i)).toUpperCase();
            String attribute = (String) rs.getObject(columnName);

            /* 権限ID設定 */
            if (columnName.equals(ROLE_ID)) {
                bean.setID(attribute);
            }
            
            /* 備考設定 */
            if (columnName.equals(DESCRIPTION)) {
                bean.setName(attribute);                
            }

            /* 権限ID、備考以外の情報を権限情報に設定 */
            if (!columnName.equals(ROLE_ID)
                    && !columnName.equals(DESCRIPTION)) {
                bean.setAttribute(columnName, attribute);
            }
        }
        return bean;
    }

    /**
     * 指定されたユーザがシステム権限を持つかをチェックする。
     * 
     * @param  uid ユーザID
     * @return 指定されたユーザがsystem権限情報を持つならtrue
	 * @throws SQLException 任意のSQL例外
	 * @throws DAOException DAO実行中に処理としてあげる場合がある例外
     */
    public boolean checkSystemRole(String uid)
    throws SQLException, DAOException
    {
        PreparedStatement st = getConnection().prepareStatement(SQL_SELECT_SYSTEM_ROLE_COUNT_BY_USER_ID);
		try {
			st.setString(1, uid);			

			/************** DEBUG **************/
			eLog.debug(st.toString());
			/***********************************/

			ResultSet rs = st.executeQuery();
			
			boolean result = false;
			if (rs.next()) {
				if (rs.getInt(1) > 0) result = true;
			}
			return result;
		} finally {
			st.close();
		}
    }
}