/*
 * Created on May 8, 2004
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.anodyneos.xp.standard;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.VariableResolver;

import org.anodyneos.xp.XpContentHandler;
import org.anodyneos.xp.XpException;
import org.anodyneos.xp.tagext.XpTagSupport;

/**
 * SetTag supports setting a value of a scripting variable or a property of a
 * target that may be a java bean or Map.
 *
 * <xp:set @value @var [@scope]/>
 *
 * <xp:set @var [@scope]>bodyContent </xp:set> (Not yet supported)
 *
 * <xp:set @value @target @property/>
 *
 * <xp:set @target @property>bodyContent </xp:set> (Not yet supported)
 *
 * @author jvas
 */
public class SetTag extends XpTagSupport {

    private Object value;
    private Object target;
    private String property;
    private String scope;
    private String var;

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

    public void setValue(Object value) {
        this.value = value;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public void setVar(String var) {
        this.var = var;
    }

    public void doTag(XpContentHandler out) throws XpException, ELException {
        if (null != var) {
            // simple case, setting a scripting variable
            if (null == scope) {
                getXpContext().setAttribute(var, value);
            } else {
                getXpContext().setAttribute(var, value, getXpContext().resolveScope(scope));
            }
        } else {
            // must have target and property
            if (target instanceof Map) {
                if(value == null) {
                    ((Map) target).remove(property);
                } else {
                    ((Map) target).put(property, value);
                }
            } else if (target != null) {
                // must be a javaBean
                try {
                    PropertyDescriptor pd[] =
                        Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors();
                    boolean succeeded = false;
                    for (int i=0; i < pd.length; i++) {
                        if(pd[i].getName().equals(property)) {
                            Method m = pd[i].getWriteMethod();
                            if (m == null) {
                                throw new XpException("No setter method");
                            }
                            if (value != null) {
                                try {
                                    m.invoke(target, new Object[] { convertToExpectedType(value, m.getParameterTypes()[0])});
                                } catch (ELException ex) {
                                    throw new XpException(ex.getMessage());
                                }
                            } else {
                                m.invoke(target, new Object[] {null});
                            }
                            succeeded = true;
                        }
                    }
                    if (!succeeded) {
                        throw new XpException("No setter method");
                    }
                } catch (IllegalAccessException ex) {
                    throw new XpException(ex.getMessage());
                } catch (IntrospectionException ex) {
                    throw new XpException(ex.getMessage());
                } catch (InvocationTargetException ex) {
                    throw new XpException(ex.getMessage());
                }
            } else {
                throw new XpException("Must provide target or var attribute");
            }
        }
    }

    /**
     * Convert an object to an expected type according to the conversion
     * rules of the Expression Language.
     */
    private Object convertToExpectedType( final Object val, Class expectedType )
           throws ELException {
        ExpressionEvaluator evaluator = getXpContext().getExpressionEvaluator();
        return evaluator.evaluate( "${result}", expectedType,
            new VariableResolver() {
                public Object resolveVariable( String pName ) {
                    return val;
                }
            }, null );
    }

}

@@ -670,16 +670,49 @@
 Support;
+%0Aimport org.xml.sax.SAXException;
 %0A%0A/**%0A *
@@ -915,36 +915,16 @@
 /xp:set%3E
- (Not yet supported)
 %0A *%0A * %3C
@@ -1014,28 +1014,8 @@
 set%3E
- (Not yet supported)
 %0A *%0A
@@ -1735,34 +1735,284 @@
 ion, ELException
- %7B
+, SAXException %7B%0A        if (null == value) %7B%0A            if (getXpBody() != null) %7B%0A                // get value from body%0A                value = getXpBody().invokeToString();%0A            %7D else %7B%0A                value = null;%0A            %7D%0A        %7D
 %0A        if (nul

add ability to have value in body of <c:set>