ActionContextJavaScriptResolution.java

package sk.iway.iwcm.system.stripes;

import java.util.ArrayList;
import java.util.List;

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

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Message;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.ajax.JavaScriptBuilder;
import net.sourceforge.stripes.validation.ValidationError;
import net.sourceforge.stripes.validation.ValidationErrors;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.PageLng;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.stripes.RegUserAction;

/**
 *  ActionContextJavaScriptResolution.java
 *  <p>Resolution that will convert a Java object web to a web of JavaScript objects and arrays, and
 * stream the JavaScript back to the client.  The output of this resolution can be evaluated in
 * JavaScript using the eval() function, and will return a reference to the top level JavaScript
 * object.  For more information see {@link JavaScriptBuilder}</p>
 *
 * <p>Resolution builds up result from the object of current ActionBean context returning simple
 * containing involved validation errors if they occur.</p>
 *
 * source: http://blog.novoj.net/2008/01/25/running-ajax-with-jquery-in-stripes-framework/
 *
 *@Title        webjet4
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2008
 *@author       $Author: jeeff $
 *@version      $Revision: 1.3 $
 *@created      Date: 3.2.2008 15:21:35
 *@modified     $Date: 2008/07/29 14:42:14 $
 */
public class ActionContextJavaScriptResolution implements Resolution
{
	private JavaScriptBuilder builder;

	/**
	 * Constructs a new JavaScriptResolution that will convert the supplied object to JavaScript.
	 *
	 * @param actionBean
	 */
	public ActionContextJavaScriptResolution(ActionBean actionBean)
	{
		ActionBeanContext ctx = actionBean.getContext();
		Object result;
		HttpServletRequest request = ctx.getRequest();
		ValidationErrors validationErrors = ctx.getValidationErrors();
		int userId = -1;

		if(actionBean instanceof RegUserAction && ((RegUserAction)actionBean).getUsr() != null)
			userId = ((RegUserAction)actionBean).getUsr().getUserId();

		if (!validationErrors.isEmpty())
		{
			result = new AjaxPostFormResult(validationErrors, composeErrorsHtml(validationErrors, request), userId);

			((AjaxPostFormResult)result).updateCsrfToken(request);
		}
		else
		{
			result = new AjaxPostFormResult(composeMessagesHtml(ctx.getMessages(), request), composeErrorsHtml(ctx.getMessages(),
						request), userId);

			((AjaxPostFormResult)result).updateCsrfToken(request);
		}
		this.builder = new JavaScriptBuilder(result);
	}

	/**
	 * Converts the object passed in to JavaScript and streams it back to the client.
	 */
	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception
	{
		//response.setContentType("text/javascript; charset="+response.getCharacterEncoding());

		sk.iway.iwcm.Encoding.setResponseEnc(request, response, "text/javascript");

		String ajaxRedirectCode = (String)request.getAttribute("ajaxRedirectCode");
		if (Tools.isNotEmpty(ajaxRedirectCode))
		{
			response.getWriter().println(ajaxRedirectCode);
		}

		this.builder.build(response.getWriter());

		response.flushBuffer();
	}

	/**
	 * Compose HTML snippet with messages based on standard properties enveloping used by Stripes
	 * <html:messages/> tag.
	 * @param messages
	 * @param request
	 * @return
	 */
	private String composeMessagesHtml(List<Message> messages, ServletRequest request)
	{
		String[] properties = getStandardMessageProperties(request);
		return composeResult(request, messages, properties, new MessageFilter()
		{
			@Override
			public boolean includeMessage(Message message)
			{
				return !(message instanceof ValidationError);
			}
		});
	}

	/**
	 * Compose HTML snippet with messages based on standard properties enveloping used by Stripes
	 * <html:errors/> tag.
	 * @param messages
	 * @param request
	 * @return
	 */
	private String composeErrorsHtml(List<Message> messages, ServletRequest request)
	{
		String[] properties = getStandardErrorProperties(request);
		return composeResult(request, messages, properties, new MessageFilter()
		{
			@Override
			public boolean includeMessage(Message message)
			{
				return (message instanceof ValidationError);
			}
		});
	}

