ElementContext.java :  » Web-Framework » rife-1.6.1 » com » uwyn » rife » engine » Java Open Source

Java Open Source » Web Framework » rife 1.6.1 
rife 1.6.1 » com » uwyn » rife » engine » ElementContext.java
/*
 * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
 * Distributed under the terms of either:
 * - the common development and distribution license (CDDL), v1.0; or
 * - the GNU Lesser General Public License, v2.1 or later
 * $Id: ElementContext.java 3813 2007-06-25 18:22:03Z gbevin $
 */
package com.uwyn.rife.engine;

import com.uwyn.rife.engine.exceptions.*;
import com.uwyn.rife.template.*;
import com.uwyn.rife.tools.*;
import java.util.*;

import com.uwyn.rife.authentication.credentialsmanagers.RoleUserAttributes;
import com.uwyn.rife.authentication.credentialsmanagers.RoleUserIdentity;
import com.uwyn.rife.authentication.elements.Identified;
import com.uwyn.rife.config.RifeConfig;
import com.uwyn.rife.continuations.CallState;
import com.uwyn.rife.continuations.ContinuationConfigRuntime;
import com.uwyn.rife.continuations.ContinuationContext;
import com.uwyn.rife.continuations.ContinuationManager;
import com.uwyn.rife.continuations.exceptions.AnswerException;
import com.uwyn.rife.continuations.exceptions.CallException;
import com.uwyn.rife.continuations.exceptions.PauseException;
import com.uwyn.rife.continuations.exceptions.StepBackException;
import com.uwyn.rife.engine.CharSequenceDeferred;
import com.uwyn.rife.site.Constrained;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.ConstrainedUtils;
import com.uwyn.rife.site.FormBuilder;
import com.uwyn.rife.template.exceptions.TemplateException;
import com.uwyn.rife.tools.exceptions.BeanUtilsException;
import com.uwyn.rife.tools.exceptions.LightweightError;
import com.uwyn.rife.tools.exceptions.SerializationUtilsErrorException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

public class ElementContext
{
  public static final String  PREFIX_EXIT_QUERY = "EXIT:QUERY:";
  public static final String  PREFIX_EXIT_FORM = "EXIT:FORM:";
  public static final String  PREFIX_EXIT_PARAMS = "EXIT:PARAMS:";
  public static final String  PREFIX_EXIT_PARAMSJS = "EXIT:PARAMSJS:";
  public static final String  PREFIX_SUBMISSION_QUERY = "SUBMISSION:QUERY:";
  public static final String  PREFIX_SUBMISSION_FORM = "SUBMISSION:FORM:";
  public static final String  PREFIX_SUBMISSION_PARAMS = "SUBMISSION:PARAMS:";
  public static final String  PREFIX_SUBMISSION_PARAMSJS = "SUBMISSION:PARAMSJS:";

  public static final String  PREFIX_PARAM = "PARAM:";
  public static final String  PREFIX_INPUT = "INPUT:";
  public static final String  PREFIX_OUTPUT = "OUTPUT:";
  public static final String  PREFIX_INCOOKIE = "INCOOKIE:";
  public static final String  PREFIX_OUTCOOKIE = "OUTCOOKIE:";
  public static final String  PREFIX_ELEMENT = "ELEMENT:";
  public static final String  PREFIX_PROPERTY = "PROPERTY:";
  public static final String  PREFIX_OGNL_ROLEUSER = "OGNL:ROLEUSER:";
  public static final String  PREFIX_MVEL_ROLEUSER = "MVEL:ROLEUSER:";
  public static final String  PREFIX_GROOVY_ROLEUSER = "GROOVY:ROLEUSER:";
  public static final String  PREFIX_JANINO_ROLEUSER = "JANINO:ROLEUSER:";

  public static final String  ID_WEBAPP_ROOTURL = "WEBAPP:ROOTURL";
  public static final String  ID_SERVER_ROOTURL = "SERVER:ROOTURL";
  
  public static final String  TAG_PROPERTY = "^"+PREFIX_PROPERTY+"\\s*(.*?)\\s*$";
  public static final String  TAG_ELEMENT = "^("+PREFIX_ELEMENT+"\\s*([\\-\\+]?)\\s*(.*?)\\s*)(?::([^:]*))?$";
  public static final String  TAG_OGNL_ROLEUSER = "(?s)^("+PREFIX_OGNL_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
  public static final String  TAG_MVEL_ROLEUSER = "(?s)^("+PREFIX_MVEL_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
  public static final String  TAG_JANINO_ROLEUSER = "(?s)^("+PREFIX_JANINO_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
  public static final String  TAG_GROOVY_ROLEUSER = "(?s)^("+PREFIX_GROOVY_ROLEUSER+".*):\\s*\\[\\[\\s*+(.*?)\\s*+\\]\\]\\s*$";
  
  private static ThreadLocal<ElementSupport>  sActiveElementSupport = new ThreadLocal<ElementSupport>();
  
  private ElementSupport            mElement = null;
  private ElementInfo              mElementInfo = null;
  private RequestState            mRequestState = null;
  private ElementExecutionState        mElementState = null;
  private Response              mResponse = null;
  private OutputValues            mOutputs = null;
  private OutcookieValues            mOutcookies = null;
  private ArrayList<OutputListener>      mOutputListeners = null;
  private ArrayList<OutcookieListener>    mOutcookieListeners = null;
  private String                mContextId = null;
  private String                mSubmission = null;
  private boolean                mSteppedBack = false;
  
  public static ElementSupport getActiveElementSupport()
  {
    return sActiveElementSupport.get();
  }
  
  ElementContext(ElementSupport element, RequestState state, Response response)
  throws EngineException
  {
    assert element != null;
    assert state != null;
    assert response != null;

    mElement = element;
    synchronized (mElement)
    {
      mElement.setElementContext(this);
      
      mElementInfo = mElement.getElementInfo();
      
      mRequestState = state;
      mElementState = mRequestState.getElementState();
      mResponse = response;
      mOutputs = new OutputValues(this);
      mOutcookies = new OutcookieValues(this);
      
      // register the EmbeddingListener of the current state so that the
      // output values are kept in sync with global vars and the outcookies
      // are kept in sync with the global cookies
      if (state.isEmbedded())
      {
        RequestState.EmbeddingListener listener = state.getEmbeddingListener();
        addOutputListener(listener);
        addOutcookieListener(listener);
      }
      
      // register the PrecedenceListener of the current state so that the
      // output values are kept in sync with global vars and the outcookies
      // are kept in sync with the global cookies
      if (state.isPreceeding())
      {
        RequestState.PrecedenceListener listener = state.getPrecedenceListener();
        addOutputListener(listener);
        addOutcookieListener(listener);
      }
  
      // if the child element has been reached, replace the request parameters with the
      // original child request parameters
      if (mElementState.isInheritanceTarget() &&
        mElementState.hasRequestParameterValue(ReservedParameters.CHILDREQUEST))
      {
        ChildRequestEncoder.decode(element.getElementInfo(), mRequestState);
      }
      
      // automatically set an output value if the input value has been provided
      // and it's a global variable
      String[]  output_values = null;
      for (String globalvar_name : mElementInfo.getGlobalVarNames())
      {
        if (mElementState.hasInputValue(globalvar_name))
        {
          output_values = mElementState.getInputValues(globalvar_name);
          setAutomatedOutputValues(globalvar_name, output_values);
        }
      }
    }
  }
  
  private void handleChildTriggerVariablesPre()
  throws EngineException
  {
    // verify if no value are present that will automatically launch a child trigger
    if (mElementInfo.getChildTriggerNames().size() > 0)
    {
      // check each child trigger for a corresponding value
      // if it was found, launch the trigger
      for (String child_trigger_name : mElementInfo.getChildTriggerNames())
      {
        // check if a provided input value is acceptable for this
        // element as input or global variable
        if (mElementState.hasInputValue(child_trigger_name))
        {
          if (mElementInfo.containsGlobalVar(child_trigger_name) ||
            mElementInfo.containsInput(child_trigger_name))
          {
            triggerChild(child_trigger_name, getInputValues(child_trigger_name));
            continue;
          }
        }
        // check if a provided cookie is acceptable for this
        // element as incookie
        else if (mRequestState.getCookie(child_trigger_name) != null)
        {
          if (mElementInfo.containsGlobalCookie(child_trigger_name) ||
            mElementInfo.containsIncookie(child_trigger_name))
          {
            triggerChild(child_trigger_name, new String[] {getCookie(child_trigger_name).getValue()});
            continue;
          }
        }
        // check if a default input value is present that can trigger the child
        else if (mElementInfo.hasInputDefaultValues(child_trigger_name))
        {
          triggerChild(child_trigger_name, mElementInfo.getInputDefaultValues(child_trigger_name));
          continue;
        }
        // check if a default cookie value is present that can trigger the child
        else if (mElementInfo.hasIncookieDefaultValue(child_trigger_name))
        {
          triggerChild(child_trigger_name, new String[] {mElementInfo.getIncookieDefaultValue(child_trigger_name)});
          continue;
        }
        // check if a global var default is present for the trigger name
        else if (mElementInfo.containsGlobalVar(child_trigger_name) &&
             mElementInfo.hasGlobalVarDefaultValues(child_trigger_name))
        {
          triggerChild(child_trigger_name, mElementInfo.getGlobalVarDefaultValues(child_trigger_name));
          continue;
        }
        // check if a global cookie default is present for the trigger name
        else if (mElementInfo.containsGlobalCookie(child_trigger_name) &&
             mElementInfo.hasGlobalCookieDefaultValue(child_trigger_name))
        {
          triggerChild(child_trigger_name, new String[] {mElementInfo.getGlobalCookieDefaultValue(child_trigger_name)});
          continue;
        }
      }
    }
  }
  
  private void handleChildTriggerVariablesPost()
  throws EngineException
  {
    // verify if no value are present that will automatically launch a child trigger
    if (mElementInfo.getChildTriggerNames().size() > 0)
    {
      // check each child trigger for a corresponding value
      // if it was found, launch the trigger
      for (String child_trigger_name : mElementInfo.getChildTriggerNames())
      {
        // check if an output default value is present
        if (mElementInfo.hasOutputDefaultValues(child_trigger_name))
        {
          String[]  output_defaultvalues = mElementInfo.getOutputDefaultValues(child_trigger_name);
          triggerChild(child_trigger_name, output_defaultvalues);
          continue;
        }
      }
    }
  }
  
