All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.directwebremoting.extend.InboundContext Maven / Gradle / Ivy

package org.directwebremoting.extend;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.ConversionException;

/**
 * InboundContext is the context for set of inbound conversions.
 * Since a data set may be recursive parts of some data members may refer to
 * others so we need to keep track of who is converted for what.
 * @author Joe Walker [joe at getahead dot ltd dot uk]
 */
public final class InboundContext
{
    /**
     * When we are sure we have finished parsing the input, we can begin to
     * fix all cross-references.
     * @throws ConversionException If cross-references don't add up
     */
    public void dereference() throws ConversionException
    {
        for (InboundVariable variable : variables.values())
        {
            variable.dereference();
        }
    }

    /**
     * Someone wants to tell us about a new conversion context.
     * @param context The current conversion context
     */
    public void pushContext(Property context)
    {
        typeHintStack.addFirst(context);
    }

    /**
     * Someone wants to tell us about a finished conversion context.
     */
    public void popContext()
    {
        typeHintStack.removeFirst();
    }

    /**
     * @return The method that we are currently converting data for
     */
    public Property getCurrentProperty()
    {
        return typeHintStack.getFirst();
    }

    /**
     * Create an inbound variable.
     * Usually called by a query parser to setup a list of known variables.
     * This method also checks to see if the new variable is a parameter and if
     * it is it updates the count of parameters
     * @param callNum The call number to work on
     * @param key The name of the variable
     * @param type The javascript type of the variable
     * @param value The value of the variable
     */
    public void createInboundVariable(int callNum, String key, String type, String value)
    {
        InboundVariable cte = new InboundVariable(this, key, type, value);
        checkInboundVariable(callNum, key, cte);
    }

    /**
     * Create an inbound variable.
     * Usually called by a query parser to setup a list of known variables.
     * This method also checks to see if the new variable is a parameter and if
     * it is it updates the count of parameters
     * @param callNum The call number to work on
     * @param key The name of the variable
     * @param type The javascript type of the variable
     * @param value The value of the variable
     * @param urlDecoded value been URL decoded?
     */
    public void createInboundVariable(int callNum, String key, String type, String value, boolean urlDecoded)
    {
        InboundVariable cte = new InboundVariable(this, key, type, value, urlDecoded);
        checkInboundVariable(callNum, key, cte);
    }

    /**
     * Create an inbound file variable.
     * Usually called by a query parser to setup a list of known variables.
     * This method also checks to see if the new variable is a parameter and if
     * it is it updates the count of parameters
     * @param callNum The call number to work on
     * @param key The name of the variable
     * @param type The javascript type of the variable
     * @param value The value of the file
     */
    public void createInboundVariable(int callNum, String key, String type, FormField value)
    {
        InboundVariable iv = new InboundVariable(this, key, type, value);
        checkInboundVariable(callNum, key, iv);
    }

    /**
     * Internal method to check the variable we just created does not already
     * exist, and to ensure that our count of inbound parameters is up to date
     * @param callNum The number of this call
     * @param key The name of the variable
     * @param iv The value to check
     */
    private void checkInboundVariable(int callNum, String key, InboundVariable iv)
    {
        Object old = variables.put(key, iv);
        if (old != null)
        {
            log.warn("Duplicate variable called: " + key);
        }

        String paramPrefix = ProtocolConstants.INBOUND_CALLNUM_PREFIX + callNum +
                             ProtocolConstants.INBOUND_CALLNUM_SUFFIX +
                             ProtocolConstants.INBOUND_KEY_PARAM;

        if (key.startsWith(paramPrefix))
        {
            int i = Integer.parseInt(key.substring(paramPrefix.length())) + 1;
            if (i > paramCount)
            {
                paramCount = i;
            }
        }
    }

    /**
     * Method to allow entries to resolve references
     * @param name The name of the variable to lookup
     * @return The found variable
     */
    public InboundVariable getInboundVariable(String name)
    {
        return variables.get(name);
    }

    /**
     * Clear the list of converted objects.
     * If the conversion attempt for a given method failed, we may want to try
     * another so we will need to ditch the list of converted objects because
     * the next method could well have different parameter types.
     */
    public void clearConverted()
    {
        converted.clear();
    }

    /**
     * Add to the (temporary) list of converted objects
     * @param iv The converted object
     * @param type The type that we converted the object to
     * @param bean The converted version
     */
    public void addConverted(InboundVariable iv, Class type, Object bean)
    {
        Conversion conversion = new Conversion(iv, type);
        Object old = converted.put(conversion, bean);
        if (old != null)
        {
            log.warn("Duplicate variable conversion called: " + conversion);
        }
    }

    /**
     * Check to see if the conversion has already been done
     * @param iv The inbound data to check
     * @param type The type that we want the object converted to
     * @return The converted data or null if it has not been converted
     */
    public Object getConverted(InboundVariable iv, Class type)
    {
        Conversion conversion = new Conversion(iv, type);
        return converted.get(conversion);
    }

