/*
 * Copyright (c) 2003, Morpho Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the Morpho Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package jp.morpho.webapp.action.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import jp.morpho.webapp.action.AbstractActionMappingManager;
import jp.morpho.webapp.action.ActionHandler;
import jp.morpho.webapp.action.ActionHandlerChain;
import jp.morpho.webapp.action.ActionMappingNotFoundException;

/**
 * <P>
 * ActionMappingManager の実装です。
 * デフォルトのアクションマッピングマネージャで、ActionMappingManager の基本動作を実装します。
 * 必要ならば、このクラスを拡張するか、または新たに ActionMappingManager を実装して独自のアクションマッピングマネージャを作成することができます。
 * </P>
 * <P>
 * 新たに ActionMappingManager を実装する場合は、AbstractActionMappingManager クラスを拡張して必要なメソッドを実装します。
 * </P>
 * @author Kumiko Hiroi
 */
public class ActionMappingManagerImpl
	extends AbstractActionMappingManager
{

	/** 要求を処理するステータスのパラメータの名前 */
	private String statusParameterName = null;

	/** 要求を処理するアクションのパラメータの名前 */
	private String actionParameterName = null;

	/** 要求を受け取るサーブレットのパスのマップ */
	private Map actionServletPaths = new HashMap();

	/**
	 * <P>
	 * ActionMappingManagerImpl を作成します。
	 * </P>
	 */
	public ActionMappingManagerImpl()
	{
		super();
	}

	/**
	 * <P>
	 * 要求を処理するステータスのパラメータの名前を返します。
	 * </P>
	 * @return 要求を処理するステータスのパラメータの名前
	 * @see jp.morpho.webapp.action.ActionMappingManager#getStatusParameterName()
	 */
	public synchronized String getStatusParameterName()
	{
		return statusParameterName;
	}

	/**
	 * <P>
	 * 要求を処理するステータスのパラメータの名前を設定します。
	 * </P>
	 * @param statusParameterName 要求を処理するステータスのパラメータの名前
	 */
	public synchronized void setStatusParameterName(String statusParameterName)
	{
		this.statusParameterName = statusParameterName;
	}

	/**
	 * <P>
	 * 要求を処理するアクションのパラメータの名前を返します。
	 * </P>
	 * @return 要求を処理するアクションのパラメータの名前
	 * @see jp.morpho.webapp.action.ActionMappingManager#getActionParameterName()
	 */
	public synchronized String getActionParameterName()
	{
		return actionParameterName;
	}

	/**
	 * <P>
	 * 要求を処理するアクションのパラメータの名前を設定します。
	 * </P>
	 * @param actionParameterName 要求を処理するアクションのパラメータの名前
	 */
	public synchronized void setActionParameterName(String actionParameterName)
	{
		this.actionParameterName = actionParameterName;
	}

	/**
	 * <P>
	 * アクションハンドラチェインを返します。
	 * アクションハンドラチェインは、ActionMappingManager の getActionHandlers(String, String, String) メソッドと getActionView(String, String) メソッドを呼び出します。
	 * </P>
	 * @param servletPath アクションマッピングが対応する URL
	 * @param status 要求を処理するステータスの値
	 * @param action 要求を処理するアクションの値
	 * @return アクションハンドラチェイン
	 * @see jp.morpho.webapp.action.ActionMappingManager#getActionHandlerChain(String, String, String)
	 */
	public synchronized ActionHandlerChain getActionHandlerChain(
		String servletPath,
		String status,
		String action)
	{
		return new ActionHandlerChainImpl(this, servletPath, status, action);
	}

	/**
	 * <P>
	 * アクションハンドラの配列を返します。
	 * 返されたアクションハンドラの配列は、アクションハンドラチェインの doActionHandler メソッドで順に呼び出されます。
	 * </P>
	 * <P>
	 * このメソッドは、要求を処理するアクションハンドラを取得するために、アクションハンドラチェインから呼び出されます。
	 * </P>
	 * @param servletPath アクションマッピングが対応する URL
	 * @param status 要求を処理するステータスの値
	 * @param action 要求を処理するアクションの値
	 * @return アクションハンドラの配列
	 * @exception ActionMappingNotFoundException クライアントから呼び出されたサーブレットの URL 、ステータス、アクションを使用してアクションマッピングを取得しようとしたが、対応するアクションマッピングの定義が見つからなかった場合
	 * @see jp.morpho.webapp.action.ActionMappingManager#getActionHandlers(String, String, String)
	 */
	public synchronized ActionHandler[] getActionHandlers(
		String servletPath,
		String status,
		String action)
		throws ActionMappingNotFoundException
	{
		//要求を受け取ったサーブレットのパスをマッピングから取得します
		//取得できなかった場合は、空のアクションハンドラの配列を返します
		ActionServletPath asp = getActionServletPath(servletPath);
		if (asp == null)
		{
			return new ActionHandler[0];
		}

		//アクションハンドラのエントリをマッピングから取得します
		//取得できなかった場合は、空のアクションハンドラの配列を返します
		ActionHandlerEntry entry = asp.getActionHandlerEntry(status, action);
		if (entry == null || entry.size() == 0)
		{
			return new ActionHandler[0];
		}

		//アクションハンドラクラスをロードして、アクションハンドラの配列を返します
		ArrayList actionHandlers = new ArrayList();
		for (int i = 0; i < entry.size(); i++)
		{
			String className = (String)entry.get(i);
			try
			{
				ClassLoader cl = Thread.currentThread().getContextClassLoader();
				if (cl == null)
				{
					cl = getClass().getClassLoader();
				}
				actionHandlers.add((ActionHandler)cl.loadClass(className).newInstance());
			}
			catch (Exception e)
			{
				throw new ActionMappingNotFoundException(e.getMessage());
			}
		}
		return (ActionHandler[])actionHandlers.toArray(new ActionHandler[actionHandlers.size()]);
	}

	/**
	 * <P>
	 * アクションビューのパス (JSP など) を返します。
	 * アクションビューは、アクションハンドラチェインの doActionHandler メソッドで、アクションハンドラが呼び出された後、応答にインクルードされます。
	 * </P>
	 * <P>
	 * ステータスの値は、アクションハンドラで変更されることがあるため、サーブレットが要求を受け取った時の値と必ずしも一致しません。
	 * このメソッドは、アクションハンドラチェインの doActionHandler メソッドで、すべてのアクションハンドラが実行された後に呼び出されます。
	 * </P>
	 * @param servletPath アクションマッピングが対応する URL
	 * @param status 要求を処理するステータスの値
	 * @return アクションビューのパス
	 * @exception ActionMappingNotFoundException クライアントから呼び出されたサーブレットの URL 、ステータスを使用してアクションマッピングを取得しようとしたが、対応するアクションマッピングの定義が見つからなかった場合
	 * @see jp.morpho.webapp.action.ActionMappingManager#getActionView(String, String)
	 */
	public synchronized String getActionView(String servletPath, String status)
		throws ActionMappingNotFoundException
	{
		//要求を受け取ったサーブレットのパスをマッピングから取得します
		//取得できなかった場合は、例外をスローします
		ActionServletPath asp = getActionServletPath(servletPath);
		if (asp == null)
		{
			StringBuffer msg = new StringBuffer();
			msg.append("servletPath not found: ");
			msg.append("servletPath=");
			msg.append(servletPath);
			msg.append(",status=");
			msg.append(status);
			throw new ActionMappingNotFoundException(msg.toString());
		}

		//アクションビューのエントリをマッピングから取得します
		//取得できなかった場合は、例外をスローします
		ActionViewEntry entry = asp.getActionViewEntry(status);
		if (entry == null)
		{
			StringBuffer msg = new StringBuffer();
			msg.append("ActionViewEntry not found: ");
			msg.append("servletPath=");
			msg.append(servletPath);
			msg.append(",status=");
			msg.append(status);
			throw new ActionMappingNotFoundException(msg.toString());
		}

		//アクションビューのパスを返します
		return entry.getPath();
	}

	/**
	 * <P>
	 * 要求を処理するサーブレットのパスのエントリに指定のエントリを追加します。
	 * </P>
	 * @param asp 要求を処理するサーブレットのパスのエントリに追加されるエントリ
	 * @exception IllegalArgumentException 同じパスのエントリがあった場合
	 */
	public synchronized void addActionServletPath(ActionServletPath asp)
	{
		String key = asp.getServletPath();
		if (actionServletPaths.containsKey(key))
		{
			throw new IllegalArgumentException("ActionServletPath already exists: " + key);
		}
		actionServletPaths.put(key, asp);
	}

	/**
	 * <P>
	 * 指定されたパスの要求を処理するサーブレットのパスのエントリを返します。
	 * </P>
	 * @param servletPath 要求を処理するサーブレットのパス
	 */
	public synchronized ActionServletPath getActionServletPath(String servletPath)
	{
		return (ActionServletPath)actionServletPaths.get(servletPath);
	}

}
