/*
 * Aipo is a groupware program developed by TOWN, Inc.
 * Copyright (C) 2004-2015 TOWN, Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aimluck.eip.project.util;

import static com.aimluck.eip.util.ALLocalizationUtils.*;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.cayenne.DataRow;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.fulcrum.localization.LocalizationService;
import org.apache.jetspeed.services.customlocalization.CustomLocalizationService;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.jetspeed.util.ServiceUtil;
import org.apache.turbine.util.RunData;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;

import com.aimluck.eip.cayenne.om.portlet.EipMProjectKubun;
import com.aimluck.eip.cayenne.om.portlet.EipMProjectKubunValue;
import com.aimluck.eip.cayenne.om.portlet.EipTProject;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectFile;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectMember;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectTask;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectTaskComment;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectTaskCommentFile;
import com.aimluck.eip.cayenne.om.portlet.EipTProjectTaskMember;
import com.aimluck.eip.cayenne.om.security.TurbineUser;
import com.aimluck.eip.common.ALDBErrorException;
import com.aimluck.eip.common.ALEipConstants;
import com.aimluck.eip.common.ALEipUser;
import com.aimluck.eip.common.ALPageNotFoundException;
import com.aimluck.eip.mail.ALAdminMailContext;
import com.aimluck.eip.mail.ALAdminMailMessage;
import com.aimluck.eip.mail.ALMailService;
import com.aimluck.eip.mail.util.ALEipUserAddr;
import com.aimluck.eip.mail.util.ALMailUtils;
import com.aimluck.eip.orm.Database;
import com.aimluck.eip.orm.query.Operations;
import com.aimluck.eip.orm.query.SQLTemplate;
import com.aimluck.eip.orm.query.SelectQuery;
import com.aimluck.eip.project.ProjectResultData;
import com.aimluck.eip.project.ProjectTaskCommentResultData;
import com.aimluck.eip.project.ProjectTaskMemberResultData;
import com.aimluck.eip.project.ProjectTaskResultData;
import com.aimluck.eip.services.orgutils.ALOrgUtilsService;
import com.aimluck.eip.services.storage.ALStorageService;
import com.aimluck.eip.util.ALEipUtils;
import com.aimluck.eip.util.ALLocalizationUtils;

/**
 * プロジェクト管理のユーティリティクラスです。
 * 
 */
public class ProjectUtils {

  /** logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
    .getLogger(ProjectUtils.class.getName());

  /** ポートレット名 */
  public static final String PROJECT_PORTLET_NAME = "Project";

  /** フラグ：ON */
  public static final String FLG_ON = "t";

  /** フラグ：OFF */
  public static final String FLG_OFF = "f";

  /**
   * 日付空扱い
   */
  public static final String EMPTY_DATE = "9999/12/31";

  /**
   * 日付フォーマット（ロジック用）
   */
  public static final String DATE_FORMAT = "yyyy/MM/dd";

  /**
   * 日付フォーマット（DB用）
   */
  public static final String DB_DATE_FORMAT = "YYYY/MM/DD";

  /**
   * 日付フォーマット（画面表示用）
   */
  public static final String DISP_DATE_FORMAT = "yyyy年M月d日（E）";

  /** 小数値項目フォーマット */
  private final static DecimalFormat df = new DecimalFormat("#.0##");

  /** 新規、進行中、フィードバック */
  public static final List<String> incompleteStatus = Arrays.asList(
    "1",
    "2",
    "3");

  /** 完了、却下、停止 */
  public static final List<String> completeStatus = Arrays
    .asList("4", "5", "6");

  // ---------------------------------------------------
  // プロジェクト関連
  // ---------------------------------------------------

  /**
   * プロジェクト情報オブジェクトモデルを取得します。
   * 
   * @param rundata
   *          RunData
   * @param context
   *          Context
   * @return プロジェクト情報オブジェクトモデル
   */
  public static EipTProject getEipTProject(RunData rundata, Context context) {
    Integer projectId =
      Integer.valueOf(ALEipUtils.getTemp(
        rundata,
        context,
        ALEipConstants.ENTITY_ID));
    return getEipTProject(projectId);
  }

