/** * Copyright 2005-2010 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.net.URLDecoder; 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.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.state.State; /** * <p> * It generates the states of each page by storing them in the user session. To be * able to associate the request state with the state stored in session, an extra * parameter is added to each request, containing the state identifier which makes * possible to get the state of the user session. * </p> * <p> * Non editable values are hidden to the client, guaranteeing <b>confidentiality</b> * </p> * * @see org.hdiv.dataComposer.AbstractDataComposer * @see org.hdiv.composer.IDataComposer * @author Roberto Velasco */ public class DataComposerMemory extends AbstractDataComposer implements IDataComposer { /** * Commons Logging instance. */ private static Log log = LogFactory.getLog(DataComposerMemory.class); /** * Dash character */ protected static final String DASH = "-"; /** * Page with the possible requests or states */ protected IPage page; /** * State that represents all the data of a request or a form existing in a page * <code>page</code> */ protected IState state; /** * Last parameter treated by the compose method */ protected IParameter lastParameter; /** * Represents the identifier of each possible state stored in the page * <code>page</code>. */ protected int requestCounter = 0; /** * States stack to store all states of the page <code>page</code> */ protected Stack statesStack; /** * HDIV configuration object. */ protected HDIVConfig hdivConfig; /** * Unique id generator */ protected UidGenerator uidGenerator; /** * DataComposerMemory initialization with HTTP session wrapper and new stack to * store all states of the page <code>page</code>. */ public void init() { this.page = new Page(); this.statesStack = new Stack(); } /** * 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 encoded 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 encoded 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, "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 encoded 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, "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 encoded 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, "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 encoded 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) { this.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 encoded 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, 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 encoded 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 charEncoding character encoding * @return Codified value to send to the client * @since HDIV 1.1 */ public String compose(String parameter, String value, boolean editable, String editableName, boolean isActionParam, String charEncoding) { this.composeParameter(parameter, value, editable, editableName, isActionParam, charEncoding); if (this.hdivConfig.isStartParameter(parameter)) { return value; } if (this.isUserDefinedNonValidationParameter(parameter)) { return value; } if (Boolean.FALSE.equals(this.hdivConfig.getConfidentiality())) { return value; } if (this.getAction() != null) { if (this.hdivConfig.isStartPage(this.getAction())) { return value; } } return (this.lastParameter.getCount() - 1) + ""; } /** * 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 */ private boolean isUserDefinedNonValidationParameter(String parameter) { String actionWithoutContextPath = this.getAction(); if (actionWithoutContextPath.startsWith("/")) { int secondSlash = actionWithoutContextPath.indexOf("/", 1); if (secondSlash > 0) { actionWithoutContextPath = actionWithoutContextPath.substring(secondSlash); } } if (this.hdivConfig.isParameterWithoutValidation(actionWithoutContextPath, 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 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 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 */ private void composeParameter(String parameter, String value, boolean editable, String editableName, boolean isActionParam, String charEncoding) { // we decoded value before store it in state. String decodedValue = this.getDecodedValue(value, charEncoding); if (this.state.existParameter(parameter)) { if ((this.lastParameter == null) || (!this.lastParameter.getName().equals(parameter))) { this.lastParameter = this.state.getParameter(parameter); } // add a new value to the parameter this.lastParameter.addValue(decodedValue); } else { // create a new parameter and add to the request this.lastParameter = new Parameter(); this.lastParameter.addValue(decodedValue); this.lastParameter.setName(parameter); this.lastParameter.setEditable(editable); this.lastParameter.setEditableDataType(editableName); this.lastParameter.setActionParam(isActionParam); this.state.addParameter(parameter, lastParameter); } } /** * 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) { IParameter storedParameter = this.state.getParameter(oldParameter); if (storedParameter.getValues().size() > 0) { this.composeParameter(newParameter, storedParameter.getValuePosition(0), false, "", false, "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); this.lastParameter.addValue(currentValue); } } } /** * Decoded <code>value</code> using input <code>charEncoding</code>. * * @param value value to decode * @param charEncoding character encoding * @return value decoded */ private String getDecodedValue(String value, String charEncoding) { String decodedValue = null; try { decodedValue = URLDecoder.decode(value, charEncoding); } catch (Exception e) { decodedValue = value; } return (decodedValue == null) ? "" : decodedValue; } /** * It is called by each request or form existing in the page returned by the * server. It creates a new state to store all the parameters and values of the * request or form. */ public void beginRequest() { this.state = new State(); this.state.setAction(this.getAction()); String currentRequestCounter = String.valueOf(requestCounter); this.state.setId(currentRequestCounter); this.statesStack.push(this.state); requestCounter++; this.lastParameter = null; } /** * It is called in the pre-processing stage of each request or form existing in * the page returned by the server, as long as the destiny of the request is an * action. It creates a new state to store all the parameters and values of the * request or form. * * @param action action name * @see org.hdiv.dataComposer.DataComposerMemory#beginRequest() */ public void beginRequest(String action) { this.setAction(action); this.beginRequest(); } /** * It is called in the pre-processing stage of each request or form existing in * the page returned by the server. It adds the state of the treated request or * form to the page <code>page</code> and returns and identifier compoded by * the page identifier and the state identifier. * * @return Identifier composed by the page identifier and the state identifier. */ public String endRequest() { this.state = (IState) this.statesStack.pop(); this.state.setPageId(this.getPage().getName()); this.page.addState(this.state); String id = this.getPage().getName() + DASH + this.state.getId() + DASH + this.getHdivStateSuffix(); this.updateComposerState(); return id; } /** * Obtains the suffix to add to the _HDIV_STATE_ parameter in the memory version. * * @return Returns suffix added to the _HDIV_STATE_ parameter in the memory * version. * @since HDIV 1.1 */ public String getHdivStateSuffix() { return this.page.getRandomToken(); } /** * Updates the state <code>state</code> of <code>this</code> with the state * in the first position of the state stack. If the state stack is empty it does * nothing. */ public void updateComposerState() { if (this.statesStack.size() > 0) { this.state = (IState) this.statesStack.lastElement(); } } /** * It is called in the pre-processing stage of each user request assigning a new * page identifier to the page. */ public void startPage() { if (this.page.getName() == null) { this.initPageId(); this.page.setName(this.getPageId()); this.page.setRandomToken(this.uidGenerator.generateUid().toString()); } } /** * This method is called in the pre-processing stage of each user request to add * an IPage object, which represents the page to show by the server, with all its * states to the user session. */ public void endPage() { if(this.page.getStates().size() > 0){ this.getSession().addPage(this.getPageId(), this.page); }else{ log.debug("The page ["+this.page.getName()+"] has no states, is not stored in session"); } } /** * @return IPage which represents the page in memory. */ public IPage getPage() { return this.page; } /** * @param page The page to set. */ public void setPage(IPage page) { this.page = page; } /** * @param hdivConfig The HDIV configuration object to set. */ public void setHdivConfig(HDIVConfig hdivConfig) { this.hdivConfig = hdivConfig; } /** * 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); } public void setUidGenerator(UidGenerator uidGenerator) { this.uidGenerator = uidGenerator; } }