org.hdiv.dataComposer.AbstractDataComposer.java Source code

Java tutorial

Introduction

Here is the source code for org.hdiv.dataComposer.AbstractDataComposer.java

Source

/**
 * Copyright 2005-2013 hdiv.org
 *
 * Licensed 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.hdiv.dataComposer;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hdiv.config.HDIVConfig;
import org.hdiv.idGenerator.UidGenerator;
import org.hdiv.session.ISession;
import org.hdiv.state.IPage;
import org.hdiv.state.IParameter;
import org.hdiv.state.IState;
import org.hdiv.state.Page;
import org.hdiv.state.Parameter;
import org.hdiv.util.Constants;
import org.springframework.web.util.HtmlUtils;

/**
 * <p>
 * It processes the data contributed by the HDIV custom tags. The aim of this class is to create an object of type
 * IState for each possible request (form or link) in every page processed by the HDIV custom tags. The IState object is
 * used to validate client's later requests.
 * </p>
 * <p>
 * The process of creating an IState object is as follows: Each time a link or a form processing begins, HDIV custom
 * tags set the request beginning by calling beginRequest method. Once the beginning is set, an IState object is created
 * and it is fill in with all the data of the request(parameter values, non editable values, parameter types) using the
 * compose method. After processing all the request data of the link or form, custom tags set the end of the processing
 * by calling endRequest method.
 * </p>
 * <p>
 * Depending on the strategy defined in HDIV configuration (memory, cipher or hash), the IState object is stored in the
 * user session or is sent to the client in the html code by adding an extra parameter called _HDIV_STATE_.
 * </p>
 * <p>
 * In the memory strategy IState objects are stored in the user session (HttpSession) while in the cipher and hash
 * strategies these objects are stored in the client.
 * </p>
 * 
 * @author Roberto Velasco
 */
public abstract class AbstractDataComposer implements IDataComposer {

    /**
     * Commons Logging instance.
     */
    private static Log log = LogFactory.getLog(AbstractDataComposer.class);

    /**
     * Dash character
     */
    protected static final String DASH = "-";

    /**
     * Http session wrapper
     */
    protected ISession session;

    /**
     * Unique id generator
     */
    protected UidGenerator uidGenerator;

    /**
     * Page with the possible requests or states
     */
    protected IPage page;

    /**
     * States stack to store all states of the page <code>page</code>
     */
    private Stack<IState> statesStack;

    /**
     * HDIV configuration object.
     */
    protected HDIVConfig hdivConfig;

    /**
     * DataComposer initialization with new stack to store all states of the page <code>page</code>.
     */
    public void init() {
        this.setPage(new Page());
        this.statesStack = new Stack<IState>();
    }

    /**
     * Obtains a new unique identifier for the page.
     */
    public void initPage() {
        this.page = new Page();
        int pageId = this.session.getPageId();
        this.page.setId(pageId);
    }

    /**
     * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed
     * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if
     * confidentiality indicator <code>confidentiality</code> is true.
     * 
     * @param parameter
     *            HTTP parameter name
     * @param value
     *            value generated by server
     * @param editable
     *            parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @return Codified value to send to the client
     */
    public String compose(String parameter, String value, boolean editable) {
        return this.compose(parameter, value, editable, false);
    }