  /**
   * プロジェクト情報オブジェクトモデルを取得します。
   * 
   * @param projectId
   *          プロジェクトID
   * @return プロジェクト情報 オブジェクトモデル
   */
  public static EipTProject getEipTProject(Integer projectId) {

    try {
      if (projectId == null) {
        // Request IDが空の場合
        logger.debug("[ProjectUtils] Empty ID...");
        return null;
      }

      SelectQuery<EipTProject> query = Database.query(EipTProject.class);
      query.setQualifier(ExpressionFactory.matchDbExp(
        EipTProject.PROJECT_ID_PK_COLUMN,
        projectId));

      List<EipTProject> list = query.fetchList();

      if (list == null || list.isEmpty()) {
        // 指定したプロジェクトIDのレコードが見つからない場合
        logger.debug("[ProjectUtils] Not found ID...");
        return null;
      }
      return list.get(0);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * 全プロジェクトを返す
   * 
   * @return 全プロジェクトのリスト
   */
  public static List<ProjectResultData> getAllProject() {
    List<ProjectResultData> allProject = new ArrayList<ProjectResultData>();

    try {
      List<EipTProject> list =
        Database.query(EipTProject.class).orderAscending(
          EipTProject.PROJECT_NAME_PROPERTY).fetchList();

      for (EipTProject project : list) {
        ProjectResultData data = new ProjectResultData();
        data.initField();
        data.setProjectId(project.getProjectId());
        data.setProjectName(project.getProjectName());
        allProject.add(data);
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }

    return allProject;
  }

  /**
   * プロジェクトオブジェクトよりプロジェクトResult情報を返す
   * 
   * @param model
   *          プロジェクトオブジェクト
   * @return プロジェクトResult情報
   */
  public static ProjectResultData getProjectResultData(EipTProject model) {

    try {
      // 登録者
      ALEipUser adminUser = ALEipUtils.getALEipUser(model.getAdminUserId());

      ProjectResultData data = new ProjectResultData();
      data.initField();
      data.setProjectId(model.getProjectId()); // プロジェクトID
      data.setProjectName(model.getProjectName()); // プロジェクト名
      data.setExplanation(model.getExplanation()); // 説明
      data.setAdminUserId(model.getAdminUserId()); // 管理者ID
      data.setAdminUserName(adminUser.getAliasName().getValue()); // 管理者名
      data.setProgressFlg(model.getProgressFlg()); // 進捗率入力フラグ
      data.setProgressRate(model.getProgressRate()); // 進捗率
      data.setCreateDate(model.getCreateDate()); // 作成日
      data.setUpdateDate(model.getUpdateDate()); // 更新日
      data.setTaskUpdateDate(model.getUpdateDate()); // タスク更新日（初期値）
      return data;

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * プロジェクト進捗率を取得します。 最上位のタスクより進捗率を計算します。
   * 
   * @param projectId
   *          プロジェクトID
   * @return 紐づく全タスク情報
   */
  public static int getProjectProgressRate(Integer projectId) {

    try {

      List<DataRow> result = getProjectProgress(projectId);

      if (result == null) {
        return 0;
      }

      DataRow row = result.get(0);
      Object per = row.get("result_per");
      if (per == null) {
        return 0;
      }

      return Integer.valueOf(row.get("result_per").toString());

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return 0;
    }
  }

  /**
   * プロジェクトの進捗情報を取得します。
   * 
   * @param projectId
   *          プロジェクトID
   * @return 進捗情報
   */
  public static List<DataRow> getProjectProgress(Integer projectId) {

    /** CURRENT_DATE in SQL depend on Database locale, so unify the time in java */

    if (Database.isJdbcMySQL()) {

      SimpleDateFormat sdfSrc = new SimpleDateFormat("yyyy/MM/dd");
      Date date = null;
      try {
        date = sdfSrc.parse(EMPTY_DATE);
      } catch (ParseException e) {
        logger.error("getProjectProgress", e);
        throw new RuntimeException(e);
      }
      SimpleDateFormat sdfDest = new SimpleDateFormat("yyyy-MM-dd");
      String formatedEmptyDate = sdfDest.format(date);

      StringBuilder sb = new StringBuilder();
      sb
        .append("SELECT task.progress_rate, task.start_plan_date, task.end_plan_date, task.plan_workload, member.workload, ");
      sb.append("  CASE WHEN ");
      sb.append("    ").append(getCurrentDateWithCast()).append(
        " < task.end_plan_date ");
      sb.append("  THEN");
      sb.append("    CASE WHEN ");
      sb.append("      ").append(getCurrentDateWithCast()).append(
        " - task.start_plan_date + 1 < 0 THEN 0 ");
      sb.append("    ELSE");
      sb.append("      ").append(getCurrentDateWithCast()).append(
        " - task.start_plan_date + 1 ");
      sb.append("    END");
      sb.append("  ELSE");
      sb.append("    task.end_plan_date - task.start_plan_date + 1 ");
      sb.append("  END");
      sb.append("  AS lapsed_days, ");
      sb
        .append("  task.end_plan_date - task.start_plan_date + 1 AS task_days, task.update_date AS task_update_date FROM eip_t_project_task AS task ");
      sb.append("  JOIN");
      sb.append("  (");
      sb
        .append("    SELECT task_id, SUM(workload) AS workload FROM eip_t_project_task_member GROUP BY task_id");
      sb.append("  ) AS member ON member.task_id = task.task_id ");
      sb.append("  WHERE task.project_id = #bind($project_id) AND NOT EXISTS");
      sb.append("  (");
      sb
        .append("    SELECT 0 FROM eip_t_project_task AS sub WHERE sub.parent_task_id = task.task_id");
      sb.append("  )");
      sb
        .append("  AND start_plan_date <> #bind($empty_date) AND end_plan_date <> #bind($empty_date)");

      String subQuery = sb.toString();

      StringBuilder main = new StringBuilder();
      main
        .append("SELECT COUNT(0) AS cnt, SUM(lapsed_days) AS lapsed_days, SUM(task_days) AS task_days, CONVERT(SUM(task_days * progress_rate) / SUM(task_days), SIGNED) AS result_per, CONVERT(SUM(lapsed_days) * 100 / SUM(task_days), SIGNED) AS plan_per, CONVERT(SUM(plan_workload), SIGNED) AS plan_workload, CONVERT(SUM(workload), SIGNED) AS workload, CONVERT(SUM(task_days * progress_rate) / SUM(task_days), SIGNED) AS result_per, MAX(task_update_date) AS task_update_date FROM");
      main.append("(").append(subQuery).append(") AS base");

      String query = main.toString();
      SQLTemplate<EipTProjectTask> sqltemp =
        Database.sql(EipTProjectTask.class, query);
      sqltemp.param("project_id", projectId);
      sqltemp.param("empty_date", formatedEmptyDate);
      List<DataRow> result = sqltemp.fetchListAsDataRow();
      return result;
    } else {

      StringBuilder sb = new StringBuilder();
      sb.append("WITH base AS (");
      sb.append("  SELECT");
      sb.append("        task.progress_rate"); // 進捗率
      sb.append("      , task.start_plan_date"); // 開始予定日
      sb.append("      , task.end_plan_date"); // 完了予定日
      sb.append("      , task.plan_workload"); // 計画工数
      sb.append("      , member.workload"); // 工数
      sb.append("      , CASE");
      sb.append("          WHEN ").append(getCurrentDateWithCast()).append(
        " < task.end_plan_date");
      sb.append("            THEN");
      sb
        .append("              CASE WHEN ")
        .append(getCurrentDateWithCast())
        .append(" - task.start_plan_date + 1 < 0");
      sb.append("                THEN 0");
      sb
        .append("                ELSE ")
        .append(getCurrentDateWithCast())
        .append(" - task.start_plan_date + 1");
      sb.append("              END");
      sb.append("            ELSE");
      sb.append("              task.end_plan_date - task.start_plan_date + 1");
      sb.append("        END AS lapsed_days"); // 基準日までのタスク経過日数
      sb
        .append("      , task.end_plan_date - task.start_plan_date + 1 AS task_days"); // タスク経過日数
      sb.append("      , task.update_date AS task_update_date"); // 更新日
      sb.append("    FROM");
      sb.append("      eip_t_project_task AS task");
      sb.append("        JOIN ( ");
      sb.append("          SELECT");
      sb.append("              task_id");
      sb.append("              , SUM(workload) AS workload");
      sb.append("            FROM");
      sb.append("              eip_t_project_task_member");
      sb.append("            GROUP BY");
      sb.append("              task_id");
      sb.append("        ) AS member ");
      sb.append("          ON member.task_id = task.task_id");
      sb.append("   WHERE");
      sb.append("         task.project_id = #bind($project_id)");
      sb.append("     AND NOT EXISTS(");
      sb.append("           SELECT 0");
      sb.append("             FROM eip_t_project_task AS sub");
      sb.append("            WHERE");
      sb.append("                  sub.parent_task_id = task.task_id");
      sb.append("         )");
      sb
        .append("     AND start_plan_date <> TO_DATE(#bind($empty_date), #bind($date_format))");
      sb
        .append("     AND end_plan_date <> TO_DATE(#bind($empty_date), #bind($date_format))");
      sb.append(")");
      sb.append("SELECT");
      sb.append("      #result('COUNT(0)' 'int' 'cnt')");
      sb.append("    , #result('SUM(lapsed_days)' 'int' 'lapsed_days')");
      sb.append("    , #result('SUM(task_days)' 'int' 'task_days')");
      sb
        .append("    , #result('SUM(task_days * progress_rate) / SUM(task_days)' 'int' 'result_per')");
      sb
        .append("    , #result('SUM(lapsed_days) * 100 / SUM(task_days)' 'int' 'plan_per')"); // 予定進捗
      sb
        .append("    , #result('SUM(plan_workload)' 'java.math.BigDecimal' 'plan_workload')");// 計画工数
      sb
        .append("    , #result('SUM(workload)' 'java.math.BigDecimal' 'workload')");// 実績工数
      sb
        .append("    , #result('SUM(task_days * progress_rate) / SUM(task_days)' 'int' 'result_per')");
      sb
        .append("    , #result('MAX(task_update_date)' 'java.util.Date' 'task_update_date')");
      sb.append("  FROM");
      sb.append("    base");

      SQLTemplate<EipTProjectTask> sqltemp =
        Database.sql(EipTProjectTask.class, String.valueOf(sb));
      sqltemp.param("project_id", projectId);
      sqltemp.param("date_format", DB_DATE_FORMAT);
      sqltemp.param("empty_date", EMPTY_DATE);

      List<DataRow> result = sqltemp.fetchListAsDataRow();
      return result;
    }
  }

  // ---------------------------------------------------
  // プロジェクトメンバー関連
  // ---------------------------------------------------

  /**
   * プロジェクトメンバーを返す
   * 
   * @param projectId
   *          プロジェクトID
   * @return プロジェクトメンバーのリスト
   */
  public static List<ALEipUser> getProjectMembers(Integer projectId) {
    List<ALEipUser> members = new ArrayList<ALEipUser>();

    try {
      SelectQuery<EipTProjectMember> query =
        Database.query(EipTProjectMember.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectMember.EIP_TPROJECT_PROPERTY,
        projectId));

      List<EipTProjectMember> list = query.fetchList();

      for (EipTProjectMember member : list) {
        members.add(ALEipUtils.getALEipUser(member.getUserId()));
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }

    return members;
  }

  /**
   * プロジェクトメンバーリストを取得します。 <BR>
   * 
   * @param rundata
   * @param context
   * @return
   */
  public static List<EipTProjectMember> getEipTProjectMember(EipTProject project) {
    try {
      SelectQuery<EipTProjectMember> query =
        Database.query(EipTProjectMember.class);
      Expression exp =
        ExpressionFactory.matchDbExp(EipTProject.PROJECT_ID_PK_COLUMN, project
          .getProjectId());
      query.setQualifier(exp);

      List<EipTProjectMember> members = query.fetchList();

      if (members == null || members.size() == 0) {
        // 指定した Project IDのレコードが見つからない場合
        logger.debug("[ProjectUtils] Not found ID...");
        return null;
      }
      return members;
    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * プロジェクトに紐づくメンバーを削除する
   * 
   * @param project
   *          プロジェクトオブジェクト
   * @return TRUE 成功 FALSE 失敗
   */
  public static boolean removeProjectMember(EipTProject project) {

    try {
      // プロジェクトに紐づく担当者
      SelectQuery<EipTProjectMember> query =
        Database.query(EipTProjectMember.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectMember.EIP_TPROJECT_PROPERTY,
        project));

      List<EipTProjectMember> projectMemberList = query.fetchList();

      if (projectMemberList == null || projectMemberList.isEmpty()) {
        return true;
      }

      // プロジェクトに紐づくメンバーを削除
      Database.deleteAll(projectMemberList);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return false;
    }

    return true;
  }

  /**
   * プロジェクト作成時に参加メンバーへ通知メールを送る
   * 
   * @param taskId
   *          タスクID
   * @return 成否
   */
  public static boolean sendMailForProjectMembers(RunData rundata,
      Context context, EipTProject project, List<ALEipUser> memberList) {

    String orgId = Database.getDomainName();
    String subject =
      "["
        + ALOrgUtilsService.getAlias()
        + "]"
        + ALLocalizationUtils.getl10n("PROJECT_MAIL_TITLE");

    try {
      List<ALEipUserAddr> destMemberList =
        ALMailUtils.getALEipUserAddrs(
          memberList,
          ALEipUtils.getUserId(rundata),
          false);

      List<ALAdminMailMessage> messageList =
        new ArrayList<ALAdminMailMessage>();
      for (ALEipUserAddr destMember : destMemberList) {
        ALAdminMailMessage message = new ALAdminMailMessage(destMember);
        message.setPcSubject(subject);
        message.setCellularSubject(subject);
        message.setPcBody(createProjectMemberMsg(rundata, message
          .getPcMailAddr(), project));
        message.setCellularBody(createProjectMemberMsg(rundata, message
          .getCellMailAddr(), project));
        messageList.add(message);
      }

      ALMailService.sendAdminMailAsync(new ALAdminMailContext(orgId, ALEipUtils
        .getUserId(rundata), messageList, ALMailUtils
        .getSendDestType(ALMailUtils.KEY_MSGTYPE_WORKFLOW)));

    } catch (Exception ex) {
      logger.error("project", ex);
      return false;
    }
    return true;

  }

  /**
   * 送信するメールの内容を作成する．
   * 
   * @return
   */
  public static String createProjectMemberMsg(RunData rundata, String addr,
      EipTProject project) {
    VelocityContext context = new VelocityContext();
    boolean enableAsp = JetspeedResources.getBoolean("aipo.asp", false);
    String CR = ALMailUtils.CR;

    context.put("user_email", addr);

    // （さんの申請は承認されました。など）
    StringBuffer message = new StringBuffer("");
    message.append(CR);
    message.append(
      getl10nFormat("PROJECT_MAIL_TEXT2", project.getProjectName())).append(CR);
    context.put("message", message);

    // サービス
    context.put("serviceAlias", ALOrgUtilsService.getAlias());
    // サービス（Aipo）へのアクセス
    context.put("enableAsp", enableAsp);
    context.put("globalurl", ALMailUtils.getGlobalurl());
    context.put("localurl", ALMailUtils.getLocalurl());
    CustomLocalizationService locService =
      (CustomLocalizationService) ServiceUtil
        .getServiceByName(LocalizationService.SERVICE_NAME);
    String lang = locService.getLocale(rundata).getLanguage();
    StringWriter writer = new StringWriter();
    try {
      if (lang != null && lang.equals("ja")) {
        Template template =
          Velocity.getTemplate("portlets/mail/"
            + lang
            + "/project-notification-mail.vm", "utf-8");
        template.merge(context, writer);
      } else {
        Template template =
          Velocity.getTemplate(
            "portlets/mail/project-notification-mail.vm",
            "utf-8");
        template.merge(context, writer);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    writer.flush();
    String ret = writer.getBuffer().toString();
    return ret;

  }

  // ---------------------------------------------------
  // タスク関連
  // ---------------------------------------------------

  /**
   * タスクオブジェクトモデルを取得します。
   * 
   * @param rundata
   *          RunData
   * @param context
   *          Context
   * @return プロジェクト情報オブジェクトモデル
   */
  public static EipTProjectTask getEipTProjectTask(RunData rundata,
      Context context) {
    String taskId =
      ALEipUtils.getTemp(rundata, context, ALEipConstants.ENTITY_ID);
    return getEipTProjectTask(taskId);
  }

  /**
   * タスク一覧を返す
   * 
   * @param taskId
   *          プロジェクトID
   * @return タスク一覧
   */
  public static EipTProjectTask getEipTProjectTask(String taskId) {

    try {
      if (taskId == null || Integer.valueOf(taskId) == null) {
        // Request IDが空の場合
        logger.debug("[ProjectUtils] Empty ID...");
        return null;
      }

      SelectQuery<EipTProjectTask> query =
        Database.query(EipTProjectTask.class);
      query.setQualifier(ExpressionFactory.matchDbExp(
        EipTProjectTask.TASK_ID_PK_COLUMN,
        taskId));

      List<EipTProjectTask> list = query.fetchList();

      if (list == null || list.isEmpty()) {
        // 指定したプロジェクトIDのレコードが見つからない場合
        logger.debug("[ProjectUtils] Not found ID...");
        return null;
      }
      return list.get(0);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }

  }

  /**
   * タスクの存在チェック
   * 
   * @param projectId
   *          プロジェクトID
   * @return
   */
  public static boolean getEipTProjectTaskCheck(Integer projectId) {

    try {

      SelectQuery<EipTProjectTask> query =
        Database.query(EipTProjectTask.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTask.EIP_TPROJECT_PROPERTY,
        projectId));

      List<EipTProjectTask> list = query.fetchList();

      if (list == null || list.isEmpty()) {
        return true;
      }
      return false;

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return false;
    }
  }

  /**
   * タスクオブジェクトリストより項目定義Result情報リストを返す
   * 
   * @param taskList
   *          タスクオブジェクトリスト
   * @return タスクResult情報リスト
   */
  public static List<ProjectTaskResultData> getProjectTaskResultList(
      List<EipTProjectTask> taskList) {
    List<ProjectTaskResultData> resultList =
      new ArrayList<ProjectTaskResultData>();
    for (EipTProjectTask task : taskList) {
      resultList.add(getProjectTaskResultData(task));
    }
    return resultList;
  }

  /**
   * タスクオブジェクトよりタスクResult情報を返す
   * 
   * @param model
   *          タスクオブジェクト
   * @return タスクResult情報
   */
  public static ProjectTaskResultData getProjectTaskResultData(
      EipTProjectTask model) {
    ProjectTaskResultData data = new ProjectTaskResultData();
    data.initField();
    data.setTaskId(model.getTaskId()); // タスクID
    data.setProjectId(model.getProjectId()); // プロジェクトID
    data.setTracker(model.getTracker()); // 分類
    data.setTaskName(model.getTaskName()); // タスク名
    data.setExplanation(model.getExplanation()); // 説明
    data.setStatus(model.getStatus()); // ステータス
    data.setPriority(model.getPriority()); // 優先度
    // 開始予定日
    if (!ProjectUtils.isEmptyDate(model.getStartPlanDate())) {
      data.setStartPlanDate(model.getStartPlanDate());
    }
    // 完了予定日
    if (!ProjectUtils.isEmptyDate(model.getEndPlanDate())) {
      data.setEndPlanDate(model.getEndPlanDate());
    }
    // 開始実績日
    if (!ProjectUtils.isEmptyDate(model.getStartDate())) {
      data.setStartDate(model.getStartDate());
    }
    // 完了実績日
    if (!ProjectUtils.isEmptyDate(model.getEndDate())) {
      data.setEndDate(model.getEndDate());
    }
    data.setPlanWorkload(new BigDecimal(df.format(model.getPlanWorkload()))); // 計画工数
    data.setProgressRate(model.getProgressRate()); // 進捗率
    data.setCreateDate(model.getCreateDate());// 作成日
    data.setUpdateDate(model.getUpdateDate());// 更新日

    // 担当者
    data.setMemberList(ProjectUtils.getProjectTaskMembers(model
      .getTaskId()
      .toString()));

    // 親タスクの有無
    Integer parentTaskId = model.getParentTaskId();
    if (parentTaskId != null) {
      EipTProjectTask parent = getEipTProjectTask("" + parentTaskId);
      data.setParentTaskId(parent.getTaskId()); // 親タスクID
      data.setParentTaskName(parent.getTaskName());
    }
    data.setIndent(model.getIndent()); // タスク名表示インデント

    return data;
  }

  /**
   * 子タスクの件数を取得します。
   * 
   * @param taskId
   *          タスクID
   * @return 子タスクの件数
   */
  public static int getCountChildrenTask(Integer taskId) {

    try {

      if (taskId == null) {
        return 0;
      }

      SelectQuery<EipTProjectTask> query =
        Database.query(EipTProjectTask.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTask.PARENT_TASK_ID_PROPERTY,
        taskId));

      List<EipTProjectTask> list = query.fetchList();

      if (list == null || list.isEmpty()) {
        return 0;
      }
      return list.size();

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return -1;
    }

  }

  // ---------------------------------------------------
  // タスク担当者関連
  // ---------------------------------------------------

  /**
   * タスク担当者を返す
   * 
   * @param taskId
   *          タスクID
   * @return タスク担当者のリスト
   */
  public static List<ProjectTaskMemberResultData> getProjectTaskMembers(
      String taskId) {
    List<ProjectTaskMemberResultData> members =
      new ArrayList<ProjectTaskMemberResultData>();

    try {
      SelectQuery<EipTProjectTaskMember> query =
        Database.query(EipTProjectTaskMember.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTaskMember.EIP_TPROJECT_TASK_PROPERTY,
        taskId));
      query.orderAscending(EipTProjectTaskMember.USER_ID_PROPERTY);

      List<EipTProjectTaskMember> list = query.fetchList();

      for (EipTProjectTaskMember member : list) {
        ALEipUser user = ALEipUtils.getALEipUser(member.getUserId());
        ProjectTaskMemberResultData data = new ProjectTaskMemberResultData();
        data.initField();
        data.setUserId(user.getUserId().getValue());
        data.setUserName(user.getAliasName().getValue());
        BigDecimal workload = new BigDecimal(df.format(member.getWorkload()));
        data.setWorkload(workload);
        members.add(data);
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }

    return members;
  }

  /**
   * タスクに紐づく担当者を削除する
   * 
   * @param task
   *          タスクオブジェクト
   * @return TRUE 成功 FALSE 失敗
   */
  public static boolean removeProjectTaskMember(EipTProjectTask task) {

    try {
      // タスクに紐づく担当者
      SelectQuery<EipTProjectTaskMember> query =
        Database.query(EipTProjectTaskMember.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTaskMember.EIP_TPROJECT_TASK_PROPERTY,
        task));

      List<EipTProjectTaskMember> taskMemberList = query.fetchList();

      if (taskMemberList == null || taskMemberList.isEmpty()) {
        return true;
      }

      // タスクに紐づく担当者を削除
      Database.deleteAll(taskMemberList);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return false;
    }

    return true;
  }

  /**
   * タスク担当者へ通知メールを送る
   * 
   * @param taskId
   *          タスクID
   * @return 成否
   */
  public static boolean sendMailForTaskMembers(RunData rundata,
      Context context, EipTProjectTask task, EipTProject project,
      List<ProjectTaskMemberResultData> mailUserList) {

    String orgId = Database.getDomainName();
    String subject =
      "["
        + ALOrgUtilsService.getAlias()
        + "]"
        + ALLocalizationUtils.getl10n("PROJECT_MAIL_TITLE");

    try {
      List<ALEipUser> memberList = new ArrayList<ALEipUser>();
      for (ProjectTaskMemberResultData rd : mailUserList) {
        memberList.add(ALEipUtils
          .getALEipUser(rd.getUserId().getValueWithInt()));
      }

      List<ALEipUserAddr> destMemberList =
        ALMailUtils.getALEipUserAddrs(
          memberList,
          ALEipUtils.getUserId(rundata),
          false);

      List<ALAdminMailMessage> messageList =
        new ArrayList<ALAdminMailMessage>();
      for (ALEipUserAddr destMember : destMemberList) {
        ALAdminMailMessage message = new ALAdminMailMessage(destMember);
        message.setPcSubject(subject);
        message.setCellularSubject(subject);
        message.setPcBody(createTaskMemberMsg(
          rundata,
          message.getPcMailAddr(),
          task,
          project));
        message.setCellularBody(createTaskMemberMsg(rundata, message
          .getCellMailAddr(), task, project));
        messageList.add(message);
      }

      ALMailService.sendAdminMailAsync(new ALAdminMailContext(orgId, ALEipUtils
        .getUserId(rundata), messageList, ALMailUtils
        .getSendDestType(ALMailUtils.KEY_MSGTYPE_WORKFLOW)));

    } catch (Exception ex) {
      logger.error("project", ex);
      return false;
    }
    return true;

  }

  /**
   * 送信するメールの内容を作成する．
   * 
   * @return
   */
  public static String createTaskMemberMsg(RunData rundata, String addr,
      EipTProjectTask task, EipTProject project) {
    VelocityContext context = new VelocityContext();
    boolean enableAsp = JetspeedResources.getBoolean("aipo.asp", false);
    String CR = ALMailUtils.CR;

    context.put("user_email", addr);

    // （さんの申請は承認されました。など）
    StringBuffer message = new StringBuffer("");
    message.append(CR);
    message.append(
      getl10nFormat("PROJECT_MAIL_TEXT", project.getProjectName(), task
        .getTaskName())).append(CR);
    context.put("message", message);

    // サービス
    context.put("serviceAlias", ALOrgUtilsService.getAlias());
    // サービス（Aipo）へのアクセス
    context.put("enableAsp", enableAsp);
    context.put("globalurl", ALMailUtils.getGlobalurl());
    context.put("localurl", ALMailUtils.getLocalurl());
    CustomLocalizationService locService =
      (CustomLocalizationService) ServiceUtil
        .getServiceByName(LocalizationService.SERVICE_NAME);
    String lang = locService.getLocale(rundata).getLanguage();
    StringWriter writer = new StringWriter();
    try {
      if (lang != null && lang.equals("ja")) {
        Template template =
          Velocity.getTemplate("portlets/mail/"
            + lang
            + "/project-notification-mail.vm", "utf-8");
        template.merge(context, writer);
      } else {
        Template template =
          Velocity.getTemplate(
            "portlets/mail/project-notification-mail.vm",
            "utf-8");
        template.merge(context, writer);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    writer.flush();
    String ret = writer.getBuffer().toString();
    return ret;

  }

  // ---------------------------------------------------
  // タスクコメント関連
  // ---------------------------------------------------

  /**
   * タスクコメントオブジェクトモデルを取得します。
   * 
   * @param rundata
   *          RunData
   * @param context
   *          Context
   * @return プロジェクト情報オブジェクトモデル
   */
  public static EipTProjectTaskComment getEipTProjectTaskComment(
      RunData rundata, Context context) {
    String commentId =
      ALEipUtils.getTemp(rundata, context, ALEipConstants.ENTITY_ID);
    return getEipTProjectTaskComment(commentId);
  }

  /**
   * タスクコメント一覧を返す
   * 
   * @param commentId
   *          コメントID
   * @return コメント一覧
   */
  public static EipTProjectTaskComment getEipTProjectTaskComment(
      String commentId) {

    try {
      if (commentId == null || Integer.valueOf(commentId) == null) {
        // Request IDが空の場合
        logger.debug("[ProjectUtils] Empty ID...");
        return null;
      }

      SelectQuery<EipTProjectTaskComment> query =
        Database.query(EipTProjectTaskComment.class);
      query.setQualifier(ExpressionFactory.matchDbExp(
        EipTProjectTaskComment.COMMENT_ID_PK_COLUMN,
        commentId));

      List<EipTProjectTaskComment> list = query.fetchList();

      if (list == null || list.isEmpty()) {
        // 指定したプロジェクトIDのレコードが見つからない場合
        logger.debug("[ProjectUtils] Not found ID...");
        return null;
      }
      return list.get(0);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }

  }

  /**
   * タスクコメントリストを返す
   * 
   * @param taskId
   *          タスクID
   * @return タスクコメントのリスト
   */
  public static List<ProjectTaskCommentResultData> getProjectTaskCommentList(
      String taskId) {
    List<ProjectTaskCommentResultData> commentList =
      new ArrayList<ProjectTaskCommentResultData>();

    try {
      SelectQuery<EipTProjectTaskComment> query =
        Database.query(EipTProjectTaskComment.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTaskComment.EIP_TPROJECT_TASK_PROPERTY,
        taskId));

      List<EipTProjectTaskComment> list = query.fetchList();

      /** ファイル操作オブジェクト */
      ProjectFile<EipTProjectTaskCommentFile, EipTProjectTaskComment> pfile =
        new ProjectFile<EipTProjectTaskCommentFile, EipTProjectTaskComment>(
          EipTProjectTaskCommentFile.class);

      for (EipTProjectTaskComment comment : list) {
        ALEipUser user =
          ALEipUtils.getALEipUser(comment.getTurbineUser().getUserId());
        ProjectTaskCommentResultData data = new ProjectTaskCommentResultData();
        data.initField();
        data.setCommentId(comment.getCommentId());
        data.setTaskId(comment.getTaskId());
        data.setComment(comment.getComment());
        data.setCreateUserId(user.getUserId().getValue());
        data.setCreateUser(user.getAliasName().getValue());
        data.setCreateDate(comment.getCreateDate());
        data.setUpdateDate(comment.getUpdateDate());
        data.setHasPhoto(user.hasPhoto());

        // ファイルリスト
        List<EipTProjectTaskCommentFile> filelist =
          pfile.getSelectQueryForFiles(
            EipTProjectTaskComment.COMMENT_ID_PK_COLUMN,
            Integer.valueOf(comment.getCommentId())).fetchList();
        data.setAttachmentFiles(pfile.getFileList(filelist));

        commentList.add(data);
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
    }

    return commentList;
  }

  /**
   * タスクに紐づくコメントを削除する
   * 
   * @param task
   *          タスクオブジェクト
   * @return TRUE 成功 FALSE 失敗
   */
  public static boolean removeProjectTaskComment(RunData rundata,
      EipTProjectTask task) {

    try {
      // タスクに紐づくコメント
      SelectQuery<EipTProjectTaskComment> query =
        Database.query(EipTProjectTaskComment.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipTProjectTaskComment.EIP_TPROJECT_TASK_PROPERTY,
        task));

      List<EipTProjectTaskComment> taskCommentList = query.fetchList();

      if (taskCommentList == null || taskCommentList.isEmpty()) {
        return true;
      }

      /** ファイル操作オブジェクト */
      ProjectFile<EipTProjectTaskCommentFile, EipTProjectTaskComment> pfile =
        new ProjectFile<EipTProjectTaskCommentFile, EipTProjectTaskComment>(
          EipTProjectTaskCommentFile.class);

      for (EipTProjectTaskComment comment : taskCommentList) {
        // タスクコメントに紐づくファイルを削除
        pfile.removeFile(
          rundata,
          EipTProjectTaskCommentFile.EIP_TPROJECT_TASK_COMMENT_PROPERTY,
          comment.getCommentId());
      }

      // タスクに紐づくコメントを削除
      Database.deleteAll(taskCommentList);

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return false;
    }

    return true;
  }

  // ---------------------------------------------------
  // ファイル情報取得
  // ---------------------------------------------------

  /**
   * ファイル検索のクエリを返します
   * 
   * @param requestid
   *          ファイルを検索するリクエストのid
   * @return query
   */
  public static SelectQuery<EipTProjectFile> getSelectQueryForFiles(
      int requestid) {
    SelectQuery<EipTProjectFile> query = Database.query(EipTProjectFile.class);
    Expression exp =
      ExpressionFactory.matchDbExp(EipTProject.PROJECT_ID_PK_COLUMN, Integer
        .valueOf(requestid));
    query.setQualifier(exp);
    return query;
  }

  /**
   * ファイルオブジェクトモデルを取得します。 <BR>
   * 
   * @param rundata
   * @param context
   * @return
   */
  public static EipTProjectFile getEipTProjectFile(RunData rundata)
      throws ALPageNotFoundException, ALDBErrorException {
    try {
      int attachmentIndex =
        rundata.getParameters().getInt("attachmentIndex", -1);
      if (attachmentIndex < 0) {
        // ID が空の場合
        logger.debug("[ProjectUtils] Empty ID...");
        throw new ALPageNotFoundException();

      }

      SelectQuery<EipTProjectFile> query =
        Database.query(EipTProjectFile.class);
      Expression exp =
        ExpressionFactory.matchDbExp(EipTProjectFile.FILE_ID_PK_COLUMN, Integer
          .valueOf(attachmentIndex));
      query.andQualifier(exp);
      List<EipTProjectFile> files = query.fetchList();
      if (files == null || files.size() == 0) {
        // 指定した ID のレコードが見つからない場合
        logger.debug("[ProjectUtils] Not found ID...");
        throw new ALPageNotFoundException();
      }
      return files.get(0);
    } catch (Exception ex) {
      logger.error("[ProjectUtils]", ex);
      throw new ALDBErrorException();
    }
  }

  // ---------------------------------------------------
  // 区分値取得
  // ---------------------------------------------------
  /**
   * 指定区分の区分を取得します。
   * 
   * @param key
   *          区分名
   * @return 区分
   */
  public static EipMProjectKubun getEipMProjectKubun(String key) {

    try {

      SelectQuery<EipMProjectKubun> query =
        Database.query(EipMProjectKubun.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipMProjectKubun.PROJECT_KUBUN_CD_PROPERTY,
        key));

      return query.fetchSingle();

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * 指定区分の区分値を取得します。
   * 
   * 
   * @param cd
   *          区分コード
   * @param valCd
   *          区分値コード
   * @return 区分値マップ
   */
  public static EipMProjectKubunValue getEipMProjectKubunValue(String cd,
      String valCd) {

    try {

      // 区分
      EipMProjectKubun kubun = getEipMProjectKubun(cd);

      SelectQuery<EipMProjectKubunValue> query =
        Database.query(EipMProjectKubunValue.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipMProjectKubunValue.PROJECT_KUBUN_PROPERTY,
        kubun));
      query.andQualifier(ExpressionFactory.matchExp(
        EipMProjectKubunValue.PROJECT_KUBUN_VALUE_CD_PROPERTY,
        valCd));

      return query.fetchSingle();

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
  }

  /**
   * 指定区分の区分値マップを取得します。
   * 
   * @param cd
   *          区分コード
   * @return 区分値マップ
   */
  public static Map<String, String> getKubunMap(String cd) {

    try {

      // 区分
      EipMProjectKubun kubun = getEipMProjectKubun(cd);

      Map<String, String> map = new LinkedHashMap<String, String>();

      SelectQuery<EipMProjectKubunValue> query =
        Database.query(EipMProjectKubunValue.class);
      query.setQualifier(ExpressionFactory.matchExp(
        EipMProjectKubunValue.PROJECT_KUBUN_PROPERTY,
        kubun));
      query.orderAscending(EipMProjectKubunValue.ORDER_NO_PROPERTY);

      List<EipMProjectKubunValue> list = query.fetchList();

      for (EipMProjectKubunValue value : list) {
        map.put(value.getProjectKubunValueCd(), value.getProjectKubunValue());
      }

      return map;

    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }

  }

  /**
   * 区分値を取得する
   * 
   * @param cd
   *          区分コード
   * @param valCd
   *          区分値コード
   * @return 区分値文字列
   */
  public static String getKubunValueString(String key, String valCd) {
    try {
      EipMProjectKubunValue val = getEipMProjectKubunValue(key, valCd);
      return val.getProjectKubunValue();

    } catch (Exception e) {
      return null;
    }
  }

  // ---------------------------------------------------
  // 日付用
  // ---------------------------------------------------

  /**
   * 空設定用日付を取得する
   * 
   * @return 日付
   */
  public static Date getEmptyDate() {
    Calendar cal = Calendar.getInstance();
    cal.set(9999, 11, 31);
    return cal.getTime();
  }

  /**
   * 空設定用日付かをチェックする
   * 
   * @param date
   *          日付
   * @return TRUE:空設定日付 FALSE:通常の日付
   */
  public static boolean isEmptyDate(Date date) {
    if (date == null) {
      return false;
    }
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    return calendar.get(Calendar.YEAR) == 9999;
  }

  /**
   * 経過日数を計算します。<br />
   * 差分日数ではなく当日も含めたものとなります。<br />
   * 例：from:2013/11/11 to:2013/11/11 → return:1
   * 
   * @param from
   *          開始日
   * @param to
   *          終了日
   * @return 経過日数
   */
  public static int getLapsedDays(Calendar from, Calendar to) {
    return getLapsedDays(toString(from.getTime()), toString(to.getTime()));
  }

  /**
   * 経過日数を計算します。<br />
   * 差分日数ではなく当日も含めたものとなります。<br />
   * 例：from:2013/11/11 to:2013/11/11 → return:1
   * 
   * @param from
   *          開始日
   * @param to
   *          終了日
   * @return 経過日数
   */
  public static int getLapsedDays(String from, String to) {

    if (EMPTY_DATE.equals(from) || EMPTY_DATE.equals(to)) {
      return 0;
    }

    SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
    Date dateFrom = null;
    Date dateTo = null;

    try {
      dateFrom = sdf.parse(from);
      dateTo = sdf.parse(to);
    } catch (Exception ex) {
      logger.error("Exception", ex);
      return 0;
    }
    return (int) ((dateTo.getTime() - dateFrom.getTime())
      / (1000 * 60 * 60 * 24) + 1);
  }

  /**
   * 文字列をDate型に変換します。
   * 
   * @param str
   *          日付文字列
   * @return 日付
   */
  public static Date toDate(String str) {
    try {
      return (new SimpleDateFormat(DATE_FORMAT)).parse(str);
    } catch (ParseException e) {
      return null;
    }
  }

  /**
   * Date型を文字列に変換します。
   * 
   * @param date
   *          日付
   * @return 日付文字列
   */
  public static String toString(Date date) {
    if (date == null) {
      return null;
    }
    return (new SimpleDateFormat(DATE_FORMAT)).format(date);
  }

  /**
   * 年月日フォームよりカレンダークラスを取得する（検索用）
   * 
   * @param rundata
   *          Rundata
   * @param context
   *          Context
   * @param key
   *          日付フォーム名
   * @return 入力日カレンダー
   */
  public static Calendar getViewCalendar(RunData rundata, Context context,
      String key) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);

    String year = getParameter(rundata, context, key + "_year");
    String month = getParameter(rundata, context, key + "_month");
    String day = getParameter(rundata, context, key + "_day");

    if (year != null) {
      cal.set(Calendar.YEAR, Integer.valueOf(year));
    }
    if (month != null) {
      cal.set(Calendar.MONTH, Integer.valueOf(month) - 1);
    }
    if (day != null) {
      cal.set(Calendar.DAY_OF_MONTH, Integer.valueOf(day));
    }

    return cal;
  }

  // ---------------------------------------------------
  // その他
  // ---------------------------------------------------

  /**
   * 入力値を取得する。フォーム値、セッション値の順で取得する。
   * 
   * @param rundata
   *          Rundata
   * @param context
   *          Context
   * @param key
   *          フォーム名
   * @return 値
   */
  public static String getParameter(RunData rundata, Context context, String key) {
    String val = rundata.getParameters().getString(key);
    if (val == null) {
      val = ALEipUtils.getTemp(rundata, context, key);
    } else {
      ALEipUtils.setTemp(rundata, context, key, val);
    }
    return val;
  }

  public static void setParameterList(RunData rundata, Context context) {
    String filter = rundata.getParameters().getString("filter", "");
    String filterType = rundata.getParameters().getString("filtertype", "");
    if (filterType.isEmpty()) {
      return;
    }

    if (filterType
      .equals("target_user_id,target_tracker,target_priority,target_status")) {
      String[] filterTypeList = filterType.split(",");
      String[] filterList = filter.split(",");
      for (int i = 0; i <= 4; i++) {
        ALEipUtils.setTemp(rundata, context, filterTypeList[i], filterList[i]);

      }

    }
  }

  /**
   * 予定進捗率を計算します。
   * 
   * @param lapsedDays
   *          経過日数
   * @param taskDays
   *          タスク日数
   * @return 予定進捗率
   */
  public static Integer getPlanWorkload(Integer lapsedDays, Integer taskDays) {

    if (taskDays == 0) {
      return 0;
    }

    Integer planProgressRate = lapsedDays * 100 / taskDays;
    if (planProgressRate < 0) {
      planProgressRate = 0;
    }
    return planProgressRate;
  }

  /**
   * ユーザ毎のルート保存先（絶対パス）を取得します。
   * 
   * @param uid
   * @return
   */
  public static String getSaveDirPath(String orgId, int uid) {
    return ALStorageService.getDocumentPath(
      ProjectFile.FOLDER_FILEDIR,
      ProjectFile.CATEGORY_KEY + ALStorageService.separator() + uid);
  }

  /**
   * タスクのパンくずリストを取得します。
   * 
   * @param taskId
   *          タスクID
   * @return パンくずリスト
   */
  public static String getTaskTopicPath(Integer projectId) {
    String path = "";
    EipTProject project = getEipTProject(projectId);
    if (project != null) {
      path = project.getProjectName();
    }
    return path;
  }

  public static String getEscapedStringForMysql(String text) {
    if (StringUtils.isEmpty(text)) {
      return text;
    }
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < text.length(); i++) {
      char c = text.charAt(i);
      switch (c) {
        case 0: /* Must be escaped for 'mysql' */
          buf.append('\\');
          buf.append('0');
          break;
        case '\n': /* Must be escaped for logs */
          buf.append('\\');
          buf.append('n');
          break;
        case '\r':
          buf.append('\\');
          buf.append('r');
          break;
        case '\\':
          buf.append('\\');
          buf.append('\\');
          break;
        case '\'':
          buf.append('\\');
          buf.append('\'');
          break;
        case '"': /* Better safe than sorry */
          buf.append('\\');
          buf.append('"');
          break;
        case '\032': /* This gives problems on Win32 */
          buf.append('\\');
          buf.append('Z');
          break;
        default:
          buf.append(c);
      }
    }
    return buf.toString();
  }

  public static String getLikeEnclosed(String text) {
    return new StringBuffer().append("'%").append(text).append("%'").toString();
  }

  public static String getSingleQuoteEnclosed(String text) {
    return new StringBuffer().append("'").append(text).append("'").toString();
  }

  public static String getCurrentDateWithCast() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    String currentDate = sdf.format(new Date());
    return new StringBuilder()
      .append("CAST(")
      .append(ProjectUtils.getSingleQuoteEnclosed(currentDate))
      .append(" AS DATE)")
      .toString();
  }

  public static Expression getIncompleteExpression() {
    Expression exp = null;
    for (String status : incompleteStatus) {
      Expression tmp =
        ExpressionFactory.matchExp(EipTProjectTask.STATUS_PROPERTY, status);
      if (null == exp) {
        exp = tmp;
      } else {
        exp = exp.orExp(tmp);
      }
    }
    return exp;
  }

  public static String getIncompleteSQL(String column) {
    StringBuilder build = new StringBuilder();
    build.append(" ").append(column).append(" IN (");
    Iterator<String> it = incompleteStatus.iterator();
    while (it.hasNext()) {
      String status = it.next();
      build.append("'").append(status).append("'");
      if (it.hasNext()) {
        build.append(",");
      }
    }
    build.append(") ");
    return build.toString();
  }

  public static List<EipTProjectMember> getProjectMember(Integer projectId) {
    List<EipTProjectMember> result =
      Database.query(EipTProjectMember.class).where(
        Operations.eq("projectId", projectId)).fetchList();
    return result;
  }

  public static Map<String, String> getProjectMemberMap(Integer projectId) {
    Map<String, String> map = new HashMap<String, String>();
    List<EipTProjectMember> result = getProjectMember(projectId);
    if (result.isEmpty()) {
      return map;
    }
    List<Integer> idList = new ArrayList<Integer>();
    for (EipTProjectMember member : result) {
      idList.add(member.getUserId());
    }
    List<ALEipUser> users = getALEipUsers(idList);
    for (EipTProjectMember member : result) {
      for (ALEipUser user : users) {
        if (user.getUserId().getValueWithInt() == member.getUserId().intValue()) {
          map.put(user.getUserId().toString(), user.getAliasName().toString());
          continue;
        }
      }
    }
    return map;
  }

  private static List<TurbineUser> getTurbineUsers(List<Integer> idList) {
    if (idList.isEmpty()) {
      return new ArrayList<TurbineUser>(0);
    }
    SelectQuery<TurbineUser> query = Database.query(TurbineUser.class);
    Expression inExp =
      ExpressionFactory.inDbExp(TurbineUser.USER_ID_PK_COLUMN, idList);
    query.setQualifier(inExp);
    return query.fetchList();
  }

  private static List<ALEipUser> getALEipUsers(List<Integer> idList) {
    List<TurbineUser> list = getTurbineUsers(idList);
    List<ALEipUser> users = new ArrayList<ALEipUser>();
    for (TurbineUser user : list) {
      try {
        users.add(ALEipUtils.getALEipUser(user));
      } catch (ALDBErrorException ignore) {
        // no occur
      }
    }
    return users;
  }
}