  ElementContext processContext()
  throws EngineException
  {
    long start = mElementInfo.startTrace();
    boolean clear_request = false;
    
    ElementContext result = null;
    
    try
    {
      ElementSupport previous_active_element;
      
      synchronized (mElement)
      {
        // handle the inheritance structure
        if (mElementState.inInheritanceStructure())
        {
          // try to see if the top of the trigger list matches the current element
          if (mElementState.isNextTrigger(mElementInfo))
          {
            // if it does, and it's a child trigger, use the values to launch a child trigger
            if (TriggerContext.TRIGGER_CHILD == mElementState.getNextTriggerType() &&
              mElementInfo.containsChildTrigger(mElementState.getNextTriggerName()))
            {
              triggerChild(mElementState.getNextTriggerName(), mElementState.getNextTriggerValues());
            }
            else if (TriggerContext.TRIGGER_EXIT == mElementState.getNextTriggerType() &&
                 mElementInfo.containsExit(mElementState.getNextTriggerName()))
            {
              exit(mElementState.getNextTriggerName());
            }
          }
          
          handleChildTriggerVariablesPre();
        }
        
        // handle precedence
        mRequestState.handlePrecedence(mResponse, getElementInfo());
        
        // set the response content type if this has been specified by the element
        if (mElementInfo.getContentType() != null &&
          mElementInfo.getContentType().length() > 0)
        {
          mResponse.setContentType(mElementInfo.getContentType());
        }

        // obtain the continuation context
        ContinuationContext continuation_context = mRequestState.getContinuationContext(mElementInfo);
        ContinuationContext.setActiveContext(continuation_context);
        ContinuationConfigRuntime.setActiveConfigRuntime(EngineContinuationConfigRuntimeSingleton.INSTANCE);
        
        // register the element as the active one in this thread and remember
        // which one that was previously active
        previous_active_element = sActiveElementSupport.get();

        sActiveElementSupport.set(mElement);
      }
      
      try
      {
        final ElementAware element_aware;
        Method submission_handler = null;
        
        synchronized (mElement)
        {
          // get the element aware interface
          element_aware = mElement.getElementAware();
          
          // check if there's are submissions
          if (mElementState.hasRequestParameterValue(ReservedParameters.SUBMISSION))
          {
            String[]    submission_names = mElementState.getRequestParameterValues(ReservedParameters.SUBMISSION);
            String[]    submission_contexts = mElementState.getRequestParameterValues(ReservedParameters.SUBMISSIONCONTEXT);
            String      submission_context = null;
            String      submission_context_id = null;
            String      submission_target = null;
            HashSet<String>  non_requestparameter_inputs = null;
            int        counter = 0;
            for (String submission_name : submission_names)
            {
              // get the submission context
              if (null == submission_contexts ||
                counter >= submission_contexts.length)
              {
                submission_context = getContextId();
              }
              else
              {
                try
                {
                  byte[] decoded = Base64.decode(submission_contexts[counter].getBytes("UTF-8"));
                  if (decoded != null &&
                    decoded.length > 0)
                  {
                    submission_context = new String(decoded);
                  }
                  else
                  {
                    submission_context = getContextId();
                  }
                }
                catch (UnsupportedEncodingException e)
                {
                  // should never happen
                }
              }
              
              // split up in the submission context id and the submission target
              int seperator_index = submission_context.indexOf("^");
              if (-1 == seperator_index)
              {
                submission_context_id = submission_context;
                submission_target = submission_context;
              }
              else
              {
                submission_context_id = submission_context.substring(0, seperator_index);
                submission_target = submission_context.substring(seperator_index+1);
              }
              
              // check if the submission target corresponds to the context id
              if (null == mSubmission &&
                getContextId().equals(submission_context_id) &&
                mElementInfo.hasSubmission(submission_name))
              {
                mSubmission = submission_name;
              }
              // if it doesn't, use the submission target element id to
              // retrieve the submission declaration, and the parameters
              // that have to be disabled as inputs for this element
              else
              {
                // get the submission target
                if (0 == submission_target.length())
                {
                  submission_target = getElementInfo().getId();
                }
  
                // obtain the element info
                ElementInfo submission_target_element = mElementInfo.getSite().resolveId(submission_target);
                if (submission_target_element != null)
                {
                  // get the submission declaration
                  Submission submission = submission_target_element.getSubmission(submission_name);
                  if (submission != null)
                  {
                    // get the submission parameter names
                    Collection<String> names = submission.getParameterNames();
                    if (names != null &&
                      names.size() > 0)
                    {
                      if (null == non_requestparameter_inputs)
                      {
                        non_requestparameter_inputs = new HashSet<String>();
                      }
                      
                      // add the submission parameters to the collection
                      // of inputs that shouldn't retrieve their values
                      // from the request parameters
                      non_requestparameter_inputs.addAll(names);
                    }
                  }
                }
              }
    
              counter++;
            }
            
            // register the non parameter inputs
            mElementState.setNonRequestParameterInputs(non_requestparameter_inputs);
          }
          
          // check if there's a submission handler
          
          // check if a submission is present and a corresponding do*() method is
          // available and retrieve the method
          final String submission_name = getSubmission();
          if (submission_name != null)
          {
            String submission_handler_name = "do"+StringUtils.capitalize(submission_name);
            try
            {
              submission_handler = element_aware.getClass().getMethod(submission_handler_name, (Class[])null);
                          submission_handler.setAccessible(true);
                      }
            catch (NoSuchMethodException e)
            {
              submission_handler = null;
            }
            catch (SecurityException e)
            {
              throw new EngineException(e);
            }
          }
          
          // inject the properties request values into the element instance
          new ElementInjector(this).performInjection(submission_name);
  
          // update the target element in the response
          mResponse.setLastElement(mElement);
          
          // initialize the element in a fully setup context
          mElement.initialize();
        }
        
        try
        {
          try
          {
            synchronized (mElement)
            {
              // call the processElement() method of the element itself if no handler could be found
              if (null == submission_handler)
              {
                element_aware.processElement();
              }
              // call the submission handler if it's available
              else
              {
                try
                {
                  submission_handler.invoke(element_aware, (Object[])null);
                }
                catch (InvocationTargetException e)
                {
                  if (e.getCause() instanceof LightweightError)
                  {
                    throw (LightweightError)e.getCause();
                  }
                  else if (e.getCause() instanceof EngineException)
                  {
                    throw (EngineException)e.getCause();
                  }
                  else
                  {
                    throw new EngineException(e.getCause());
                  }
                }
                catch (IllegalArgumentException e)
                {
                  throw new EngineException(e);
                }
                catch (IllegalAccessException e)
                {
                  throw new EngineException(e);
                }
              }
            }
          }
          catch (CallException e)
          {
            ContinuationContext context = e.getContext();
            
            // register context
            ContinuationManager  manager = mElementInfo.getSite().getContinuationManager();
            manager.addContext(context);
            
            synchronized (mElement)
            {
              String exit = (String)e.getTarget();
              mElementInfo.validateExitName(exit);
              if (null == mElementInfo.getFlowLink(exit))
              {
                throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
              }
            
              // create a new call state
              CallState call_state = new CallState(context.getId(), mElementState.clone());
              context.setCreatedCallState(call_state);
              
              // setup the exit
              result = setupExitContext(exit);
            }
          }
        }
        finally
        {
          synchronized (mElement)
          {
            String context_id = getContextId();
            
            ElementResultState result_state = null;
  
            // extract the preserved inputs of this element
            Set<Map.Entry<String, String[]>>  output_value_entries = mOutputs.aggregateValues().entrySet();
            Map<String, String[]>        preserved_inputs = collectPreservedInputs(output_value_entries);
            
            // if a call continuation is active, also preserve the previous preserved inputs without overriding the new ones
            ContinuationContext active_context = ContinuationContext.getActiveContext();
            if (active_context != null &&
              active_context.getActiveCallState() != null)
            {
              ElementResultState previous_result_state = mRequestState.getElementResultStatesRestored().get(context_id);
              if (previous_result_state != null)
              {
                Map<String, String[]> previous_preserved_inputs = previous_result_state.getPreservedInputs();
                if (previous_preserved_inputs != null)
                {
                  if (null == preserved_inputs)
                  {
                    preserved_inputs = previous_preserved_inputs;
                  }
                  else
                  {
                    for (Map.Entry<String, String[]> entry : previous_preserved_inputs.entrySet())
                    {
                      if (!preserved_inputs.containsKey(entry.getKey()))
                      {
                        preserved_inputs.put(entry.getKey(), entry.getValue());
                      }
                    }
                  }
                }
              }
              
              // preserve the continuation ID of this element
              if (null == result_state)
              {
                result_state = mElementInfo.getStateStore().createNewResultState(context_id);
              }
              result_state.setContinuationId(active_context.getActiveCallState().getContinuationId());
            }
            
            // add the preserved inputs to the result state
            if (preserved_inputs != null &&
              preserved_inputs.size() > 0)
            {
              if (null == result_state)
              {
                result_state = mElementInfo.getStateStore().createNewResultState(context_id);
              }
              
              result_state.setPreservedInputs(preserved_inputs);
            }
            
            // state the resulting element state
            if (result_state != null)
            {
              mRequestState.getElementResultStatesObtained().put(result_state);
            }
            
            // handle the setting of the getter outcookies
            mOutcookies.processGetters();
  
            // handle the firing of listener methods for getters outjection
            mOutputs.processGetters();
          }
          
          // handle the outcookie and output childtriggers from the getters outjection
          mOutcookies.processGetterChildTriggers();
          mOutputs.processGetterChildTriggers();
        }
      }
      catch (AnswerException e)
      {
        synchronized (mElement)
        {
          // only answer a call if a call state is available
          // otherwise the answer will function as a regular return
          if (e.getContext().getActiveCallState() != null)
          {          
            throw e;
          }
        }
      }
      finally
      {
        synchronized (mElement)
        {
          // restore the previously active element
          sActiveElementSupport.set(previous_active_element);
        }
      }
      
      synchronized (mElement)
      {
        // handle the child trigger values that could have an effect after
        // the actual element logic
        handleChildTriggerVariablesPost();
        
        // handle the default outcookies
        if (mElementInfo.hasOutcookieDefaults())
        {
          Cookie  default_cookie = null;
          
          for (Map.Entry<String, String> default_outcookie_entry : mElementInfo.getDefaultOutcookies().entrySet())
          {
            if (!mOutcookies.contains(default_outcookie_entry.getKey()))
            {
              default_cookie = new Cookie(default_outcookie_entry.getKey(), default_outcookie_entry.getValue());
              default_cookie.setPath("");
              setCookie(default_cookie);
            }
          }
        }
        
        // handle the default global cookies
        if (mElementInfo.hasGlobalCookieDefaults())
        {
          Cookie  default_cookie = null;
          
          for (Map.Entry<String, String> default_globalcookie_entry : mElementInfo.getDefaultGlobalCookies().entrySet())
          {
            if (!mOutcookies.contains(default_globalcookie_entry.getKey()))
            {
              default_cookie = new Cookie(default_globalcookie_entry.getKey(), default_globalcookie_entry.getValue());
              default_cookie.setPath("");
              setCookie(default_cookie);
            }
          }
        }
      }
    }
    catch (PauseException e)
    {
      ContinuationContext context = e.getContext();
      
      // register context
      ContinuationManager  manager = mElementInfo.getSite().getContinuationManager();
      manager.addContext(context);

      synchronized (mElement)
      {
        // preserve the continuation ID of this element
        String        context_id = getContextId();
        ElementResultState  result_state = mRequestState.getElementResultStatesObtained().get(context_id);
        
        if (null == result_state)
        {
          result_state = mElementInfo.getStateStore().createNewResultState(context_id);
          mRequestState.getElementResultStatesObtained().put(result_state);
        }
        result_state.setContinuationId(context.getId());
        
        clear_request = true;
        try
        {
          mResponse.flush();
        }
        catch (EngineException e2)
        {
          // if errors occurred during flushing it means that the client has
          // disconnected, disregard them
        }
        mResponse = null;
      }
    }
    catch (ChildTriggeredException e)
    {
      synchronized (mElement)
      {
        result = setupChildtriggerContext(e.getChildTriggerName(), e.getChildTriggerValues());
      }
    }
    catch (ExitTriggeredException e)
    {
      synchronized (mElement)
      {
        result = handleExitTrigger(e.getExitName());
      }
    }
    catch (StepBackException e)
    {
      ContinuationContext  context = e.getContext();
      
      // register context
      ContinuationManager manager = mElementInfo.getSite().getContinuationManager();
      manager.addContext(context);
      
      synchronized (mElement)
      {
        // preserve the continuation ID of this element
        String        context_id = getContextId();
        ElementResultState  result_state = mRequestState.getElementResultStatesObtained().get(context_id);
        if (null == result_state)
        {
          result_state = mElementInfo.getStateStore().createNewResultState(context_id);
          mRequestState.getElementResultStatesObtained().put(result_state);
        }
        result_state.setContinuationId(context.getId());
        
        // try to obtain the id of the previous continuation
        String stepbackid = e.lookupStepBackId();
  
        // there is no previous continuation, so start from the beginning again
        if (null == stepbackid)
        {
          ContinuationContext.clearActiveContext();
          mRequestState.setContinuationId(null);
        }
        else
        {
          mRequestState.setContinuationId(stepbackid);
        }
        
        result = new ElementContext(mElement, mRequestState, mResponse);
        result.mSteppedBack = true;
      }
    }
    catch (ForwardException e)
    {
      synchronized (mElement)
      {
        handleForward(e.getUrl());
      }
    }
    catch (RedirectException e)
    {
      synchronized (mElement)
      {
        getResponse().sendRedirect(e.getUrl());
      }
    }
    finally
    {
      synchronized (mElement)
      {
        try
        {
          // flush the output buffer
          if (mResponse != null)
          {
            mResponse.flush();
          }
          
          // trace element activity
          mElementInfo.outputTrace(start, mRequestState);
        }
        catch (EngineException e)
        {
          // if errors occurred during flushing it means that the client has
          // disconnected, disregard them
        }
        finally
        {
          // clear the request if this is needed, this typically happens
          // for continuations, to prevent the request to be cloned when
          // element instances are cloned
          if (clear_request)
          {
            mRequestState.clearRequest();
          }
        }
      }
    }

    return result;
  }

  private ElementContext setupChildtriggerContext(String childtrigger, String[] values)
  throws EngineException
  {
    ElementContext      element_context = null;
    Map<String, String[]>  child_inputs = null;
    
    if (mElementState.inInheritanceStructure())
    {
      // construct the child element
      ElementInfo child_element_info = mElementState.getInheritanceStack().pop();
      
      // verify if the trigger wasn't raised automatically, if this is the
      // case, the first trigger context has to be removed from the trigger
      // list since it has been used by this element
      if (mElementState.isNextChildTrigger(mElementInfo, childtrigger))
      {
        // obtain the stored exit inputs
        child_inputs = mElementState.nextTrigger().getParameters();
      }
      // since the current element deferred the logical flow to its child,
      // record the child trigger that caused this
      else
      {
        child_inputs = getChildInputValues(child_element_info);
        
        // don't store an child trigger which isn't dependent on watched values in the
        // trigger list, the whole element will be executed each time for those triggers
        if (childtrigger != null)
        {
          mElementState.addTrigger(TriggerContext.generateChildTrigger(mElementInfo, childtrigger, values, child_inputs));
        }
      }
      
      // construct the child element's context
      mElementState.setTriggerInputs(child_inputs);
      element_context = mRequestState.getElementContext(child_element_info, mResponse);
    }
    
    return element_context;
  }
  
  private ElementContext handleExitTrigger(String exit)
  throws EngineException
  {
    // get the the flowlink to check first if it's a redirection
    FlowLink  flowlink = mElementInfo.getFlowLink(exit);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
    }