    /**
     * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed
     * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if
     * confidentiality indicator <code>confidentiality</code> is true.
     * 
     * @param action
     *            target action
     * @param parameter
     *            HTTP parameter name
     * @param value
     *            value generated by server
     * @param editable
     *            parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @return Codified value to send to the client
     */
    public String compose(String action, String parameter, String value, boolean editable) {

        return this.compose(action, parameter, value, editable, false, Constants.ENCODING_UTF_8);
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the
     * server for the parameter <code>parameter</code> in the cipher and memory strategies.
     * 
     * @param parameter
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param isActionParam
     *            parameter added in action attribute
     * @return Codified value to send to the client
     */
    public String compose(String parameter, String value, boolean editable, boolean isActionParam) {

        return this.compose(parameter, value, editable, isActionParam, Constants.ENCODING_UTF_8);
    }

    /**
     * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed
     * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if
     * confidentiality indicator <code>confidentiality</code> is true.
     * 
     * @param parameter
     *            HTTP parameter name
     * @param value
     *            value generated by server
     * @param editable
     *            parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableName
     *            editable name (text or textarea)
     * @return Codified value to send to the client
     * @since HDIV 1.1
     */
    public String compose(String parameter, String value, boolean editable, String editableName) {

        return this.compose(parameter, value, editable, editableName, false, null, Constants.ENCODING_UTF_8);
    }

    /**
     * It generates a new encoded value for the parameter <code>parameter</code> and the value <code>value</code> passed
     * as parameters. The returned value guarantees the confidentiality in the cipher and memory strategies if
     * confidentiality indicator <code>confidentiality</code> is true.
     * 
     * @param action
     *            target action
     * @param parameter
     *            parameter name
     * @param value
     *            value generated by server
     * @param editable
     *            parameter type: editable(textbox, password,etc.) or non editable (hidden, select,...)
     * @param isActionParam
     *            parameter added in action attribute
     * @param charEncoding
     *            character encoding
     * @return Codified value to send to the client
     */
    public String compose(String action, String parameter, String value, boolean editable, boolean isActionParam,
            String charEncoding) {

        // Get current IState
        IState state = this.getStatesStack().peek();
        if (state.getAction() != null && state.getAction().trim().length() == 0) {
            state.setAction(action);
        }
        return this.compose(parameter, value, editable, isActionParam, charEncoding);
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the
     * server for the parameter <code>parameter</code> in the cipher and memory strategies.
     * 
     * @param parameter
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param isActionParam
     *            parameter added in action attribute
     * @param charEncoding
     *            character encoding
     * @return Codified value to send to the client
     */
    public String compose(String parameter, String value, boolean editable, boolean isActionParam,
            String charEncoding) {

        return this.compose(parameter, value, editable, null, isActionParam, null, charEncoding);
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the
     * server for the parameter <code>parameter</code> in the cipher and memory strategies.
     * 
     * @param parameter
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableName
     *            editable name (text or textarea)
     * @param isActionParam
     *            parameter added in action attribute
     * @param method
     *            http method, GET or POST
     * @return Codified value to send to the client
     * @since HDIV 2.1.5
     */
    public String compose(String parameter, String value, boolean editable, String editableName,
            boolean isActionParam, String method) {
        return this.compose(parameter, value, editable, editableName, isActionParam, method,
                Constants.ENCODING_UTF_8);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.hdiv.dataComposer.IDataComposer#composeParams(java.lang.String, java.lang.String, java.lang.String)
     */
    public String composeParams(String parameters, String method, String charEncoding) {

        if (parameters == null || parameters.length() == 0) {
            return null;
        }

        String decodedParams = this.getDecodedValue(parameters, charEncoding);

        // Get current IState
        IState state = this.getStatesStack().peek();
        state.setParams(decodedParams);

        if (this.hdivConfig.getConfidentiality()) {
            // replace real values with confidential ones
            parameters = this.applyConfidentialityToParams(parameters, method);
        }

        return parameters;
    }

    /**
     * Apply confidentiality to parameters String. Replaces real values with confidential ones.
     * 
     * @param parameters
     *            parameters in query format
     * @param method
     *            HTTP method
     * @return parameters in query format with confidential values
     */
    private String applyConfidentialityToParams(String parameters, String method) {

        Map<String, Integer> pCount = new HashMap<String, Integer>();

        parameters = parameters.replaceAll("&amp;", "&");
        String newParameters = parameters;

        // Init indexes
        int beginIndex = 0;
        int endIndex = parameters.indexOf("&") > 0 ? parameters.indexOf("&") : parameters.length();
        do {
            String param = parameters.substring(beginIndex, endIndex);
            int index = param.indexOf("=");
            index = index < 0 ? param.length() : index;
            String name = param.substring(0, index);

            if (this.isConfidentialParam(name, method)) {
                // Parameter is not a start parameter
                Integer count = pCount.get(name);
                int num = (count == null) ? 0 : count + 1;
                pCount.put(name, num);

                // Replace parameter with confidential values
                newParameters = newParameters.replace(param, name + "=" + num);
            }

            // Update indexes
            beginIndex = endIndex + 1;
            endIndex = parameters.indexOf("&", endIndex + 1);
            if (endIndex < 0) {
                endIndex = parameters.length();
            }

        } while (endIndex > beginIndex);

        return newParameters;
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the
     * server for the parameter <code>parameter</code> in the cipher and memory strategies.
     * <p>
     * Custom method for form field.
     * 
     * @param parameter
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableName
     *            editable name (text or textarea)
     * @return Codified value to send to the client
     * @since HDIV 2.1.5
     */
    public String composeFormField(String parameter, String value, boolean editable, String editableName) {

        return this.compose(parameter, value, editable, editableName, false, "POST", Constants.ENCODING_UTF_8);
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>. If confidentiality is activated it generates a new encoded value that will be returned by the
     * server for the parameter <code>parameter</code> in the cipher and memory strategies.
     * 
     * @param parameterName
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableName
     *            editable name (text or textarea)
     * @param isActionParam
     *            parameter added in action attribute
     * @param method
     *            http method, GET or POST
     * @param charEncoding
     *            character encoding
     * @return Codified value to send to the client
     * @since HDIV 2.1.5
     */
    public String compose(String parameterName, String value, boolean editable, String editableName,
            boolean isActionParam, String method, String charEncoding) {

        if (!this.isRequestStarted()) {
            // If request not started, do nothing
            return value;
        }

        if (method == null || method.length() == 0) {
            // Default method is GET
            method = "GET";
        }

        IParameter parameter = this.composeParameter(parameterName, value, editable, editableName, isActionParam,
                charEncoding);

        if (this.isConfidentialParam(parameterName, method)) {
            return parameter.getConfidentialValue();
        } else {
            return value;
        }

    }

    /**
     * Returns true if the parameter requires confidentiality. False otherwise.
     * 
     * @param parameterName
     *            the name of the parameter
     * @param method
     *            request HTTP method
     * @return boolean result
     * @since HDIV 2.1.6
     */
    protected boolean isConfidentialParam(String parameterName, String method) {

        if (!this.hdivConfig.getConfidentiality()) {
            return false;
        }

        if (this.hdivConfig.isStartParameter(parameterName)) {
            return false;
        }

        if (this.isUserDefinedNonValidationParameter(parameterName)) {
            return false;
        }

        if (this.hdivConfig.isParameterWithoutConfidentiality(parameterName)) {
            return false;
        }

        return true;
    }

    /**
     * Checks if the parameter <code>parameter</code> is defined by the user as a no required validation parameter for
     * the action <code>this.target</code>.
     * 
     * @param parameter
     *            parameter name
     * @return True If it is parameter that needs no validation. False otherwise.
     * @since HDIV 2.0.6
     */
    protected boolean isUserDefinedNonValidationParameter(String parameter) {

        // Get current IState
        IState state = this.getStatesStack().peek();
        String action = state.getAction();

        if (this.hdivConfig.isParameterWithoutValidation(action, parameter)) {

            if (log.isDebugEnabled()) {
                log.debug("parameter " + parameter + " doesn't need validation. It is user defined parameter.");
            }
            return true;
        }
        return false;
    }

    /**
     * Adds a new IParameter object, generated from the values passed as parameters, to the current state
     * <code>state</code>.
     * 
     * @param parameterName
     *            HTTP parameter
     * @param value
     *            value generated by server
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableDataType
     *            editable parameter name (text or textarea)
     * @param isActionParam
     *            parameter added in action attribute
     * @param charEncoding
     *            character encoding
     * @return Codified value to send to the client
     * @since HDIV 1.1
     */
    protected IParameter composeParameter(String parameterName, String value, boolean editable,
            String editableDataType, boolean isActionParam, String charEncoding) {

        // we decoded value before store it in state.
        String decodedValue = null;
        if (!editable) {
            decodedValue = this.getDecodedValue(value, charEncoding);
        }

        // Get current IState
        IState state = this.getStatesStack().peek();

        IParameter parameter = state.getParameter(parameterName);
        if (parameter != null) {
            if (parameter.isEditable() != editable) {
                // A parameter can be created as editable but if a new non editable value is added, the parameter is
                // changed to non editable. This is required in some frameworks like Struts 2.
                parameter.setEditable(editable);
            }
            parameter.addValue(decodedValue);
        } else {
            // create a new parameter and add to the request
            parameter = createParameter(parameterName, decodedValue, editable, editableDataType, isActionParam,
                    charEncoding);
            state.addParameter(parameter);
        }

        return parameter;
    }

    /**
     * Instantiates the parameter
     * 
     * @param parameterName
     *            name of the parameter
     * @param decodedValue
     *            the decoded value of the parameter
     * @param editable
     *            Parameter type: editable(textbox, password,etc.) or non editable (hidden, select, radio, ...)
     * @param editableDataType
     *            editable parameter name (text or textarea)
     * @param isActionParam
     *            parameter added in action attribute
     * @param charEncoding
     *            character encoding
     * @return New IParameter object
     */
    protected IParameter createParameter(String parameterName, String decodedValue, boolean editable,
            String editableDataType, boolean isActionParam, String charEncoding) {
        return new Parameter(parameterName, decodedValue, editable, editableDataType, isActionParam);
    }

    /**
     * Creates a new parameter called <code>newParameter</code> and adds all the values of <code>oldParameter</code>
     * stored in the state to it.
     * 
     * @param oldParameter
     *            name of the parameter stored in the state
     * @param newParameter
     *            name of the new parameter
     */
    public void mergeParameters(String oldParameter, String newParameter) {

        // Get current IState
        IState state = this.getStatesStack().peek();
        IParameter storedParameter = state.getParameter(oldParameter);

        if (storedParameter.getValues().size() > 0) {

            IParameter parameter = this.composeParameter(newParameter, storedParameter.getValuePosition(0), false,
                    "", false, Constants.ENCODING_UTF_8);

            String currentValue = null;
            // We check the parameters since the second position because the first
            // value has been used to create the parameter
            for (int i = 1; i < storedParameter.getValues().size(); i++) {

                currentValue = storedParameter.getValuePosition(i);
                parameter.addValue(currentValue);
            }
        }
    }

    /**
     * <p>
     * Decoded <code>value</code> using input <code>charEncoding</code>.
     * </p>
     * <p>
     * Removes Html Entity elements too. Like that:
     * </p>
     * <blockquote> &amp;#<i>Entity</i>; - <i>(Example: &amp;amp;) case sensitive</i> &amp;#<i>Decimal</i>; -
     * <i>(Example: &amp;#68;)</i><br>
     * &amp;#x<i>Hex</i>; - <i>(Example: &amp;#xE5;) case insensitive</i><br>
     * </blockquote>
     * <p>
     * Based on {@link HtmlUtils.htmlUnescape}.
     * </p>
     * 
     * @param value
     *            value to decode
     * @param charEncoding
     *            character encoding
     * @return value decoded
     */
    private String getDecodedValue(String value, String charEncoding) {

        if (value == null || value.length() == 0) {
            return "";
        }

        String decodedValue = null;
        try {
            decodedValue = URLDecoder.decode(value, charEncoding);
        } catch (UnsupportedEncodingException e) {
            decodedValue = value;
        } catch (IllegalArgumentException e) {
            decodedValue = value;
        }

        // Remove escaped Html elements
        if (decodedValue.contains("&")) {
            // Can contain escaped characters
            decodedValue = HtmlUtils.htmlUnescape(decodedValue);
        }

        return (decodedValue == null) ? "" : decodedValue;
    }

    /**
     * True if beginRequest has been executed and endRequest not.
     * 
     * @return boolean
     */
    public boolean isRequestStarted() {
        return this.statesStack.size() > 0;
    }

    /**
     * Adds the flow identifier to the page of type <code>IPage</code>.
     * 
     * @since HDIV 2.0.3
     */
    public void addFlowId(String id) {
        this.page.setFlowId(id);
    }

    /**
     * Obtains the suffix to add to the _HDIV_STATE_ parameter in the memory and hash strategy.
     * 
     * @param method
     *            HTTP method
     * 
     * @return Returns suffix added to the _HDIV_STATE_ parameter.
     * @since 2.1.7
     */
    protected String getStateSuffix(String method) {

        String randomToken = this.page.getRandomToken(method);
        if (randomToken == null) {
            randomToken = this.uidGenerator.generateUid().toString();
            this.page.setRandomToken(randomToken, method);
        }
        return randomToken;
    }

    /**
     * @param session
     *            the session to set
     */
    public void setSession(ISession session) {
        this.session = session;
    }

    /**
     * @return the page
     */
    public IPage getPage() {
        return page;
    }

    /**
     * @param page
     *            the page to set
     */
    public void setPage(IPage page) {
        this.page = page;
    }

    /**
     * @param uidGenerator
     *            the uidGenerator to set
     */
    public void setUidGenerator(UidGenerator uidGenerator) {
        this.uidGenerator = uidGenerator;
    }

    /**
     * @return the statesStack
     */
    public Stack<IState> getStatesStack() {
        return statesStack;
    }

    /**
     * @param hdivConfig
     *            The HDIV configuration object to set.
     */
    public void setHdivConfig(HDIVConfig hdivConfig) {
        this.hdivConfig = hdivConfig;
    }

}