/*
 * $Id: ServletActionContext.java 471754 2006-11-06 14:55:09Z husted $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.struts.chain.contexts;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.chain2.web.servlet.ServletWebContext;
import org.apache.commons.chain2.web.servlet.ServletWebContextBase;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.chain.Constants;
import org.apache.struts.config.ActionConfig;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.util.MessageResources;

/**
 * <p> Implement ActionContext interface while making Servlet API-specific
 * values available. </p>
 */
public class ServletActionContext extends WebActionContext<ServletWebContext<String, Object>> {

    /**
     * <p> Instantiate this composite by wrapping a ServletWebContext. </p>
     * @param context The ServletWebContext to wrap
     */
    public ServletActionContext(final ServletWebContext<String, Object> context) {
        super(context);
    }

    /**
     * <p> Instantiate this Context for a given ServletContext,
     * HttpServletRequest, and HttpServletResponse. </p>
     *
     * @param context  The instant ServletContext
     * @param request  The instant HttpServletRequest
     * @param response The instant HttpServletResponse
     */
    public ServletActionContext(final ServletContext context,
            final HttpServletRequest request, final HttpServletResponse response) {
        this(new ServletWebContextBase(context, request, response));
    }

    /**
     * <p> Provide the ServletWebContext for this composite. </p>
     *
     * @return Our ServletWebContext
     */
    protected ServletWebContext<String, Object> servletWebContext() {
        return getBaseContext();
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#release()
     */
    @Override
    public void release() {
        servletWebContext().release();
        super.release();
    }

    // -------------------------------
    // Servlet specific properties
    // -------------------------------

    /**
     * <p> Return the ServletContext for this context. </p>
     *
     * @return Our ServletContext
     */
    public ServletContext getContext() {
        return servletWebContext().getContext();
    }

    /**
     * <p> Return the HttpServletRequest for this context. </p>
     *
     * @return Our HttpServletRequest
     */
    public HttpServletRequest getRequest() {
        return servletWebContext().getRequest();
    }

    /**
     * <p> Return the HttpServletResponse for this context. </p>
     *
     * @return Our HttpServletResponse
     */
    public HttpServletResponse getResponse() {
        return servletWebContext().getResponse();
    }

    /**
     * <p> Return the ActionServlet for this context. </p>
     *
     * @return Our ActionServlet
     */
    public ActionServlet getActionServlet() {
        return retrieve(Constants.ACTION_SERVLET_KEY);
    }

    /**
     * <p> Set the ActionServlet instance for this context. </p>
     *
     * @param servlet Our ActionServlet instance
     */
    public void setActionServlet(final ActionServlet servlet) {
        put(Constants.ACTION_SERVLET_KEY, servlet);
    }

    // -------------------------------
    // Servlet specific modifications to base properties.
    // -------------------------------

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#setActionConfig(
     * org.apache.struts.config.ActionConfig)
     */
    @Override
    public void setActionConfig(final ActionConfig actionConfig) {
        super.setActionConfig(actionConfig);
        getRequestScope().put(Globals.MAPPING_KEY, actionConfig);

        // ISSUE: Should we check this call to put?
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#getMessageResources()
     */
    @Override
    public MessageResources getMessageResources() {
        return (MessageResources) getRequest().getAttribute(Globals.MESSAGES_KEY);
    }

    // ISSUE: This method would probably be better handled by a "Struts"
    // object which encapsulated the server (Application) scope.

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#getMessageResources(java.lang.String)
     */
    @Override
    public MessageResources getMessageResources(final String key) {
        // Identify the current module
        ServletContext context = getActionServlet().getServletContext();

        // Return the requested message resources instance
        return (MessageResources) context.getAttribute(key
            + getModuleConfig().getPrefix());
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#setMessageResources(
     * org.apache.struts.util.MessageResources)
     */
    @Override
    public void setMessageResources(final MessageResources resources) {
        super.setMessageResources(resources);
        getRequest().setAttribute(Globals.MESSAGES_KEY, resources);
    }

    /**
     * <p> Store the message resources for the current module under the given
     * request attribute key. </p>
     *
     * @param key       Request attribute key
     * @param resources Message resources to store
     */
    public void setMessageResources(final String key, final MessageResources resources) {
        getRequest().setAttribute(key + getModuleConfig().getPrefix(),
            resources);
    }

    // -------------------------------
    // ActionMessage Processing
    // -------------------------------

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#saveErrors(
     * org.apache.struts.action.ActionMessages)
     */
    @Override
    public void saveErrors(final ActionMessages errors) {
        // Remove any error messages attribute if none are required
        if ((errors == null) || errors.isEmpty()) {
            getRequest().removeAttribute(Globals.ERROR_KEY);
            return;
        }

        // Save the error messages we need
        getRequest().setAttribute(Globals.ERROR_KEY, errors);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#saveMessages(
     * org.apache.struts.action.ActionMessages)
     */
    @Override
    public void saveMessages(final ActionMessages messages) {
        if ((messages == null) || messages.isEmpty()) {
            getRequest().removeAttribute(Globals.MESSAGE_KEY);
            return;
        }

        getRequest().setAttribute(Globals.MESSAGE_KEY, messages);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#addMessages(
     * org.apache.struts.action.ActionMessages)
     */
    @Override
    public void addMessages(final ActionMessages messages) {
        if (messages == null) {
            return;
        }

        ActionMessages requestMessages = getMessages();

        if (requestMessages == null) {
            requestMessages = new ActionMessages();
        }

        requestMessages.add(messages);
        saveMessages(requestMessages);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#addErrors(
     * org.apache.struts.action.ActionMessages)
     */
    @Override
    public void addErrors(final ActionMessages errors) {
        if (errors == null) {
            return;
        }

        ActionMessages requestErrors = getErrors();

        if (requestErrors == null) {
            requestErrors = new ActionMessages();
        }

        requestErrors.add(errors);
        saveErrors(requestErrors);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#getErrors()
     */
    @Override
    public ActionMessages getErrors() {
        return (ActionMessages) getRequest().getAttribute(Globals.ERROR_KEY);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#getMessages()
     */
    @Override
    public ActionMessages getMessages() {
        return (ActionMessages) getRequest().getAttribute(Globals.MESSAGE_KEY);
    }

    // -------------------------------
    // Token Processing
    // Implementing the servlet-aware versions by using the
    // TokenProcessor class
    // directly should ensure greater compatibility.
    // -------------------------------

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#saveToken()
     */
    @Override
    public void saveToken() {
        super.getTokenProcessor().saveToken(getRequest());
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#generateToken()
     */
    @Override
    public String generateToken() {
        return super.getTokenProcessor().generateToken(getRequest());
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#isTokenValid(boolean)
     */
    @Override
    public boolean isTokenValid(final boolean reset) {
        return super.getTokenProcessor().isTokenValid(getRequest(), reset);
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#resetToken()
     */
    @Override
    public void resetToken() {
        super.getTokenProcessor().resetToken(getRequest());
    }

    /**
     * @see org.apache.struts.chain.contexts.ActionContextBase#findOrCreateActionForm(
     * java.lang.String, java.lang.String, org.apache.struts.config.ModuleConfig)
     */
    @Override
    public ActionForm findOrCreateActionForm(final String formName, final String scopeName,
            final ModuleConfig moduleConfig) {
        ActionForm af = super.findOrCreateActionForm(formName, scopeName, moduleConfig);
        return af;
    }
}