    if (flowlink.isRedirect())
    {
      getResponse().sendRedirect(_getExitQueryUrl(flowlink, null, mOutputs.aggregateValues(), null).toString());
      return null;
    }
    else
    {
      return setupExitContext(exit);
    }
  }

  private ElementContext setupExitContext(String exit)
  throws EngineException
  {
    // get the element info of the exit target
    FlowLink flowlink = mElementInfo.getFlowLink(exit);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElementInfo.getDeclarationName(), exit);
    }
    ElementInfo target = flowlink.getExitTarget(mRequestState);
    
    // handle embedding cancellation
    if (mRequestState.isEmbedded() &&
      flowlink.cancelEmbedding())
    {
      // this flag is set in the embedding context to indicate that the
      // embedding needs to be cancelled, it will be checked by the
      // processEmbeddedElement method of the embedding element
      mRequestState.getEmbeddingContext().setCancelEmbedding(true);
      
      // clear all current output buffers
      RequestState request_state = mRequestState;
      while (request_state.isEmbedded())
      {
        request_state.getEmbeddingContext().getElementContext().getResponse().clearBuffer();
        request_state = request_state.getEmbeddingContext().getElementContext().getRequestState();
      }
    }
    
    Map<String, String[]>  exit_request_params = null;
    Map<String, String[]>  exit_inputs = null;
    Stack<ElementInfo>    inheritance_stack = null;
    
    Set<Map.Entry<String, String[]>> output_entries = mOutputs.aggregateValues().entrySet();
    if (mElementState.inInheritanceStructure() &&
      !flowlink.cancelInheritance())
    {
      // verify if the trigger wasn't raised automatically, if this is the
      // case, the first trigger context has to be removed from the trigger
      // list since it has been used by this element
      if (mElementState.isNextExitTrigger(mElementInfo, exit))
      {
        exit_request_params = mElementState.getRequestParameters();
        // obtain the stored exit inputs
        exit_inputs = mElementState.nextTrigger().getParameters();
      }
      // since the current element deferred the logical flow to an exit,
      // record this action in the trigger list
      else
      {
        exit_request_params = new HashMap<String, String[]>();
        exit_request_params.put(ReservedParameters.CHILDREQUEST, new String[] {getEncodedChildRequest()});
        
        exit_request_params.put(ReservedParameters.TRIGGERLIST, mElementState.getRequestParameterValues(ReservedParameters.TRIGGERLIST));
        
        exit_inputs = getExitInputValues(flowlink, output_entries, target, flowlink.isSnapback());
        
        mElementState.addTrigger(TriggerContext.generateExitTrigger(mElementInfo, exit, exit_inputs));
      }
      
      inheritance_stack = mElementState.getInheritanceStack();
      
      // check for successive inheritance stacks
      Stack<ElementInfo>  dest_inheritance_stack = target.getInheritanceStack();
      if (dest_inheritance_stack != null)
      {
        inheritance_stack.addAll(dest_inheritance_stack);
        target = inheritance_stack.pop();
      }
    }
    else
    {
      exit_request_params = new HashMap<String, String[]>();
      exit_inputs = getExitInputValues(flowlink, output_entries, target, flowlink.isSnapback());
      
      mRequestState.setTarget(target);
      
      // obtain the inheritance stack and if it exists the top parent
      // this isn't done if an inheritance structure is already present
      Stack<ElementInfo>  dest_inheritance_stack = target.getInheritanceStack();
      if (dest_inheritance_stack != null)
      {
        inheritance_stack = new Stack<ElementInfo>();
        inheritance_stack.addAll(dest_inheritance_stack);
        target = inheritance_stack.pop();
      }
    }
    
    // the request method isn't get or post anymore since all parameters have
    // been removed
    mElementState.setMethod(RequestMethod.EXIT);
    
    // construct the target element's context
    mElementState.setRequestParameters(exit_request_params);
    mElementState.setInheritanceStack(inheritance_stack);
    mElementState.setTriggerInputs(exit_inputs);
    
    // return the new element context
    return mRequestState.getElementContext(target, mResponse);
  }
  
  private void handleForward(String url)
  {
    boolean is_absolute_url = true;
    if (-1 == url.indexOf(":/"))
    {
      is_absolute_url = false;
      
      StringBuilder absolute_url = new StringBuilder();
      absolute_url.append(mRequestState.getWebappRootUrl(RifeConfig.Engine.getLocalForwardPort()));
      if (url.startsWith("/"))
      {
        absolute_url.append(url.substring(1));
      }
      else
      {
        absolute_url.append(url);
      }
      url = absolute_url.toString();
    }
    
    try
    {
      Map<String, String> request_header_map = new HashMap<String, String>();
      
      Request  request = mRequestState.getRequest();
      Enumeration<String>  request_header_names = request.getHeaderNames();
      
      // convert the headers to a map
      if (request_header_names.hasMoreElements())
      {
        String  header_name = null;
        String  header_name_lowercase = null;
        String  header_value = null;
        do
        {
          header_name = request_header_names.nextElement();
          if (null != header_name)
          {
            header_name_lowercase = header_name.toLowerCase();
            header_value = request.getHeader(header_name);
            if (is_absolute_url &&
              ("host".equals(header_name_lowercase) ||
               "connection".equals(header_name_lowercase) ||
               "keep-alive".equals(header_name_lowercase) ||
               "content-type".equals(header_name_lowercase) ||
               "content-length".equals(header_name_lowercase)))
            {
              continue;
            }
            request_header_map.put(header_name, header_value);
          }
        }
        while (request_header_names.hasMoreElements());
      }
      
      // retrieve the page
      HttpUtils.Page page = new HttpUtils.Request(url).headers(request_header_map).retrieve();
      
      // incorporate the page data in the current request
      if ((page.getResponseCode() / 100) != 2)
      {
        // report errors
        mResponse.sendError(page.getResponseCode(), page.getResponseMessage());
      }
      else
      {
        // preserve the status code
        mResponse.setStatus(page.getResponseCode());
      }

      if (page.getContent() != null)
      {
        mResponse.print(page.getContent());
      }
      
      for (Map.Entry<String, List<String>> header : page.getHeaders().entrySet())
      {
        if (header.getKey() != null)
        {
          String key = header.getKey().toLowerCase();
          
          // strip out several duplicate headers
          if (key.equals("accept-encoding") ||
            key.equals("content-encoding") ||
            key.equals("content-length") ||
            key.equals("date") ||
            key.equals("host") ||
            key.equals("server") ||
            key.equals("transfer-encoding"))
          {
            continue;
          }
          
          // handle the content type differently
          if (key.equals("content-type"))
          {
            mResponse.setContentType(HttpUtils.extractMimeTypeFromContentType(page.getContentType()));
            continue;
          }
          
          for (String header_value : header.getValue())
          {
            mResponse.addHeader(header.getKey(), header_value);
          }
        }
      }
    }
    catch (IOException e)
    {
      mResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
    
    RequestDispatcher dispatcher = mRequestState.getRequest().getRequestDispatcher(url);
    if (null == dispatcher)
    {
      mResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
  }
  
  RequestState getRequestState()
  {
    return mRequestState;
  }
  
  ElementInfo getElementInfo()
  {
    return mElementInfo;
  }
  
  ElementSupport getElementSupport()
  {
    return mElement;
  }
  
  ElementExecutionState getElementState()
  {
    return mElementState;
  }
  
  Response getResponse()
  {
    return mResponse;
  }
  
  void setResponse(Response response)
  {
    mResponse = response;
  }
  
  Template getHtmlTemplate(String name, String encoding, TemplateTransformer transformer)
  throws TemplateException
  {
    if (null == name)      throw new IllegalArgumentException("name can't be null.");
    if (0 == name.length())    throw new IllegalArgumentException("name can't be empty.");

    return TemplateFactory.ENGINEHTML.get(name, encoding, transformer);
  }
  
  Template getXhtmlTemplate(String name, String encoding, TemplateTransformer transformer)
  throws TemplateException
  {
    if (null == name)      throw new IllegalArgumentException("name can't be null.");
    if (0 == name.length())    throw new IllegalArgumentException("name can't be empty.");

    return TemplateFactory.ENGINEXHTML.get(name, encoding, transformer);
  }
  
  Template getXmlTemplate(String name, String encoding, TemplateTransformer transformer)
  throws TemplateException
  {
    if (null == name)      throw new IllegalArgumentException("name can't be null.");
    if (0 == name.length())    throw new IllegalArgumentException("name can't be empty.");

    return TemplateFactory.ENGINEXML.get(name, encoding, transformer);
  }
  
  Template getTxtTemplate(String name, String encoding, TemplateTransformer transformer)
  throws TemplateException
  {
    if (null == name)      throw new IllegalArgumentException("name can't be null.");
    if (0 == name.length())    throw new IllegalArgumentException("name can't be empty.");

    return TemplateFactory.ENGINETXT.get(name, encoding, transformer);
  }
  
  void processEmbeddedElementsEarly(Template template, ElementSupport embeddingElement)
  {
    // process the embedded elements
    if (template.hasFilteredValues(TAG_ELEMENT))
    {
      List<String[]>  element_tags = template.getFilteredValues(TAG_ELEMENT);
      for (String[] captured_groups : element_tags)
      {
        // only embed the element if the value hasn't been set in the
        // template yet and is declared as 'early'
        if (!template.isValueSet(captured_groups[0]) &&
          captured_groups[2].equals("-"))
        {
          processEmbeddedElement(captured_groups[0], template, embeddingElement, captured_groups[3], captured_groups[4], null);
        }
      }
      
      for (String[] captured_groups : element_tags)
      {
        // only embed the element if the value hasn't been set in the
        // template yet and has no specific priority
        if (!template.isValueSet(captured_groups[0]) &&
          0 == captured_groups[2].length())
        {
          processEmbeddedElement(captured_groups[0], template, embeddingElement, captured_groups[3], captured_groups[4], null);
        }
      }
    }
  }

  void processEmbeddedElementsLate(Template template, ElementSupport embeddingElement)
  {
    // process the embedded elements
    if (template.hasFilteredValues(TAG_ELEMENT))
    {
      List<String[]>  element_tags = template.getFilteredValues(TAG_ELEMENT);
      for (String[] captured_groups : element_tags)
      {
        // only embed the element if the value hasn't been set in the
        // template yet and is declared as late
        if (!template.isValueSet(captured_groups[0]) &&
          captured_groups[2].equals("+"))
        {
          processEmbeddedElement(captured_groups[0], template, embeddingElement, captured_groups[3], captured_groups[4], null);
        }
      }
    }
  }
  
  void print(Template template)
  throws TemplateException, EngineException
  {
    List<String> set_values = processTemplate(template);
    
    // set the content type
    if (!mResponse.isContentTypeSet())
    {
      String content_type = template.getDefaultContentType();
      if (null == content_type)
      {
        content_type = RifeConfig.Engine.getDefaultContentType();
      }
      
      mResponse.setContentType(content_type);
    }
    
    // print the element contents with the auto-generated values
    mResponse.print(template);

    // clean up the values that were set
    template.removeValues(set_values);
  }

  List<String> processTemplate(Template template)
  throws TemplateException, EngineException
  {
    List<String> set_values = new ArrayList<String>();
    
    // pre-obtain the output entries, since they process the outjection logic
    Map<String, String[]> output_value_map = mOutputs.aggregateValues();
    Set<Map.Entry<String, String[]>> output_value_entries = output_value_map.entrySet();
    
    // retrieve the template encoder
    TemplateEncoder encoder = template.getEncoder();
    
    // process the filtered roleuser tags
    evaluateExpressionRoleUserTags(set_values, template, null);
    
    // create values for the exit urls
    Set<Map.Entry<String, FlowLink>>  exit_entries = mElementInfo.getExitEntries();
    if (exit_entries.size() > 0)
    {
      String    exit_name = null;
      FlowLink  exit_flowlink = null;
      String    exit_query_value_id = null;
      String    exit_form_value_id = null;
      String    exit_params_value_id = null;
      String    exit_paramsjs_value_id = null;
      
      for (Map.Entry<String, FlowLink> exit_entry : exit_entries)
      {
        if (exit_entry.getValue() != null)
        {
          exit_name = exit_entry.getKey();
          exit_flowlink = exit_entry.getValue();
          
          exit_query_value_id = PREFIX_EXIT_QUERY+exit_name;
          exit_form_value_id = PREFIX_EXIT_FORM+exit_name;
          exit_params_value_id = PREFIX_EXIT_PARAMS+exit_name;
          exit_paramsjs_value_id = PREFIX_EXIT_PARAMS+exit_name;
          
          if (template.hasValueId(exit_query_value_id) &&
            !template.isValueSet(exit_query_value_id))
          {
            template.setValue(exit_query_value_id, _getExitQueryUrl(exit_flowlink, null, output_value_map, null).encoder(encoder));
            set_values.add(exit_query_value_id);
          }
          
          if (template.hasValueId(exit_form_value_id))
          {
            // only substitute the template value if it hasn't been specified yet
            // this allows for user overriding
            if (!template.isValueSet(exit_form_value_id))
            {
              template.setValue(exit_form_value_id, _getExitFormUrl(exit_flowlink, null, output_value_map).encoder(encoder));
              set_values.add(exit_form_value_id);
            }
            
            // only substitute the form parameters template value if the url is present
            if (template.hasValueId(exit_params_value_id))
            {
              // only substitute the template value if it hasn't been specified yet
              // this allows for user overriding
              if (!template.isValueSet(exit_params_value_id))
              {
                template.setValue(exit_params_value_id, _getExitFormParameters(exit_flowlink, output_value_map, null));
                set_values.add(exit_params_value_id);
              }
            }
            else if (template.hasValueId(exit_paramsjs_value_id))
            {
              // only substitute the template value if it hasn't been specified yet
              // this allows for user overriding
              if (!template.isValueSet(exit_paramsjs_value_id))
              {
                template.setValue(exit_paramsjs_value_id, _getExitFormParametersJavascript(exit_flowlink, output_value_map, null));
                set_values.add(exit_paramsjs_value_id);
              }
            }
            else
            {
              throw new EngineException("The required template value '"+exit_params_value_id+"' is not present. Replacement of the template value '"+exit_form_value_id+"' alone is not sufficient.");
            }
          }
          else
          {
            if (template.hasValueId(exit_params_value_id))
            {
              throw new EngineException("The template value '"+exit_params_value_id+"' was specified, while the template value '"+exit_form_value_id+"' could not be found. This is not sufficient, both are needed.");
            }
            else if (template.hasValueId(exit_paramsjs_value_id))
            {
              throw new EngineException("The template value '"+exit_paramsjs_value_id+"' was specified, while the template value '"+exit_form_value_id+"' could not be found. This is not sufficient, both are needed.");
            }
          }
        }
      }
    }
    
    // create values for submission urls
    String  submission_query_value_id = null;
    String  submission_form_value_id = null;
    String  submission_params_value_id = null;
    String  submission_paramsjs_value_id = null;
    
    for (String submission_name : mElementInfo.getSubmissionNames())
    {
      submission_query_value_id = PREFIX_SUBMISSION_QUERY+submission_name;
      submission_form_value_id = PREFIX_SUBMISSION_FORM+submission_name;
      submission_params_value_id = PREFIX_SUBMISSION_PARAMS+submission_name;
      submission_paramsjs_value_id = PREFIX_SUBMISSION_PARAMSJS+submission_name;
      
      if (template.hasValueId(submission_query_value_id) &&
        !template.isValueSet(submission_query_value_id))
      {
        template.setValue(submission_query_value_id, _getSubmissionQueryUrl(submission_name, null, null, output_value_entries).encoder(encoder));
        set_values.add(submission_query_value_id);
      }
      
      if (template.hasValueId(submission_form_value_id))
      {
        // only substitute the template value if it hasn't been specified yet
        // this allows for user overriding
        if (!template.isValueSet(submission_form_value_id))
        {
          template.setValue(submission_form_value_id, getSubmissionFormUrl(null).encoder(encoder));
          set_values.add(submission_form_value_id);
        }
        
        // only substitute the form parameters template value if the url is present
        if (template.hasValueId(submission_params_value_id))
        {
          // only substitute the template value if it hasn't been specified yet
          // this allows for user overriding
          if (!template.isValueSet(submission_params_value_id))
          {
            template.setValue(submission_params_value_id, _getSubmissionFormParameters(submission_name, null, output_value_entries));
            set_values.add(submission_params_value_id);
          }
        }
        else if (template.hasValueId(submission_paramsjs_value_id))
        {
          // only substitute the template value if it hasn't been specified yet
          // this allows for user overriding
          if (!template.isValueSet(submission_paramsjs_value_id))
          {
            template.setValue(submission_paramsjs_value_id, _getSubmissionFormParametersJavascript(submission_name, null, output_value_entries));
            set_values.add(submission_paramsjs_value_id);
          }
        }
        else
        {
          throw new EngineException("The template value '"+submission_params_value_id+"' nor '"+submission_paramsjs_value_id+"'are not present. Replacement of the template value '"+submission_form_value_id+"' alone is not sufficient.");
        }
      }
      else
      {
        if (template.hasValueId(submission_params_value_id))
        {
          throw new EngineException("The template value '"+submission_params_value_id+"' was specified, while the template value '"+submission_form_value_id+"' could not be found. This is not sufficient, both are needed.");
        }
        else if (template.hasValueId(submission_paramsjs_value_id))
        {
          throw new EngineException("The template value '"+submission_paramsjs_value_id+"' was specified, while the template value '"+submission_form_value_id+"' could not be found. This is not sufficient, both are needed.");
        }
      }
    }
    
    // create values for properties
    if (template.hasFilteredValues(TAG_PROPERTY))
    {
      String  property_value_id = null;
      String  property_value = null;

      for (String[] property_tag : template.getFilteredValues(TAG_PROPERTY))
      {
        property_value_id = PREFIX_PROPERTY+property_tag[1];
        if (template.hasValueId(property_value_id) &&
          !template.isValueSet(property_value_id))
        {
          property_value = mElementInfo.getPropertyString(property_tag[1]);
          if (property_value != null)
          {
            template.setValue(property_value_id, encoder.encode(property_value));
            set_values.add(property_value_id);
          }
        }
      }
    }
    
    // create values for inputs
    String    input_value_id = null;
    String[]  input_values = null;
    
    for (Map.Entry<String, String[]> input_entry : mElementState.getInputEntries())
    {
      if (mElementInfo.containsInput(input_entry.getKey()) &&
        mElementState.hasInputValue(input_entry.getKey()))
      {
        input_value_id = PREFIX_INPUT+input_entry.getKey();
        input_values = input_entry.getValue();
        if (template.hasValueId(input_value_id) &&
          !template.isValueSet(input_value_id))
        {
          template.setValue(input_value_id, encoder.encode(input_values[0]));
          set_values.add(input_value_id);
        }
        
        set_values.addAll(selectInputParameter(template, input_entry.getKey(), input_values));
      }
    }
    
    // create values for outputs
    String  output_value_id = null;
    
    for (Map.Entry<String, String[]> output_entry : output_value_entries)
    {
      output_value_id = PREFIX_OUTPUT+output_entry.getKey();
      if (template.hasValueId(output_value_id) &&
        !template.isValueSet(output_value_id) &&
        output_entry.getValue() != null)
      {
        template.setValue(output_value_id, encoder.encode(output_entry.getValue()[0]));
        set_values.add(output_value_id);
      }
    }
    
    // create values for global variables
    for (String globalvar_name : mElementInfo.getGlobalVarNames())
    {
      if(mElementState.hasInputValue(globalvar_name))
      {
        input_value_id = PREFIX_INPUT+globalvar_name;
        if (template.hasValueId(input_value_id) &&
          !template.isValueSet(input_value_id))
        {
          template.setValue(input_value_id, encoder.encode(mElementState.getInput(globalvar_name)));
          set_values.add(input_value_id);
        }
        
        set_values.addAll(selectInputParameter(template, globalvar_name, mElementState.getInputValues(globalvar_name)));
      }
      
      String[] global_output_value = output_value_map.get(globalvar_name);
      if (global_output_value != null)
      {
        output_value_id = PREFIX_OUTPUT+globalvar_name;
        if (template.hasValueId(output_value_id) &&
          !template.isValueSet(output_value_id))
        {
          template.setValue(output_value_id, encoder.encode(global_output_value[0]));
          set_values.add(output_value_id);
        }
      }
    }
    
    // create values for in cookies
    String  incookie_value_id = null;
    for (Map.Entry<String, String> incookie_entry : getIncookieEntries())
    {
      if (mElementInfo.containsIncookie(incookie_entry.getKey()) ||
        mElementInfo.containsGlobalCookie(incookie_entry.getKey()))
      {
        incookie_value_id = PREFIX_INCOOKIE+incookie_entry.getKey();
        if (template.hasValueId(incookie_value_id) &&
          !template.isValueSet(incookie_value_id))
        {
          template.setValue(incookie_value_id, encoder.encode(incookie_entry.getValue()));
          set_values.add(incookie_value_id);
        }
      }
    }
    
    // create values for out cookies
    String  outcookie_value_id = null;
    for (Map.Entry<String, String> outcookie_entry : mOutcookies.aggregateValues().entrySet())
    {
      if (mElementInfo.containsOutcookiePossibility(outcookie_entry.getKey()))
      {
        outcookie_value_id = PREFIX_OUTCOOKIE+outcookie_entry.getKey();
        if (template.hasValueId(outcookie_value_id) &&
          !template.isValueSet(outcookie_value_id))
        {
          template.setValue(outcookie_value_id, encoder.encode(outcookie_entry.getValue()));
          set_values.add(outcookie_value_id);
        }
      }
    }
    
    // set the webapp root
    if (template.hasValueId(ID_WEBAPP_ROOTURL) &&
      !template.isValueSet(ID_WEBAPP_ROOTURL))
    {
      template.setValue(ID_WEBAPP_ROOTURL, mRequestState.getWebappRootUrl(-1));
      set_values.add(ID_WEBAPP_ROOTURL);
    }
    
    // set the server root
    if (template.hasValueId(ID_SERVER_ROOTURL) &&
      !template.isValueSet(ID_SERVER_ROOTURL))
    {
      template.setValue(ID_SERVER_ROOTURL, mRequestState.getServerRootUrl(-1));
      set_values.add(ID_SERVER_ROOTURL);
    }
    
    // create values for submission parameters and beans
    String[]  submission_param_values = null;
    String    submission_param_value_id = null;
    
    for (Submission submission : mElementInfo.getSubmissions())
    {
      // create the parameters
      for (String submission_param_name : submission.getParameterNames())
      {
        if (mElementState.hasRequestParameterValue(submission_param_name))
        {
          submission_param_values = mElementState.getRequestParameterValues(submission_param_name);
          
          submission_param_value_id = PREFIX_PARAM+submission_param_name;
          if (template.hasValueId(submission_param_value_id) &&
            !template.isValueSet(submission_param_value_id))
          {
            template.setValue(submission_param_value_id, encoder.encode(submission_param_values[0]));
            set_values.add(submission_param_value_id);
          }
          
          set_values.addAll(selectSubmissionParameter(template, submission_param_name, submission_param_values));
        }
      }
      
      // create the bean forms
      BeanHandler bean_handler = template.getBeanHandler();
      if (bean_handler != null)
      {
        FormBuilder  form_builder = bean_handler.getFormBuilder();
        if (form_builder != null)
        {
          for (BeanDeclaration bean : submission.getBeans())
          {
            try
            {
              Map<String, String[]> parameters = null;
              if (hasSubmission() &&
                submission.getName().equals(getSubmission()))
              {
                parameters = mElementState.getRequestParameters();
              }
              form_builder.generateForm(template, bean.getBeanClass(), parameters, bean.getPrefix());
            }
            catch (Throwable e)
            {
              throw new SubmissionBeanFormGenerationErrorException(mElementInfo.getDeclarationName(), submission.getName(), bean.getClassname(), e);
            }
          }
        }
      }
    }
    
    // process the late embedded elements
    processEmbeddedElementsLate(template, mElement);
    
    return set_values;
  }
  
  void triggerChild(String childTriggerName, String[] childTriggerValues)
  throws EngineException
  {
    mElement.enableRequestAccess(false);
    try
    {
      if (mElement.childTriggered(childTriggerName, childTriggerValues))
      {
        throw new ChildTriggeredException(childTriggerName, childTriggerValues);
      }
    }
    finally
    {
      mElement.enableRequestAccess(true);
    }
  }
  
  boolean hasSubmission()
  {
    return null != getSubmission();
  }
  
  boolean hasSubmission(String name)
  {
    String submission = getSubmission();
    return submission != null &&
         submission.equals(name);

  }
  
  String getSubmission()
  {
    if (null == mSubmission)
    {
      return null;
    }
    
    return mSubmission;
  }
  
  private void validateParameter(String parameterName)
  throws EngineException
  {
    assert parameterName != null;
    assert parameterName.length() > 0;
    
    boolean found = false;
    for (Submission submission : mElementInfo.getSubmissions())
    {
      if (submission.containsParameter(parameterName))
      {
        found = true;
        break;
      }
    }
    
    if (!found)
    {
      throw new ParameterUnknownException(mElementInfo.getDeclarationName(), parameterName);
    }
  }
  
  private void validateFile(String fileName)
  throws EngineException
  {
    assert fileName != null;
    assert fileName.length() > 0;
    
    boolean found = false;
    for (Submission submission : mElementInfo.getSubmissions())
    {
      if (submission.containsFile(fileName))
      {
        found = true;
        break;
      }
    }
    
    if (!found)
    {
      throw new FileUnknownException(mElementInfo.getDeclarationName(), fileName);
    }
  }
  
  boolean isInputEmpty(String name)
  throws EngineException
  {
    String input = getInput(name);

    return null == input ||
         input.trim().equals("");
  }
  
  String getInput(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateInputName(name);
    
    String input = mElementState.getInput(name);
    
    if (null == input &&
      mElementInfo.hasInputDefaults())
    {
      String[] default_values = mElementInfo.getInputDefaultValues(name);
      if (default_values != null)
      {
        input = default_values[0];
      }
    }

    if (null == input &&
      mElementInfo.hasGlobalVarDefaults())
    {
      String[] default_values = mElementInfo.getGlobalVarDefaultValues(name);
      if (default_values != null)
      {
        input = default_values[0];
      }
    }

    return input;
  }
  
  String[] getInputValues(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateInputName(name);
    
    String[] input_values = mElementState.getInputValues(name);
    
    if (null == input_values &&
      mElementInfo.hasInputDefaults())
    {
      input_values = mElementInfo.getInputDefaultValues(name);
    }
    
    if (null == input_values &&
      mElementInfo.hasGlobalVarDefaults())
    {
      input_values = mElementInfo.getGlobalVarDefaultValues(name);
    }
    
    return input_values;
  }
  
  boolean hasInputValue(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateInputName(name);
    
    return mElementState.hasInputValue(name) || mElementInfo.hasInputDefaultValues(name) || mElementInfo.hasGlobalVarDefaultValues(name);
  }

  Collection<String> selectInputParameter(Template template, String name, String[] values)
  {
    return selectParameter(template, PREFIX_INPUT+name, values);
  }
  
  OutputValues getOutputs()
  {
    return mOutputs;
  }
  
  void setOutput(String name, String value)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert value != null;
    
    mElementInfo.validateOutputName(name);
    
    // create a string array
    String[] value_array = new String[]{value};

    // store the value
    setOutputValues(name, value_array);
    
    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(name))
    {
      triggerChild(name, value_array);
    }
  }
  
  void setOutput(String name, String[] values)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert values != null;
    assert values.length > 0;
    
    mElementInfo.validateOutputName(name);
    
    setOutputValues(name, values);
    
    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(name))
    {
      triggerChild(name, values);
    }
  }
  
  void setOutput(String name, Object value, ConstrainedProperty constrainedProperty)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert value != null;
    
    String[] value_array = getOutputObjectValues(name, value, constrainedProperty);
    
    // store the value
    setOutputValues(name, value_array);
    
    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(name))
    {
      triggerChild(name, value_array);
    }
  }

  private String[] getOutputObjectValues(String name, Object value, ConstrainedProperty constrainedProperty)
  throws EngineException
  {
    mElementInfo.validateOutputName(name);
    
    // create a string array
    String[] value_array = null;
    
    // convert the value to a string representation
    Class  value_type = value.getClass();
    if (value_type.isArray() ||
      !(value instanceof Serializable) ||
      ClassUtils.isBasic(value_type))
    {
      value_array = ArrayUtils.createStringArray(value, constrainedProperty);
    }
    else
    {
      try
      {
        value_array = new String[]{SerializationUtils.serializeToString((Serializable)value)};
      }
      catch (SerializationUtilsErrorException e)
      {
        throw new UnserializableOutputValueException(mElementInfo.getDeclarationName(), name, value, e);
      }
    }

    return value_array;
  }

  void addOutputValue(String name, String value)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert value != null;
    
    mElementInfo.validateOutputName(name);
    
    String[] value_array = null;
    
    String[] existing_values = mOutputs.get(name);
    if (existing_values != null)
    {
      value_array = ArrayUtils.join(existing_values, value);
    }
    else
    {
      value_array = new String[]{value};
    }
    
    setOutputValues(name, value_array);

    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(name))
    {
      triggerChild(name, value_array);
    }
  }
  
  void addOutputValues(String name, String[] values)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert values != null;
    assert values.length > 0;
    
    mElementInfo.validateOutputName(name);
    
    String[] value_array = null;

    String[] existing_values = mOutputs.get(name);
    if (existing_values != null)
    {
      value_array = ArrayUtils.join(existing_values, values);
    }
    else
    {
      value_array = values;
    }
    
    setOutputValues(name, value_array);
    
    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(name))
    {
      triggerChild(name, value_array);
    }
  }
  
  void addOutputValue(String name, Object value)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert value != null;
    
    mElementInfo.validateOutputName(name);

    // convert the value to a string representation
    String  value_text = null;
    Class  value_type = value.getClass();
    if (!(value instanceof Serializable) ||
      ClassUtils.isBasic(value_type))
    {
      value_text = String.valueOf(value);
    }
    else if (value instanceof Serializable)
    {
      try
      {
        value_text = SerializationUtils.serializeToString((Serializable)value);
      }
      catch (SerializationUtilsErrorException e)
      {
        throw new UnserializableOutputValueException(mElementInfo.getDeclarationName(), name, value, e);
      }
    }
    else
    {
      value_text = String.valueOf(value);
    }
    
    addOutputValue(name, value_text);
  }
  
  void clearOutput(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateOutputName(name);
    
    clearOutputValue(name);
  }

  void clearNamedOutputBean(String name)
  throws EngineException
  {
    assert name != null;

    mElementInfo.validateOutbeanName(name);

    BeanDeclaration  output_bean = mElementInfo.getNamedOutbeanInfo(name);
    if (null == output_bean)
    {
      output_bean = mElementInfo.getNamedGlobalBeanInfo(name);
    }

    Class  output_bean_class = null;
    try
    {
      output_bean_class = Class.forName(output_bean.getClassname());
    }
    catch (ClassNotFoundException e)
    {
      throw new NamedOutbeanClassnameErrorException(mElementInfo.getDeclarationName(),name, output_bean.getClassname());
    }
    
    clearOutputBean(output_bean_class, output_bean.getPrefix());
  }

  void clearOutputBean(Class beanClass, String prefix)
  throws EngineException
  {
    if (null == beanClass)    throw new IllegalArgumentException("beanClass can't be null.");
    
    try
    {
      // handle outputs
      Collection<String>  output_names = mElementInfo.getOutputNames();
      String[]      output_names_array = new String[output_names.size()];
      output_names.toArray(output_names_array);
      
      // handle globals
      Collection<String>  globalvar_names = mElementInfo.getGlobalVarNames();
      String[]      globalvar_names_array = new String[globalvar_names.size()];
      globalvar_names.toArray(globalvar_names_array);
      
      // merge the both arrays
      String[]  merged_names_array = ArrayUtils.join(output_names_array, globalvar_names_array);
      
      // process all the possible output names
      Set<String>  property_names = BeanUtils.getPropertyNames(beanClass, merged_names_array, null, prefix);
      for (String property_name : property_names)
      {
        clearOutput(property_name);
      }
    }
    catch (BeanUtilsException e)
    {
      throw new BeanClassNamesErrorException(beanClass, e);
    }
  }
  
  String[] getOutput(String name)
  throws EngineException
  {
    mElementInfo.validateOutputName(name);
    
    return mOutputs.aggregateValues().get(name);
  }
  
  private Set<Map.Entry<String, String>> getIncookieEntries()
  {
    return getCookieValues().entrySet();
  }
  
  boolean hasCookie(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateIncookieName(name);
    
    return mRequestState.hasCookie(name) || mElementInfo.hasIncookieDefaultValue(name) || mElementInfo.hasGlobalCookieDefaultValue(name);
  }
  
  Cookie getCookie(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateIncookieName(name);
    
    Cookie cookie = mRequestState.getCookie(name);
    if (null == cookie )
    {
      if (mElementInfo.hasIncookieDefaultValue(name))
      {
        cookie = new Cookie(name, mElementInfo.getIncookieDefaultValue(name));
      }
    
      if (mElementInfo.hasGlobalCookieDefaultValue(name))
      {
        cookie = new Cookie(name, mElementInfo.getGlobalCookieDefaultValue(name));
      }
    }
    
    return cookie;
  }
  
  String getCookieValue(String name)
  throws EngineException
  {
    Cookie  cookie = getCookie(name);
    String  value = null;
    if (cookie != null)
    {
      value = cookie.getValue();
    }
    
    return value;
  }
  
  void setCookie(Cookie cookie)
  throws EngineException
  {
    assert cookie != null;
    assert cookie.getName() != null;
    
    mElementInfo.validateOutcookieName(cookie.getName());
    
    mOutcookies.put(cookie.getName(), cookie.getValue());
    setCookieRaw(cookie);
    
    if (mElementState.inInheritanceStructure() &&
      mElementInfo.containsChildTrigger(cookie.getName()))
    {
      triggerChild(cookie.getName(), new String[] {cookie.getValue()});
    }
  }

  void setCookieRaw(Cookie cookie)
  {
    mResponse.addCookie(cookie);
    mRequestState.setStateCookie(cookie);
    fireOutcookieSet(cookie);
  }
  
  HashMap<String, String> getCookieValues()
  throws EngineException
  {
    HashMap<String, String> result = new HashMap<String, String>();
    
    for (Map.Entry<String, String> entry : mElementInfo.getDefaultIncookies().entrySet())
    {
      result.put(entry.getKey(), entry.getValue());
    }
    
    for (Map.Entry<String, String> entry : mElementInfo.getDefaultGlobalCookies().entrySet())
    {
      result.put(entry.getKey(), entry.getValue());
    }
    
    for (Map.Entry<String, Cookie> entry : mRequestState.getCookies().entrySet())
    {
      result.put(entry.getKey(), entry.getValue().getValue());
    }
    
    return result;
  }
  
  boolean hasParameterValue(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateParameter(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return false;
    }
    
    return mElementState.hasRequestParameterValue(name) || mElementInfo.hasParameterDefaultValues(submission, name);
  }
    
  boolean isParameterEmpty(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    String parameter = getParameter(name);
    if (null == parameter ||
      parameter.trim().equals(""))
    {
      return true;
    }
    return false;
  }
  
  String getParameter(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateParameter(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return null;
    }

    String parameter = mElementState.getRequestParameter(name);
    
    if (null == parameter &&
      mElementInfo.hasParameterDefaults(submission))
    {
      String[] default_values = mElementInfo.getParameterDefaultValues(submission, name);
      if (default_values != null)
      {
        return default_values[0];
      }
    }

    return parameter;
  }
  
  ArrayList<String> getParameterNames(String regexp)
  throws EngineException
  {
    Pattern  pattern = null;
    if (regexp != null)
    {
      pattern = Pattern.compile("^"+regexp+"$");
    }
    
    ArrayList<String>  result = new ArrayList<String>();
    String        submission_name = getSubmission();
    if (null == submission_name)
    {
      return result;
    }

    Submission  submission = mElementInfo.getSubmission(submission_name);
    if (null == submission)
    {
      return result;
    }
    
    Collection<String>  parameter_names = null;
    
    // add all default parameters that match
    parameter_names = submission.getParameterDefaultNames();
    for (String parameter_name : parameter_names)
    {
      if (null == pattern ||
        pattern.matcher(parameter_name).matches())
      {
        result.add(parameter_name);
      }
    }
    
    // go over the possible parameters and check if they have values in the request
    parameter_names = submission.getParameterNames();
    for (String parameter_name : parameter_names)
    {
      if (mElementState.hasRequestParameterValue(parameter_name) &&
        !result.contains(parameter_name) &&
        (null == pattern || pattern.matcher(parameter_name).matches()))
      {
        result.add(parameter_name);
      }
    }
    
    // go over all the parameter regexps and find those that match with the parameters in the request
    Matcher  matcher = null;
    for (Pattern parameter_regexp : submission.getParameterRegexps())
    {
      for (String parameter_name : mElementState.getRequestParameterNames())
      {
        matcher = parameter_regexp.matcher(parameter_name);
        if (matcher.matches())
        {
          if (mElementState.hasRequestParameterValue(parameter_name) &&
            !result.contains(parameter_name) &&
            (null == pattern || pattern.matcher(parameter_name).matches()))
          {
            result.add(parameter_name);
          }
        }
      }
      
    }
    
    return result;
  }
  
  String[] getParameterValues(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateParameter(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return null;
    }
    
    String[] parameter_values = mElementState.getRequestParameterValues(name);
    if (null == parameter_values &&
      mElementInfo.hasParameterDefaults(submission))
    {
      return mElementInfo.getParameterDefaultValues(submission, name);
    }
    
    return parameter_values;
  }
  
  ArrayList<String> getUploadedFileNames(String regexp)
  throws EngineException
  {
    Pattern  pattern = null;
    if (regexp != null)
    {
      pattern = Pattern.compile("^"+regexp+"$");
    }
    
    ArrayList<String>  result = new ArrayList<String>();
    String        submission_name = getSubmission();
    if (null == submission_name)
    {
      return result;
    }
    
    Submission  submission = mElementInfo.getSubmission(submission_name);
    if (null == submission)
    {
      return result;
    }
    
    Collection<String>  file_names = null;
    
    // go over the possible files and check if they have values in the request
    file_names = submission.getFileNames();
    for (String file_name : file_names)
    {
      if (mRequestState.hasUploadedFile(file_name) &&
        !result.contains(file_name) &&
        (null == pattern || pattern.matcher(file_name).matches()))
      {
        result.add(file_name);
      }
    }
    
    // go over all the file regexps and find those that match with the files in the request
    Matcher  matcher = null;
    for (Pattern file_regexp : submission.getFileRegexps())
    {
      for (String file_name : mRequestState.getUploadedFileNames())
      {
        matcher = file_regexp.matcher(file_name);
        if (matcher.matches())
        {
          if (mRequestState.hasUploadedFile(file_name) &&
            !result.contains(file_name) &&
            (null == pattern || pattern.matcher(file_name).matches()))
          {
            result.add(file_name);
          }
        }
      }
      
    }
    
    return result;
  }
  
  ArrayList<String> getUploadedFileNames()
  throws EngineException
  {
    ArrayList<String>  result = new ArrayList<String>();
    String        submission_name = getSubmission();
    if (null == submission_name)
    {
      return result;
    }

    Submission  submission = mElementInfo.getSubmission(submission_name);
    if (null == submission)
    {
      return result;
    }
    
    Collection<String> file_names = submission.getFileNames();
    for (String file_name : file_names)
    {
      if (mRequestState.hasUploadedFile(file_name))
      {
        result.add(file_name);
      }
    }
    
    return result;
  }
  
  boolean hasUploadedFile(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateFile(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return false;
    }
    
    return mRequestState.hasUploadedFile(name);
  }
    
  boolean isFileEmpty(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    UploadedFile file = getUploadedFile(name);
    return null == file ||
         null == file.getFile() ||
         0 == file.getFile().length();
  }
  
  UploadedFile getUploadedFile(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateFile(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return null;
    }
 
    return mRequestState.getUploadedFile(name);
  }
  
  UploadedFile[] getUploadedFiles(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    validateFile(name);

    String submission = getSubmission();
    if (null == submission)
    {
      return null;
    }

    return mRequestState.getUploadedFiles(name);
  }
  
  void setOutputValues(String name, String[] values)
  {
    assert name != null;
    assert name.length() > 0;
    assert values != null;
    
    mOutputs.put(name, values);
    fireOutputValueSet(name, values);
  }
  
  void setAutomatedOutputValues(String name, String[] values)
  {
    mOutputs.putFallback(name, values);
    
    fireAutomatedOutputValueSet(name, values);
  }
  
  void clearOutputValue(String name)
  {
    assert name != null;
    assert name.length() > 0;
    
    // clearing the output value instead of removing it, this prevents
    // later automated processing to fill it in again
    mOutputs.put(name, null);
    fireOutputValueCleared(name);
  }
  
  void clearAutomatedOutputValue(String name)
  {
    // clearing the output value instead of removing it, this prevents
    // later automated processing to fill it in again
    mOutputs.putFallback(name, null);
    fireAutomatedOutputValueCleared(name);
  }
  
  void addOutputListener(OutputListener listener)
  {
    if (null == mOutputListeners)
    {
      mOutputListeners = new ArrayList<OutputListener>();
    }
    
    mOutputListeners.add(listener);
  }
  
  void removeOutputListener(OutputListener listener)
  {
    mOutputListeners.remove(listener);
  }

  void fireOutputValueSet(String name, String[] values)
  {
    if (null == mOutputListeners)
    {
      return;
    }
    
    for (OutputListener listener : mOutputListeners)
    {
      listener.outputValueSet(name, values);
    }
  }

  void fireOutputValueCleared(String name)
  {
    if (null == mOutputListeners)
    {
      return;
    }
    
    for (OutputListener listener : mOutputListeners)
    {
      listener.outputValueCleared(name);
    }
  }

  void fireAutomatedOutputValueSet(String name, String[] values)
  {
    if (null == mOutputListeners)
    {
      return;
    }
    
    for (OutputListener listener : mOutputListeners)
    {
      listener.automatedOutputValueSet(name, values);
    }
  }

  void fireAutomatedOutputValueCleared(String name)
  {
    if (null == mOutputListeners)
    {
      return;
    }
    
    for (OutputListener listener : mOutputListeners)
    {
      listener.automatedOutputValueCleared(name);
    }
  }
  
  void addOutcookieListener(OutcookieListener listener)
  {
    if (null == mOutcookieListeners)
    {
      mOutcookieListeners = new ArrayList<OutcookieListener>();
    }
    
    mOutcookieListeners.add(listener);
  }
  
  void removeOutcookieListener(OutcookieListener listener)
  {
    mOutcookieListeners.remove(listener);
  }

  void fireOutcookieSet(Cookie cookie)
  {
    if (null == mOutcookieListeners)
    {
      return;
    }
    
    for (OutcookieListener listener : mOutcookieListeners)
    {
      listener.outcookieSet(cookie);
    }
  }

  void child()
  throws EngineException
  {
    throw new ChildTriggeredException(null, null);
  }
  
  void defer()
  throws EngineException
  {
    throw new DeferException();
  }
  
  void forward(String url)
  throws EngineException
  {
    throw new ForwardException(url);
  }
  
  void redirect(String url)
  {
    throw new RedirectException(url);
  }
  
  boolean duringStepBack()
  {
    return mSteppedBack;
  }
  
  void exit(String name)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateExitName(name);
    
    throw new ExitTriggeredException(name);
  }
  
  /**
   * Retrieves the inputs values that a child receives when the parent
   * delegates the control flow to it. This is based on the current output
   * values of the parent element, combined with the global variables
   */
  private Map<String, String[]> getChildInputValues(ElementInfo targetElement)
  {
    Map<String, String[]> inputs = null;
    
    inputs = mElementState.getTriggerInputs();
    
    if (null == inputs)
    {
      inputs = mElementState.getRequestParameters();
    }

    if (mElementInfo.hasGlobalVars() &&
      targetElement.hasGlobalVars())
    {
      String[]  input_values_array = null;
      
      for (Map.Entry<String, String[]> output_entry : mOutputs.aggregateValues().entrySet())
      {
        // create automatic data link for global variables
        if (mElementInfo.containsGlobalVar(output_entry.getKey()) &&
          targetElement.containsGlobalVar(output_entry.getKey()))
        {
          input_values_array = output_entry.getValue();
          inputs.put(output_entry.getKey(), input_values_array);
        }
      }
    }
    
    return inputs;
  }
  
  /**
   * Retrieves the inputs values that an exit receives when it is
   * followed. This is based on the current output values of the
   * active element.
   */
  private HashMap<String, String[]> getExitInputValues(FlowLink flowLink, Set<Map.Entry<String, String[]>> outputEntries, ElementInfo target, boolean snapback)
  throws EngineException
  {
    HashMap<String, String[]> inputs = new HashMap<String, String[]>();
    
    // preserve the embedding element's inputs and global vars
    if (target.getId().equals(mElementInfo.getId()))
    {
      ElementContext context = this;
      while (context != null &&
           context.mRequestState.isEmbedded())
      {
        context = context.mRequestState.getEmbeddingContext().getElementContext();
        for (String input_name : context.mElementInfo.getInputNames())
        {
          if (context.mElementState.hasInputValue(input_name))
          {
            inputs.put(input_name, context.mElementState.getInputValues(input_name));
          }
        }
        for (String globalvar_name : context.mElementInfo.getGlobalVarNames())
        {
          if (context.mElementState.hasInputValue(globalvar_name))
          {
            inputs.put(globalvar_name, context.mElementState.getInputValues(globalvar_name));
          }
        }
      }
    }

    if (mElementInfo.hasSnapbackDataLinks() ||
      mElementInfo.hasDataLink(target) ||
      (mElementInfo.hasGlobalVars() &&
       target.hasGlobalVars()))
    {
      HashMap<String, String[]>  dest_input_candidates = new HashMap<String, String[]>();
      
      // if the exit is reflective, preserve the input values that have
      // identically named output values
      if (target == mElementInfo)
      {
        Collection<String>  input_names = mElementInfo.getInputNames();
        Collection<String>  output_names = mElementInfo.getOutputNames();
        Iterator<String>  input_names_it = input_names.iterator();
        String        input_name = null;
        String[]      input_values = null;
        while (input_names_it.hasNext())
        {
          input_name = input_names_it.next();
          
          if (output_names.contains(input_name))
          {
            input_values = getInputValues(input_name);
            if (input_values != null)
            {
              dest_input_candidates.put(input_name, input_values);
            }
          }
        }
      }
      
      // add the global default values
      GlobalVar  globalvar_data = null;
      
      for (Map.Entry<String, GlobalVar> globalvar_entry : mElementInfo.getGlobalVarEntries())
      {
        globalvar_data = globalvar_entry.getValue();
        if (globalvar_data != null &&
          globalvar_data.getDefaultValues() != null)
        {
          dest_input_candidates.put(globalvar_entry.getKey(), globalvar_data.getDefaultValues());
        }
      }

      // add the output values
      String[]  output_values_array = null;
      
      for (Map.Entry<String, String[]> output_entry : outputEntries)
      {
        output_values_array = output_entry.getValue();
        dest_input_candidates.put(output_entry.getKey(), output_values_array);
      }
      
      // process the exit input value candidates
      Collection<String>  dest_input_names = null;
      
      GlobalVar  globalvar_source = null;
      GlobalVar  globalvar_target = null;
      
      for (String candidate : dest_input_candidates.keySet())
      {
        if (!mElementInfo.containsDepartureVar(candidate))
        {
          // create automatic data link for global variables
          if (mElementInfo.containsGlobalVar(candidate) &&
            target.containsGlobalVar(candidate))
          {
            globalvar_source = mElementInfo.getGlobalVarInfo(candidate);
            globalvar_target = target.getGlobalVarInfo(candidate);
            
            // only create the link if the global vars are in the
            // same group scope
            if (globalvar_source.getGroupId() == globalvar_target.getGroupId())
            {
              inputs.put(candidate, dest_input_candidates.get(candidate));
            }
          }
          
          // translate outputs to inputs through a possible data link
          dest_input_names = mElementInfo.getDataLinkInputs(candidate, target, snapback, flowLink);
          if (dest_input_names != null)
          {
            for (String dest_input_name : dest_input_names)
            {
              inputs.put(dest_input_name, dest_input_candidates.get(candidate));
            }
          }
        }
      }
    }
    
    return inputs;
  }

  private void appendTargetAndPathinfo(StringBuilder url, String targetUrl, String pathinfo)
  {
    url.append(targetUrl);
    
    // append the optional pathinfo
    if (pathinfo != null &&
      pathinfo.length() > 0)
    {
      // ensure that the pathinfo is prefixed with a /
      if (url.charAt(url.length()-1) != '/')
      {
        url.append("/");
      }
      url.append(StringUtils.stripFromFront(pathinfo, "/"));
    }
  }
  
  private String getExitUrl(FlowLink flowlink, String pathInfo)
  throws EngineException
  {
    assert flowlink != null;
    
    String url = null;
    String pathinfo = null;
    
    // obtain the declared flowlink target element and determine what the
    // actual target element is according to the inheritance status
    ElementInfo  flowlink_target = flowlink.getExitTarget(mRequestState);
    ElementInfo  context_target = null;
    if (mElementState.inInheritanceStructure() &&
      !flowlink.cancelInheritance())
    {
      context_target = mRequestState.getTarget();
    }
    else
    {
      context_target = flowlink_target;
    }
    url = context_target.getUrl();
    
    // if the element defined no url and the flowlink target points to the element
    // itself when it's embedded, go up the hierarchy of embedded elements
    // to grab the first defined element url
    if (null == url)
    {
      if (!mRequestState.isEmbedded() &&
        !flowlink_target.getId().equals(mElementInfo.getId()))
      {
        throw new ExitTargetUrlMissingException(mElementInfo.getDeclarationName(), flowlink.getExitName(), context_target.getDeclarationName());
      }
      
      ElementContext context = this;
      while (null == url &&
           context != null &&
           context.mRequestState.isEmbedded())
      {
        context = context.mRequestState.getEmbeddingContext().getElementContext();
        if (context.getElementInfo().isPathInfoUsed())
        {
          pathinfo = context.mRequestState.getElementState().getPathInfo();
        }
        else
        {
          pathinfo = null;
        }
        if (context.mElementState.inInheritanceStructure())
        {
          url = context.mRequestState.getTarget().getUrl();
        }
        else
        {
          url = context.mElementInfo.getUrl();
        }
      }
    }
    
    // apply a forced pathinfo
    if (pathInfo != null &&
      pathInfo.length() > 0)
    {
      pathinfo = pathInfo;
    }
    
    // construct the correct servlet path and url that points to the
    // target child element, or the current element if there are no children
    StringBuilder url_buffer = new StringBuilder(mRequestState.getGateUrl());
    appendTargetAndPathinfo(url_buffer, url, pathinfo);
    return url_buffer.toString();
  }
  
  private Map<String, String[]> overrideOutputValues(Map<String, String[]> outputValueMap, String[] outputValues)
  throws EngineException
  {
    if (null == outputValues ||
      0 == outputValues.length)
    {
      return outputValueMap;
    }
    
    Map<String, String[]>  outputs = new HashMap<String, String[]>(outputValueMap);
    // store the output overrides
    for (int i = 0; i < outputValues.length; i += 2)
    {
      outputs.put(outputValues[i], new String[]{outputValues[i+1]});
    }
    
    return outputs;
  }
  
  private Map<String, String[]> getExitInputValues(FlowLink flowLink, ElementInfo target, boolean snapback, Map<String, String[]> outputValueMap, String[] outputValues)
  throws EngineException
  {
    // override current output values
    Map<String, String[]>  overridden_outputs = overrideOutputValues(outputValueMap, outputValues);
    
    // construct the exit input parameters
    return getExitInputValues(flowLink, overridden_outputs.entrySet(), target, snapback);
  }
  
  private FlowState _getExitParameters(FlowLink flowlink, ElementInfo  contextTarget, Map<String, String[]> outputValueMap, String[] outputValues)
  {
    FlowState state = new FlowState();
    
    // construct the exit input parameters
    Map<String, String[]> inputs = getExitInputValues(flowlink, flowlink.getTarget(), flowlink.isSnapback(), outputValueMap, outputValues);

    // Create or preserve the request parameters that were initially send to the child element
    // this permits completely seperate processing of any other parameters.
    // Maintain a stack of successful parent elements.
    if (mElementState.inInheritanceStructure() &&
      !flowlink.cancelInheritance())
    {
      state.putParameter(ReservedParameters.CHILDREQUEST, getEncodedChildRequest());
      
      // preserve the trigger list
      List<TriggerContext>  new_trigger_context = mElementState.cloneTriggerList();
      new_trigger_context.add(TriggerContext.generateExitTrigger(mElementInfo, flowlink.getExitName(), inputs));
      
      state.putParameter(ReservedParameters.TRIGGERLIST, TriggerListEncoder.encode(new_trigger_context));
    }
    else
    {
      // construct the exit input parameters
      state.setParameters(inputs);
    }

    if (!flowlink.cancelContinuations())
    {
      // Preserve the continuation id if a continuation context is active
      // and the exit goes back to the same element
      if (mElementInfo == flowlink.getTarget())
      {
        String continuation_id = ContinuationContext.getActiveContextId();
        if (continuation_id != null)
        {
          state.putParameter(ReservedParameters.CONTID, continuation_id);
        }
      }
    }
    
    return state;
  }

  CharSequenceDeferred getExitFormUrl(String name, String pathinfo)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    
    mElementInfo.validateExitName(name);
    
    FlowLink  flowlink = null;
    flowlink = mElementInfo.getFlowLink(name);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElement.getElementInfo().getDeclarationName(), name);
    }
    
    return _getExitFormUrl(flowlink, pathinfo, mOutputs.aggregateValues());
  }
  
  private CharSequenceDeferred _getExitFormUrl(FlowLink flowlink, String pathinfo, Map<String, String[]> outputValueMap)
  throws EngineException
  {
    assert flowlink != null;
    
    ElementInfo  flowlink_target = flowlink.getExitTarget(mRequestState);

    StateStore  state_store = flowlink_target.getStateStore();

    // process the pathinfo mappings
    if (null == pathinfo &&
      flowlink_target.isPathInfoUsed())
    {
      FlowState  state = _getExitParameters(flowlink, flowlink_target, outputValueMap, null);
      pathinfo = handlePathInfoMapping(flowlink_target, state, pathinfo);
    }

    return new CharSequenceFormUrl(state_store, getExitUrl(flowlink, pathinfo));
  }
  
  CharSequenceDeferred getExitFormParameters(String name, String[] outputValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert outputValues == null || outputValues.length % 2 == 0;
    
    mElementInfo.validateExitName(name);
    
    FlowLink  flowlink = null;
    flowlink = mElementInfo.getFlowLink(name);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElement.getElementInfo().getDeclarationName(), name);
    }
    
    return _getExitFormParameters(flowlink, mOutputs.aggregateValues(), outputValues);
  }
  
  CharSequenceDeferred getExitFormParametersJavascript(String name, String[] outputValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert outputValues == null || outputValues.length % 2 == 0;
    
    mElementInfo.validateExitName(name);
    
    FlowLink  flowlink = null;
    flowlink = mElementInfo.getFlowLink(name);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElement.getElementInfo().getDeclarationName(), name);
    }
    
    return _getExitFormParametersJavascript(flowlink, mOutputs.aggregateValues(), outputValues);
  }
  
  private CharSequenceDeferred _getExitFormParameters(FlowLink flowlink, Map<String, String[]> outputValueMap, String[] outputValues)
  throws EngineException
  {
    assert flowlink != null;
    
    ElementInfo    flowlink_target = flowlink.getExitTarget(mRequestState);
    FlowState  state = _getExitParameters(flowlink, flowlink_target, outputValueMap, outputValues);
    return new CharSequenceFormState(flowlink_target.getStateStore(), state, FormStateType.PARAMS);
  }

  private CharSequenceDeferred _getExitFormParametersJavascript(FlowLink flowlink, Map<String, String[]> outputValueMap, String[] outputValues)
  throws EngineException
  {
    assert flowlink != null;
    
    ElementInfo    flowlink_target = flowlink.getExitTarget(mRequestState);
    FlowState  state = _getExitParameters(flowlink, flowlink_target, outputValueMap, outputValues);
    return new CharSequenceFormState(flowlink_target.getStateStore(), state, FormStateType.JAVASCRIPT);
  }

  CharSequenceDeferred getExitQueryUrl(String name, String pathinfo, String[] outputValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert outputValues == null || outputValues.length % 2 == 0;
    
    mElementInfo.validateExitName(name);
    
    FlowLink  flowlink = null;
    flowlink = mElementInfo.getFlowLink(name);
    if (null == flowlink)
    {
      throw new ExitNotAttachedException(mElement.getElementInfo().getDeclarationName(), name);
    }
    
    return _getExitQueryUrl(flowlink, pathinfo, mOutputs.aggregateValues(), outputValues);
  }
  
  private CharSequenceDeferred _getExitQueryUrl(FlowLink flowlink, String pathinfo, Map<String, String[]> outputValueMap, String[] outputValues)
  throws EngineException
  {
    assert flowlink != null;
    
    ElementInfo  flowlink_target = flowlink.getExitTarget(mRequestState);

    // process the exit url query parameters
    StateStore  state_store = flowlink_target.getStateStore();
    FlowState  state = _getExitParameters(flowlink, flowlink_target, outputValueMap, outputValues);
    
    // process the pathinfo mappings
    if (null == pathinfo &&
      flowlink_target.isPathInfoUsed())
    {
      pathinfo = handlePathInfoMapping(flowlink_target, state, pathinfo);
    }
    
    return new CharSequenceQueryUrl(state_store, getExitUrl(flowlink, pathinfo), state, mElementInfo, "exit", flowlink.getExitName());
  }

  private String handlePathInfoMapping(ElementInfo flowlinkTarget, FlowState state, String pathinfo)
  {
    Map<String, String[]> parameters = state.getParameters();
    
    mapping:
    for (PathInfoMapping mapping : flowlinkTarget.getPathInfoMappings())
    {
      if (parameters.keySet().containsAll(mapping.getInputs()))
      {
        Iterator<String>  inputs_it = mapping.getInputs().iterator();
        
        StringBuilder  builder = new StringBuilder();
        String      input_name;
        String[]    input_value;
        for (PathInfoMappingSegment segment : mapping.getSegments())
        {
          if (segment.isRegexp())
          {
            if (!inputs_it.hasNext())
            {
              continue mapping;
            }
            
            input_name = inputs_it.next();
            
            // ensure that the input has only got one value
            input_value = parameters.get(input_name);
            if (null == input_value ||
              input_value.length != 1)
            {
              continue mapping;
            }
            
            // ensure that the input value corresponds to the
            // regexp pattern for it
            Matcher matcher = segment.getPattern().matcher(input_value[0]);
            if (!matcher.matches())
            {
              continue mapping;
            }
            
            // add the url-encoded input value to the pathinfo
            builder.append(StringUtils.encodeUrl(input_value[0]));
          }
          else
          {
            builder.append(segment.getValue());
          }
        }
        
        // remove the input parameters that are handled by the pathinfo
        for (String input : mapping.getInputs())
        {
          parameters.remove(input);
        }
        
        // build the new pathinfo
        return builder.toString();
      }
    }
    
    return null;
  }
  
  void setExitQuery(Template template, String name, String pathinfo, String[] outputValues)
  throws TemplateException, EngineException
  {
    template.setValue(PREFIX_EXIT_QUERY+name, getExitQueryUrl(name, pathinfo, outputValues).encoder(EncoderHtml.getInstance()));
  }

  void setExitForm(Template template, String name, String pathinfo, String[] outputValues)
  throws TemplateException, EngineException
  {
    template.setValue(PREFIX_EXIT_FORM+name, getExitFormUrl(name, pathinfo).encoder(EncoderHtml.getInstance()));
    String exit_javascript = PREFIX_EXIT_PARAMSJS+name;
    if (template.hasValueId(exit_javascript))
    {
      template.setValue(exit_javascript, getExitFormParameters(name, outputValues));
    }
    else
    {
      template.setValue(PREFIX_EXIT_PARAMS+name, getExitFormParameters(name, outputValues));
    }
  }
  
  Collection<String> selectParameter(Template template, String name, String[] values)
  {
    assert name != null;
    assert name.length() > 0;
    
    BeanHandler bean_handler = template.getBeanHandler();
    if (null == bean_handler)
    {
      return Collections.EMPTY_LIST;
    }
    FormBuilder form_builder = bean_handler.getFormBuilder();
    if (null == form_builder)
    {
      return Collections.EMPTY_LIST;
    }
    
    return form_builder.selectParameter(template, name, values);
  }
  
  void generateForm(Template template, Object beanInstance, String prefix)
  throws EngineException
  {
    BeanHandler bean_handler = template.getBeanHandler();
    if (null == bean_handler)
    {
      return;
    }
    FormBuilder form_builder = bean_handler.getFormBuilder();
    if (null == form_builder)
    {
      return;
    }
    try
    {
      form_builder.removeForm(template, beanInstance.getClass(), prefix);
      form_builder.generateForm(template, beanInstance, null, prefix);
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
  }
  
  void generateEmptyForm(Template template, Class beanClass, String prefix)
  throws EngineException
  {
    BeanHandler bean_handler = template.getBeanHandler();
    if (null == bean_handler)
    {
      return;
    }
    FormBuilder form_builder = bean_handler.getFormBuilder();
    if (null == form_builder)
    {
      return;
    }
    try
    {
      form_builder.removeForm(template, beanClass, prefix);
      form_builder.generateForm(template, beanClass, null, prefix);
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
  }
  
  void removeForm(Template template, Class beanClass, String prefix)
  throws EngineException
  {
    BeanHandler bean_handler = template.getBeanHandler();
    if (null == bean_handler)
    {
      return;
    }
    FormBuilder form_builder = bean_handler.getFormBuilder();
    if (null == form_builder)
    {
      return;
    }
    try
    {
      form_builder.removeForm(template, beanClass, prefix);
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
  }
  
  private String getContextId()
  {
    if (mContextId != null)
    {
      return mContextId;
    }
    
    synchronized (mElement)
    {
      mContextId = mRequestState.buildContextId();      
    }
    
    return mContextId;
  }
  
  private FlowState getSubmissionParameters(String name, String[] parameterValues, Set<Map.Entry<String, String[]>> outputEntries)
  {
    FlowState state = new FlowState();
    
    state.putParameter(ReservedParameters.SUBMISSION, name);
    
    // add the submission parameters
    if (parameterValues != null)
    {
      String  parameter_name = null;
      String  parameter_value = null;
      for (int i = 0; i < parameterValues.length; i += 2)
      {
        parameter_name = parameterValues[i];
        parameter_value = parameterValues[i+1];
        state.putParameter(parameter_name,  parameter_value);
      }

      validateParameter(parameter_name);
    }
    
    // Create the submission context parameter
    Submission submission = mElementInfo.getSubmission(name);
    if (null == submission ||
      Scope.LOCAL == submission.getScope())
    {
      String submission_context = getContextId();
      String target = getElementInfo().getId();
      if (!submission_context.equals(target))
      {
        StringBuilder submission_context_buffer = new StringBuilder(submission_context);
        submission_context_buffer.append("^");
        submission_context_buffer.append(target);
        submission_context = submission_context_buffer.toString();
      }
      try
      {
        state.putParameter(ReservedParameters.SUBMISSIONCONTEXT, Base64.encodeToString(submission_context.getBytes("UTF-8"), false));
      }
      catch (UnsupportedEncodingException e)
      {
        // should never happen
      }
    }

    // Preserve the continuation ID if a continuation context is active
    if (null == submission ||
      !submission.getCancelContinuations())
    {
      String continuation_id = ContinuationContext.getActiveContextId();
      if (continuation_id != null)
      {
        state.putParameter(ReservedParameters.CONTID, continuation_id);
      }
    }
      
    // create or preserve the request parameters that were initially send to the child element
    // this permits completely seperate processing of any other parameters
    if (mElementState.inInheritanceStructure())
    {
      // preserve the original child request
      state.putParameter(ReservedParameters.CHILDREQUEST, getEncodedChildRequest());
      
      // preserve the trigger list
      state.putParameter(ReservedParameters.TRIGGERLIST, mElementState.encodeTriggerList());
    }
      
    // preserve the embedding element's inputs and global vars
    ElementContext context = this;
    while (context != null &&
         context.mRequestState.isEmbedded())
    {
      context = context.mRequestState.getEmbeddingContext().getElementContext();
      for (String input_name : context.mElementInfo.getInputNames())
      {
        if (context.mElementState.hasInputValue(input_name))
        {
          state.putSubmissionGlobalInput(input_name, context.mElementState.getInputValues(input_name, false));
        }
      }
      for (String globalvar_name : context.mElementInfo.getGlobalVarNames())
      {
        if (context.mElementState.hasInputValue(globalvar_name))
        {
          state.putSubmissionGlobalInput(globalvar_name, context.mElementState.getInputValues(globalvar_name, false));
        }
      }
    }

    // preserve the global variables
    for (String globalvar_name : mElementInfo.getGlobalVarNames())
    {
      if (mElementState.hasInputValue(globalvar_name))
      {
        state.putSubmissionGlobalInput(globalvar_name, mElementState.getInputValues(globalvar_name, false));
      }
    }
    
    // activate reflective datalinks for global vars
    if (outputEntries != null)
    {
      for (Map.Entry<String, String[]> output : outputEntries)
      {
        // global vars
        if (mElementInfo.containsGlobalVar(output.getKey()))
        {
          state.putSubmissionGlobalInput(output.getKey(), output.getValue());
        }
      }
    }

    // add this element's inputs
    Map<String, String[]>  element_inputs = null;
    
    // preserve the input values
    for (String input_name : mElementInfo.getInputNames())
    {
      if (mElementState.hasInputValue(input_name))
      {
        if (null == element_inputs)
        {
          element_inputs = new LinkedHashMap<String, String[]>();
        }
        
        element_inputs.put(input_name, mElementState.getInputValues(input_name));
      }
    }
    
    // merge this element's inputs with its preserved inputs
    Map<String, String[]> preserved_inputs = collectPreservedInputs(outputEntries);
    
    String context_id = getContextId();
    if (preserved_inputs != null &&
      preserved_inputs.size() > 0)
    {
      if (null == element_inputs)
      {
        element_inputs = new LinkedHashMap<String, String[]>();
      }
      element_inputs.putAll(preserved_inputs);
    }
    state.setSubmissionElementInputs(element_inputs);
    
    // remember this element's context identifier for when the context inputs are extracted
    state.setSubmissionContextId(context_id);
    
    return state;
  }
  
  private Map<String, String[]> collectPreservedInputs(Set<Map.Entry<String, String[]>> outputEntries)
  {
    Map<String, String[]> element_inputs = null;
    
    // activate reflective datalinks for which outputs point to the inputs
    if (outputEntries != null)
    {
      for (Map.Entry<String, String[]> output : outputEntries)
      {
        // reflective datalinks
        Collection<String> datalink_inputs = mElementInfo.getDataLinkInputs(output.getKey(), mElementInfo, false, null);
        if (datalink_inputs != null)
        {
          for (String input_name : datalink_inputs)
          {
            if (null == element_inputs)
            {
              element_inputs = new LinkedHashMap<String, String[]>();
            }
            
            element_inputs.put(input_name, output.getValue());
          }
        }
      }
    }
    
    return element_inputs;
  }
  
  private String getSubmissionUrl(String pathInfo)
  {
    String url = null;
    String pathinfo = null;
    if (mElementInfo.isPathInfoUsed())
    {
      pathinfo = mRequestState.getElementState().getPathInfo();
    }
    if (mElementState.inInheritanceStructure())
    {
      url = mRequestState.getTarget().getUrl();
    }
    else
    {
      url = mElementInfo.getUrl();
    }
    
    // if the element defined no url, go up the hierarchy of embedded elements
    // to grab the first defined element url
    if (null == url)
    {
      if (!mRequestState.isEmbedded())
      {
        if (mElementState.inInheritanceStructure())
        {
          throw new SubmissionInheritanceUrlMissingException(mRequestState.getTarget().getDeclarationName(), mElementInfo.getDeclarationName());
        }
        else
        {
          throw new SubmissionUrlMissingException(mElementInfo.getDeclarationName());
        }
      }
      ElementContext context = this;
      while (null == url &&
           context != null &&
           context.mRequestState.isEmbedded())
      {
        context = context.mRequestState.getEmbeddingContext().getElementContext();
        if (context.getElementInfo().isPathInfoUsed())
        {
          pathinfo = context.mRequestState.getElementState().getPathInfo();
        }
        else
        {
          pathinfo = null;
        }
        if (context.mElementState.inInheritanceStructure())
        {
          url = context.mRequestState.getTarget().getUrl();
        }
        else
        {
          url = context.mElementInfo.getUrl();
        }
      }
    }
    
    // apply a forced pathinfo
    if (pathInfo != null &&
      pathInfo.length() > 0)
    {
      pathinfo = pathInfo;
    }
    
    // construct the correct servlet path and url that points to the
    // target child element, or the current element if there are no children
    StringBuilder url_buffer = new StringBuilder(mRequestState.getGateUrl());
    appendTargetAndPathinfo(url_buffer, url, pathinfo);
    return url_buffer.toString();
  }
    
  private CharSequenceDeferred _getSubmissionQueryUrl(String name, String pathinfo, String[] parameterValues, Set<Map.Entry<String, String[]>> outputEntries)
  throws EngineException
  {
    StateStore  state_store = mElementInfo.getStateStore();
    FlowState  state = getSubmissionParameters(name, parameterValues, outputEntries);
    return new CharSequenceQueryUrl(state_store, getSubmissionUrl(pathinfo), state, mElementInfo, "submission", name);
  }
  
  CharSequenceDeferred getSubmissionQueryUrl(String name, String pathinfo, String[] parameterValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert parameterValues == null || parameterValues.length % 2 == 0;
    
    mElementInfo.validateSubmissionName(name);
    
    return _getSubmissionQueryUrl(name, pathinfo, parameterValues, mOutputs.aggregateValues().entrySet());
  }
  
  CharSequenceDeferred getSubmissionFormUrl(String pathinfo)
  throws EngineException
  {
    return new CharSequenceFormUrl(mElementInfo.getStateStore(), getSubmissionUrl(pathinfo));
  }
  
  CharSequenceDeferred getSubmissionFormParameters(String name, String[] parameterValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert parameterValues == null || parameterValues.length % 2 == 0;
    
    mElementInfo.validateSubmissionName(name);
    
    return _getSubmissionFormParameters(name, parameterValues, mOutputs.aggregateValues().entrySet());
  }
  
  CharSequenceDeferred getSubmissionFormParametersJavascript(String name, String[] parameterValues)
  throws EngineException
  {
    assert name != null;
    assert name.length() > 0;
    assert parameterValues == null || parameterValues.length % 2 == 0;
    
    mElementInfo.validateSubmissionName(name);
    
    return _getSubmissionFormParametersJavascript(name, parameterValues, mOutputs.aggregateValues().entrySet());
  }
    
  private CharSequenceDeferred _getSubmissionFormParameters(String name, String[] parameterValues, Set<Map.Entry<String, String[]>> outputEntries)
  throws EngineException
  {
    FlowState  state = getSubmissionParameters(name, parameterValues, outputEntries);
    return new CharSequenceFormState(mElementInfo.getStateStore(), state, FormStateType.PARAMS);
  }
    
  private CharSequenceDeferred _getSubmissionFormParametersJavascript(String name, String[] parameterValues, Set<Map.Entry<String, String[]>> outputEntries)
  throws EngineException
  {
    FlowState  state = getSubmissionParameters(name, parameterValues, outputEntries);
    return new CharSequenceFormState(mElementInfo.getStateStore(), state, FormStateType.JAVASCRIPT);
  }
  
  void setSubmissionQuery(Template template, String name, String pathinfo, String[] parameterValues)
  throws TemplateException, EngineException
  {
    template.setValue(PREFIX_SUBMISSION_QUERY+name, getSubmissionQueryUrl(name, pathinfo, parameterValues).encoder(EncoderHtml.getInstance()));
  }
  
  void setSubmissionForm(Template template, String name, String pathinfo, String[] parameterValues)
  throws TemplateException, EngineException
  {
    template.setValue(PREFIX_SUBMISSION_FORM+name, getSubmissionFormUrl(pathinfo).encoder(EncoderHtml.getInstance()));
    String submission_javascript = PREFIX_SUBMISSION_PARAMSJS+name;
    if (template.hasValueId(submission_javascript))
    {
      template.setValue(submission_javascript, getSubmissionFormParametersJavascript(name, parameterValues));
    }
    else
    {
      template.setValue(PREFIX_SUBMISSION_PARAMS+name, getSubmissionFormParameters(name, parameterValues));
    }
  }
  
  Collection<String> selectSubmissionParameter(Template template, String name, String[] values)
  {
    return selectParameter(template, PREFIX_PARAM+name, values);
  }
  
  void setSubmissionBean(Template template, Object beanInstance)
  throws TemplateException, EngineException
  {
    setSubmissionBean(template, beanInstance, true);
  }
  
  void setSubmissionBean(Template template, Object beanInstance, boolean encode)
  throws TemplateException, EngineException
  {
    template.setBean(beanInstance, PREFIX_PARAM, encode);
  }
  
  void removeSubmissionBean(Template template, Object beanInstance)
  throws TemplateException, EngineException
  {
    template.removeBean(beanInstance, PREFIX_PARAM);
  }
  
  private <BeanType> BeanType _getSubmissionBean(Submission submission, Class<BeanType> beanClass, String prefix)
  throws EngineException
  {
    assert submission != null;
    assert beanClass != null;

    BeanType bean_instance = getBeanInstance(beanClass);

    try
    {
      HashMap<String, PropertyDescriptor>  bean_properties = BeanUtils.getUppercasedBeanProperties(beanClass);
      
      String[] parameter_values = null;
      
      for (String parameter_name : submission.getParameterNames())
      {
        if (mElementState.hasRequestParameterValue(parameter_name))
        {
          parameter_values = mElementState.getRequestParameterValues(parameter_name);
          if (parameter_values != null &&
            parameter_values.length > 0)
          {
            BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean_instance, null);
          }
        }
      }
      
      for (Pattern parameter_regexp : submission.getParameterRegexps())
      {
        for (String parameter_name : getParameterNames(parameter_regexp.pattern()))
        {
          if (mElementState.hasRequestParameterValue(parameter_name))
          {
            parameter_values = mElementState.getRequestParameterValues(parameter_name);
            if (parameter_values != null &&
              parameter_values.length > 0)
            {
              BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean_instance, null);
            }
          }
        }
      }
      
      for (String uploadedfile_name : getUploadedFileNames())
      {
        UploadedFile file = getUploadedFile(uploadedfile_name);
        BeanUtils.setUppercasedBeanProperty(uploadedfile_name, file, prefix, bean_properties, bean_instance);
      }
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
    
    return bean_instance;
  }
  
  <BeanType> BeanType getNamedSubmissionBean(String submissionName, String beanName)
  throws EngineException
  {
    assert submissionName != null;
    assert submissionName.length() > 0;
    assert beanName != null;
    assert beanName.length() > 0;

    mElementInfo.validateSubmissionName(submissionName);
    
    if (!hasSubmission(submissionName))
    {
      return null;
    }
    
    BeanDeclaration  bean = mElementInfo.getSubmission(submissionName).getNamedBean(beanName);
    Class<BeanType>  bean_class = null;
    
    try
    {
      bean_class = (Class<BeanType>)Class.forName(bean.getClassname());
    }
    catch (ClassNotFoundException e)
    {
      throw new NamedSubmissionBeanClassnameErrorException(mElementInfo.getDeclarationName(), submissionName, beanName, bean.getClassname(), e);
    }
    
    return _getSubmissionBean(mElementInfo.getSubmission(submissionName), bean_class, bean.getPrefix());
  }
  
  <BeanType> BeanType getSubmissionBean(String submissionName, Class<BeanType> beanClass, String prefix)
  throws EngineException
  {
    assert submissionName != null;
    assert submissionName.length() > 0;
    assert beanClass != null;

    mElementInfo.validateSubmissionName(submissionName);
    
    if (!hasSubmission(submissionName))
    {
      return null;
    }
    
    return _getSubmissionBean(mElementInfo.getSubmission(submissionName), beanClass, prefix);
  }
  
  private void _fillSubmissionBean(Submission submission, Object bean, String prefix)
  throws EngineException
  {
    assert submission != null;
    
    if (null == bean)
    {
      return;
    }
    
    try
    {
      HashMap<String, PropertyDescriptor>  bean_properties = BeanUtils.getUppercasedBeanProperties(bean.getClass());
      String[]              parameter_values = null;
      
      Object  empty_bean = null;
      
      // handle regular parameters
      for (String parameter_name : submission.getParameterNames())
      {
        parameter_values = mElementState.getRequestParameterValues(parameter_name);
        if (null == empty_bean &&
          (null == parameter_values ||
          0 == parameter_values[0].length()))
        {
          try
          {
            empty_bean = bean.getClass().newInstance();
          }
          catch (InstantiationException e)
          {
            throw new EngineException("Unexpected error while invoking the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
          }
          catch (IllegalAccessException e)
          {
            throw new EngineException("No permission to invoke the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
          }
        }

        BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean, empty_bean);
      }
      
      // handle regexp parameters
      for (Pattern parameter_regexp : submission.getParameterRegexps())
      {
        for (String parameter_name : getParameterNames(parameter_regexp.pattern()))
        {
          parameter_values = mElementState.getRequestParameterValues(parameter_name);
          if (null == empty_bean &&
            (null == parameter_values ||
            0 == parameter_values[0].length()))
          {
            try
            {
              empty_bean = bean.getClass().newInstance();
            }
            catch (InstantiationException e)
            {
              throw new EngineException("Unexpected error while invoking the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
            }
            catch (IllegalAccessException e)
            {
              throw new EngineException("No permission to invoke the default constructor of the bean with class '"+bean.getClass().getName()+"'.", e);
            }
          }
          
          BeanUtils.setUppercasedBeanProperty(parameter_name, parameter_values, prefix, bean_properties, bean, empty_bean);
        }
      }
      
      // automatically handle uploaded files
      for (String uploadedfile_name : getUploadedFileNames())
      {
        UploadedFile file = getUploadedFile(uploadedfile_name);
        BeanUtils.setUppercasedBeanProperty(uploadedfile_name, file, prefix, bean_properties, bean);
      }
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
  }
  
  void fillSubmissionBean(String submissionName, Object bean, String prefix)
  throws EngineException
  {
    assert submissionName != null;
    assert submissionName.length() > 0;
    
    if (null == bean)
    {
      return;
    }

    mElementInfo.validateSubmissionName(submissionName);
    
    if (!hasSubmission(submissionName))
    {
      return;
    }
    
    _fillSubmissionBean(mElementInfo.getSubmission(submissionName), bean, prefix);
  }
  
  <BeanType> BeanType getNamedInputBean(String name)
  throws EngineException
  {
    assert name != null;
    
    mElementInfo.validateInbeanName(name);

    BeanDeclaration  input_bean = mElementInfo.getNamedInbeanInfo(name);
    if (null == input_bean)
    {
      input_bean = mElementInfo.getNamedGlobalBeanInfo(name);
    }
    
    Class<BeanType>  input_bean_class = null;
    
    try
    {
      input_bean_class = (Class<BeanType>)Class.forName(input_bean.getClassname());
    }
    catch (ClassNotFoundException e)
    {
      throw new NamedInbeanClassnameErrorException(mElementInfo.getDeclarationName(),name, input_bean.getClassname());
    }
    
    return getInputBean(input_bean_class, input_bean.getPrefix());
  }
  
  <BeanType> BeanType getInputBean(Class<BeanType> beanClass, String prefix)
  throws EngineException
  {
    assert beanClass != null;

    BeanType bean_instance = getBeanInstance(beanClass);

    try
    {
      HashMap<String, PropertyDescriptor>  bean_properties = BeanUtils.getUppercasedBeanProperties(beanClass);
      
      // merge the input and global variable names
      Collection<String>  input_names = mElementInfo.getInputNames();
      Collection<String>  globalvar_names = mElementInfo.getGlobalVarNames();
      ArrayList<String>  merged_names = new ArrayList<String>();
      merged_names.addAll(input_names);
      merged_names.addAll(globalvar_names);
      
      // process all the possible input names
      String[]  values = null;
      
      for (String name : merged_names)
      {
        if (mElementState.hasInputValue(name))
        {
          values = mElementState.getInputValues(name);
          if (values != null &&
            values.length > 0)
          {
            BeanUtils.setUppercasedBeanProperty(name, values, prefix, bean_properties, bean_instance, null);
          }
        }
      }
    }
    catch (BeanUtilsException e)
    {
      throw new EngineException(e);
    }
    
    return bean_instance;
  }
  
  void setNamedOutputBean(String name, Object bean)
  throws EngineException
  {
    assert name != null;

    mElementInfo.validateOutbeanName(name);

    BeanDeclaration  output_bean = mElementInfo.getNamedOutbeanInfo(name);
    if (null == output_bean)
    {
      output_bean = mElementInfo.getNamedGlobalBeanInfo(name);
    }
    
    setOutputBean(bean, output_bean.getPrefix());
  }

  void setOutputBean(Object bean, String prefix)
  throws EngineException
  {
    if (null == bean)    throw new IllegalArgumentException("bean can't be null.");
    
    Map<String, String[]> values = getOutputBeanValues(bean, prefix, null);
    
    String name;
    String[] value;
    for (Map.Entry<String, String[]> entry : values.entrySet())
    {
      name = entry.getKey();
      value = entry.getValue();
      
      setOutputValues(name, value);
      
      if (mElementState.inInheritanceStructure() &&
        mElementInfo.containsChildTrigger(name))
      {
        triggerChild(name, value);
      }
    }
  }

  Map<String, String[]> getOutputBeanValues(Object bean, String prefix, Collection<String> included)
  throws BeanInstanceValuesErrorException
  {
    if (null == bean)  return Collections.EMPTY_MAP;
    
    Map<String, String[]> values = new LinkedHashMap<String, String[]>();
    try
    {
      Constrained     constrained = ConstrainedUtils.makeConstrainedInstance(bean);
      ConstrainedProperty  constrained_property = null;
      
      Set<String>  merged_names = new LinkedHashSet<String>();
      
      // handle outputs
      Collection<String>  output_names = mElementInfo.getOutputNames();
      if (null == included)
      {
        merged_names.addAll(output_names);
      }
      else
      {
        for (String name : output_names)
        {
          if (included.contains(name))
          {
            merged_names.add(name);
          }
        }
      }
      
      // handle globals
      Collection<String>  globalvar_names = mElementInfo.getGlobalVarNames();
      if (null == included)
      {
        merged_names.addAll(globalvar_names);
      }
      else
      {
        for (String name : globalvar_names)
        {
          if (included.contains(name))
          {
            merged_names.add(name);
          }
        }
      }
      
      // create the merged array
      String[]  merged_names_array = new String[merged_names.size()];
      merged_names.toArray(merged_names_array);
      
      // process all the possible output names
      Map<String, Object>  property_values = BeanUtils.getPropertyValues(bean, merged_names_array, null, prefix);
      Object        property_value = null;
      
      for (String property_name : property_values.keySet())
      {
        property_value = property_values.get(property_name);
        
        if (property_value != null)
        {
          // get the constrained property if that's appropriate
          if (constrained != null)
          {
            if (prefix != null)
            {
              constrained_property = constrained.getConstrainedProperty(property_name.substring(prefix.length()));
            }
            else
            {
              constrained_property = constrained.getConstrainedProperty(property_name);
            }
          }
          
          values.put(property_name, getOutputObjectValues(property_name, property_value, constrained_property));
        }
      }
    }
    catch (BeanUtilsException e)
    {
      throw new BeanInstanceValuesErrorException(bean, e);
    }
    return values;
  }
  
  private <BeanType> BeanType getBeanInstance(Class<BeanType> beanClass)
  throws EngineException
  {
    BeanType bean_instance;
    
    try
    {
      bean_instance = beanClass.newInstance();
    }
    catch (InstantiationException e)
    {
      throw new EngineException("Can't instantiate a bean with class '"+beanClass.getName()+"'.", e);
    }
    catch (IllegalAccessException e)
    {
      throw new EngineException("No permission to instantiate a bean with class '"+beanClass.getName()+"'.", e);
    }
    
    return bean_instance;
  }
  
  void processEmbeddedElement(Template template, ElementSupport embeddingElement, String elementId, String differentiator, Object data)
  throws TemplateException, EngineException
  {
    // process the embedded elements
    if (template.hasFilteredValues(TAG_ELEMENT))
    {
      List<String[]>  element_tags = template.getFilteredValues(TAG_ELEMENT);
      for (String[] captured_groups : element_tags)
      {
        if (null == differentiator)
        {
          if (elementId.equals(captured_groups[3]))
          {
            processEmbeddedElement(captured_groups[0], template, embeddingElement, captured_groups[3], null, data);
            return;
          }
        }
        else
        {
          if (elementId.equals(captured_groups[3]))
          {
            String  value_id = captured_groups[1]+":";
              
            String  differentiator_value_id = value_id+differentiator;
            if (template.hasValueId(differentiator_value_id))
            {
              processEmbeddedElement(differentiator_value_id, template, embeddingElement, elementId, differentiator, data);
              return;
            }
            else if (template.hasValueId(value_id))
            {
              processEmbeddedElement(value_id, template, embeddingElement, elementId, differentiator, data);
              return;
            }
          }
        }
      }
    }

    throw new EmbeddedElementNotFoundException(elementId);
  }

  private void processEmbeddedElement(String valueId, Template template, ElementSupport embeddingElement, String elementId, String differentiator, Object data)
  throws TemplateException, EngineException
  {
    if (!template.hasValueId(valueId))
    {
      throw new EmbeddedElementNotFoundException(elementId);
    }
    
    ElementInfo   embedded_element = null;
    RequestState  embedded_state = null;
    Response    embedded_response = null;
    
    // try to obtain the embedded element and throw an exception if
    // it couldn't be found
    embedded_element = mElementInfo.getSite().resolveId(elementId, mElementInfo);
    if (null == embedded_element)
    {
      throw new ElementIdNotFoundException(elementId);
    }

    // build the embedded element request parameter by merging
    // the current element's request parameters with its input values
    // also merge in the state global vars
    Map<String, String[]> parameters = new HashMap<String, String[]>(mElementState.getRequestParameters());
    for (Map.Entry<String, String[]> input_entry : mElementState.getInputEntries())
    {
      if (!parameters.containsKey(input_entry.getKey()))
      {
        parameters.put(input_entry.getKey(), input_entry.getValue());
      }
    }
    if (mRequestState.getStateGlobalVars() != null)
    {
      for (Map.Entry<String, String[]> globalvar_entry : mRequestState.getStateGlobalVars().entrySet())
      {
        parameters.put(globalvar_entry.getKey(), globalvar_entry.getValue());
      }
    }
    
    // build the request and response objects for the embedded element
    // and service the request
    EmbeddingContext embedding_context = new EmbeddingContext(this, embeddingElement, template, template.getDefaultValue(valueId), differentiator, data);
    embedded_response = mResponse.createEmbeddedResponse(valueId, differentiator);
    embedded_state = RequestState.getEmbeddedInstance(embedded_response, embedding_context, parameters, embedded_element);
    
    embedded_state.service();
    embedded_response.close();
    
    if (embedding_context.getCancelEmbedding())
    {
      // handle embedding cancellation by throwing a dedicated exception
      // that will bubble up to the first embedding element and print
      // out the embedded content
      throw new CancelEmbeddingTriggeredException(embedded_response.getEmbeddedContent());
    }
    else
    {
      // set the output of the element to the value in the template
      template.setValue(valueId, embedded_response.getEmbeddedContent());
    }
  }
  
  void evaluateExpressionRoleUserTags(List<String> setValues, Template template, String id)
  {
    if (template.hasFilteredBlocks(TAG_OGNL_ROLEUSER) ||
      template.hasFilteredBlocks(TAG_MVEL_ROLEUSER) ||
      template.hasFilteredBlocks(TAG_GROOVY_ROLEUSER) ||
      template.hasFilteredBlocks(TAG_JANINO_ROLEUSER))
    {
      RoleUserIdentity  identity = (RoleUserIdentity)mRequestState.getRequestAttribute(Identified.IDENTITY_ATTRIBUTE_NAME);
      
      if (identity != null)
      {
        RoleUserAttributes attributes = identity.getAttributes();
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("login", identity.getLogin());
        map.put("password", attributes.getPassword());
        map.put("userId", attributes.getUserId());
        map.put("roles", attributes.getRoles());
        
        if (template.hasFilteredBlocks(TAG_OGNL_ROLEUSER))
        {
          String language_id = id;
          if (language_id != null)
          {
            language_id = PREFIX_OGNL_ROLEUSER+language_id;
          }
          FilteredTagProcessorOgnl.getInstance().processTags(setValues, template, template.getFilteredBlocks(TAG_OGNL_ROLEUSER), language_id, RoleUserAttributes.class, "user", attributes, map);
        }
        
        if (template.hasFilteredBlocks(TAG_MVEL_ROLEUSER))
        {
          String language_id = id;
          if (language_id != null)
          {
            language_id = PREFIX_MVEL_ROLEUSER+language_id;
          }
          FilteredTagProcessorMvel.getInstance().processTags(setValues, template, template.getFilteredBlocks(TAG_MVEL_ROLEUSER), language_id, RoleUserAttributes.class, "user", attributes, map);
        }
        
        if (template.hasFilteredBlocks(TAG_GROOVY_ROLEUSER))
        {
          String language_id = id;
          if (language_id != null)
          {
            language_id = PREFIX_GROOVY_ROLEUSER+language_id;
          }
          FilteredTagProcessorGroovy.getInstance().processTags(setValues, template, template.getFilteredBlocks(TAG_GROOVY_ROLEUSER), language_id, RoleUserAttributes.class, "user", attributes, map);
        }
        
        if (template.hasFilteredBlocks(TAG_JANINO_ROLEUSER))
        {
          String language_id = id;
          if (language_id != null)
          {
            language_id = PREFIX_JANINO_ROLEUSER+language_id;
          }
          FilteredTagProcessorJanino.getInstance().processTags(setValues, template, template.getFilteredBlocks(TAG_JANINO_ROLEUSER), language_id, RoleUserAttributes.class, "user", attributes, map);
        }
      }
    }
  }

  private String getEncodedChildRequest()
  {
    String child_request = null;

    if (mElementState.hasRequestParameterValue(ReservedParameters.CHILDREQUEST))
    {
      child_request = mElementState.getRequestParameter(ReservedParameters.CHILDREQUEST);
    }
    else
    {
      child_request = ChildRequestEncoder.encode(mElementState.getInheritanceStack().get(0), mRequestState);
    }
    
    return child_request;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.