/*
 * Created on 12/12/2003
 *
 */
package com.datacodex.spring.xwork;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.datacodex.coeus.Service;
import com.datacodex.coeus.blapi.BLUnit;
import com.opensymphony.webwork.ServletActionContext;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.AroundInterceptor;

/**
 * @author CameronBraid
 * 
 * An XWORK Interceptor that auto wires an action.
 * 
 * All setters on the action are examined to determine which of them 
 * are assignable from com.datacodex.coeus.Service or com.datacodex.coeus.blapi.BLUnit
 * 
 * Those that are assignable, are resolved by class name - WebApplicationContext.getBeansOfType(..) 
 * from the spring WebApplicationContext and are set onto the action.
 * 
 * If multiple beans of the same type are found fromt he spring context, the first one is used. 
 *
 */
public class SpringAutoWireInterceptor extends AroundInterceptor
{

	/**
	 * 
	 */
	public SpringAutoWireInterceptor() {
		super();
	}

	

	/* (non-Javadoc)
	 * @see com.opensymphony.xwork.interceptor.AroundInterceptor#after(com.opensymphony.xwork.ActionInvocation, java.lang.String)
	 */
	protected void after(ActionInvocation dispatcher, String result) throws Exception {
	}

	/* (non-Javadoc)
	 * @see com.opensymphony.xwork.interceptor.AroundInterceptor#before(com.opensymphony.xwork.ActionInvocation)
	 */
	protected void before(ActionInvocation invocation) throws Exception
	{
		WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext());

		Method methods[] = invocation.getAction().getClass().getMethods();

	   for (int i = 0; i < methods.length; i++)
	   {
			if (methods[i].getName().startsWith("set") && methods[i].getParameterTypes().length == 1)
			{
				Class propertyType = methods[i].getParameterTypes()[0];

				if (Service.class.isAssignableFrom(propertyType) || BLUnit.class.isAssignableFrom(propertyType)) {

					String propertyName = methods[i].getName().substring(3);
					if (propertyName.length() == 1) { 
						propertyName = propertyName.toLowerCase();
					} else {
						propertyName = propertyName.substring(0,1).toLowerCase() + propertyName.substring(1); 
					}

					wireProperty(invocation.getAction(), context, propertyName, propertyType);
				}
			}
	   }
		
	}

	private void wireProperty(Action action, ApplicationContext context, String propertyName, Class propertyType)
	{
		Map beans = context.getBeansOfType(propertyType, true, true);
		if (beans.size() > 0)
		{
			Object bean = beans.values().iterator().next();
			try
			{
				BeanUtils.setProperty(action, propertyName, bean);
			}
			catch (IllegalAccessException e)
			{
				e.printStackTrace();
			}
			catch (InvocationTargetException e)
			{
				e.printStackTrace();
			}
		}
		else 
		{
			throw new RuntimeException("bean not availible from spring context for action " + action.getClass().getName() + "." + propertyName + " " + propertyType);
		}
		
//		Map ognlContext = Ognl.createDefaultContext(action);
//		OgnlUtil.setProperty(propertyName, bean, action, ognlContext);
	}

}