	/**
	 * Compose HTML snippet with messages based on standard properties enveloping used by Stripes
	 * <html:messages/> tag.
	 * @param errors
	 * @param request
	 * @return
	 */
	public String composeErrorsHtml(ValidationErrors errors, ServletRequest request)
	{
		String[] properties = getStandardErrorProperties(request);

		if (errors != null && errors.size()==1 && errors.get("notSendMessage")!=null)
		{
			properties = getNotSendErrorProperties(request);
		}

		List<Message> messages = new ArrayList<>();
		if (errors != null) {
			for (List<ValidationError> validationErrors : errors.values())
			{
				messages.addAll(validationErrors);
			}
		}
		return composeResult(request, messages, properties, new MessageFilter()
		{
			@Override
			public boolean includeMessage(Message message)
			{
				return true;
			}
		});
	}

	/**
	 * Returns standard message Stripes properties in array.
	 * @param request
	 * @return
	 */
	private String[] getStandardMessageProperties(ServletRequest request)
	{
		Prop prop = Prop.getInstance(PageLng.getUserLng((HttpServletRequest) request));
		//ResourceBundle bundle = StripesFilterIway.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(locale);
		return new String[]{prop.getText("stripes.messages.header"), prop.getText("stripes.messages.footer"),
					prop.getText("stripes.messages.beforeMessage"), prop.getText("stripes.messages.afterMessage")};
	}

	/**
	 * Returns standard message Stripes properties in array.
	 * @param request
	 * @return
	 */
	private String[] getStandardErrorProperties(ServletRequest request)
	{
		//Locale locale = request.getLocale();
		//ResourceBundle bundle = StripesFilterIway.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(locale);

		Prop prop = Prop.getInstance(PageLng.getUserLng((HttpServletRequest) request));

		return new String[]{prop.getText("stripes.ajax.errors.header"), prop.getText("stripes.ajax.errors.footer"),
					prop.getText("stripes.ajax.errors.beforeError"), prop.getText("stripes.ajax.errors.afterError")};
	}

	private String[] getNotSendErrorProperties(ServletRequest request)
	{
		//Locale locale = request.getLocale();
		//ResourceBundle bundle = StripesFilterIway.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(locale);

		Prop prop = Prop.getInstance(PageLng.getUserLng((HttpServletRequest) request));

		return new String[]{prop.getText("stripes.ajax.errors.header_not_send"), prop.getText("stripes.ajax.errors.footer_not_send"),
					prop.getText("stripes.ajax.errors.beforeError_not_send"), prop.getText("stripes.ajax.errors.afterError_not_send")};
	}

	/**
	 * Renders HTML snippet result containing all messages conforming to MessageFilter implementation
	 * enveloped by properties strings.
	 * @param request
	 * @param messages
	 * @param properties
	 * @param messageFilter
	 * @return
	 */
	private String composeResult(ServletRequest request, List<Message> messages, String[] properties, MessageFilter messageFilter)
	{
		boolean hasMessages = false;
		StringBuilder result = new StringBuilder(properties[0]);
		for (Message message : messages)
		{
			if (messageFilter.includeMessage(message))
			{
				hasMessages = true;
				result.append(properties[2]).append(message.getMessage(request.getLocale())).append(properties[3]);
			}
		}
		if (hasMessages)
		{
			result.append(properties[1]);
			Logger.debug(ActionContextJavaScriptResolution.class, "composeResult:"+result.toString());
			return result.toString();
		}
		else
		{
			Logger.debug(ActionContextJavaScriptResolution.class, "composeResult: null");
			return null;
		}
	}

	/**
	 * Interface used to filter messages going to output.
	 */
	private interface MessageFilter
	{
		/**
		 * Returns true or false if message should render in output.
		 * @param message
		 * @return
		 */
		boolean includeMessage(Message message);
	}
}