    /**
     * How many parameters are there?
     * @return The parameter count
     */
    public int getParameterCount()
    {
        return paramCount;
    }

    /**
     * Count the arguments (including method and script params) for a given call
     * number.
     * @param callNum The Call number to count the parameters of
     * @return The parameter count for a given Call
     */
    public int getParameterCount(int callNum)
    {
        int count = 0;
        String prefix = ProtocolConstants.INBOUND_CALLNUM_PREFIX + callNum + ProtocolConstants.INBOUND_CALLNUM_SUFFIX + ProtocolConstants.INBOUND_KEY_PARAM;
        for (String key : variables.keySet())
        {
            if (key.startsWith(prefix))
            {
                count++;
            }
        }
        return count;
    }

    /**
     * Get a parameter by index
     * @param callNum The call number to work on
     * @param index The parameter index
     * @return The found parameter
     */
    public InboundVariable getParameter(int callNum, int index)
    {
        String key = ProtocolConstants.INBOUND_CALLNUM_PREFIX + callNum +
                     ProtocolConstants.INBOUND_CALLNUM_SUFFIX +
                     ProtocolConstants.INBOUND_KEY_PARAM + index;

        InboundVariable found = variables.get(key);
        if (found != null)
        {
            return found;
        }

        return nullInboundVariable;
    }

    /**
     * This is very nasty - we need to create an array for varargs. The correct
     * solution is to have ArrayConverter (and MapConverter) part of dwrp and
     * not implementations of Converter. This would resolve a number of problems
     * however for now we will have to create a mock InboundVariable to make it
     * look to our ArrayConverter that the client passed in an array all along
     * @param callNum The call number to work on
     * @param destParamCount The number of parameters to the method that we are
     * calling, from which we can work out the size of the varargs array
     * @return ...
     */
    public InboundVariable createArrayWrapper(int callNum, int destParamCount)
    {
        int inputParamCount = getParameterCount(callNum);
        int varArgArraySize = 1 + inputParamCount - destParamCount;

        InboundVariable[] members = new InboundVariable[varArgArraySize];
        int varArgsParamStartIndex = destParamCount - 1; // The varargs param has to be last
        for (int i = 0; i < varArgArraySize; i++)
        {
            members[i] = getParameter(callNum, varArgsParamStartIndex);
            varArgsParamStartIndex++;
        }

        return new InboundVariable(this, members);
    }

    /**
     * A debug method so people can get a list of all the variable names
     * @return an iterator over the known variable names
     */
    public Iterator getInboundVariableNames()
    {
        return variables.keySet().iterator();
    }

    /**
     * A Class to use as a key in a map for conversion purposes.
     * A collection of an InboundVariable and a type
     */
    protected static class Conversion
    {
        /**
         * @param inboundVariable The new inboundVariable
         * @param type The new type
         */
        Conversion(InboundVariable inboundVariable, Class type)
        {
            if (inboundVariable == null)
            {
                throw new NullPointerException("InboundVariable");
            }

            if (type == null)
            {
                throw new NullPointerException("Class type");
            }

            this.inboundVariable = inboundVariable;
            this.type = type;
        }

        /* (non-Javadoc)
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object obj)
        {
            if (!(obj instanceof Conversion))
            {
                return false;
            }

            Conversion that = (Conversion) obj;

            return this.type.equals(that.type) && this.inboundVariable.equals(that.inboundVariable);
        }

        /* (non-Javadoc)
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode()
        {
            return inboundVariable.hashCode() + type.hashCode();
        }

        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString()
        {
            return "Conversion[" + inboundVariable + "," + type.getName() + "]";
        }

        private final InboundVariable inboundVariable;

        private final Class type;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
        StringBuffer buffer = new StringBuffer();
        buffer.append("InboundContext[");
        for (Map.Entry entry : variables.entrySet())
        {
            buffer.append(entry.getKey());
            buffer.append('=');
            buffer.append(entry.getValue());
            buffer.append(',');
        }
        buffer.append("]");
        return buffer.toString();
    }

    /**
     * A variable to use if we need to tell someone that we got nothing.
     */
    private final InboundVariable nullInboundVariable = new InboundVariable(this);

    /**
     * The stack of pushed conversion contexts.
     * i.e. What is the context of this type conversion.
     */
    private final LinkedList typeHintStack = new LinkedList();

    /**
     * How many params are there?.
     * To be more accurate, return one less than the highest numbered parameter
     * that we have come across.
     */
    private int paramCount = 0;

    /**
     * A map of all the inbound variables
     */
    private final Map variables = new HashMap();

    /**
     * A map of all the variables converted.
     */
    private final Map converted = new HashMap();

    /**
     * The log stream
     */
    private static final Log log = LogFactory.getLog(InboundContext.class);

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy